diff --git a/frontend/index.html b/frontend/index.html index 01aee08..bdbe54b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -23,6 +23,7 @@ + @@ -334,6 +335,7 @@ + diff --git a/frontend/src/css/irc.css b/frontend/src/css/irc.css new file mode 100644 index 0000000..9561f57 --- /dev/null +++ b/frontend/src/css/irc.css @@ -0,0 +1,289 @@ +/* ========================================================================== + Nexus Terminal - IRC viewer + ========================================================================== */ + +.irc-viewer-panel { + background: var(--bg-base); +} + +.irc-viewer { + display: grid; + grid-template-columns: minmax(0, 1fr) 260px; + width: 100%; + height: 100%; + min-width: 0; + min-height: 0; +} + +.irc-main { + display: grid; + grid-template-rows: 34px minmax(0, 1fr) 42px; + min-width: 0; + min-height: 0; +} + +.irc-room-tabs { + display: flex; + align-items: center; + gap: var(--space-1); + min-width: 0; + overflow-x: auto; + overflow-y: hidden; + padding: var(--space-1) var(--space-2); + background: var(--bg-surface); + border-bottom: 1px solid var(--border-subtle); +} + +.irc-room-tab { + height: 24px; + max-width: 180px; + min-width: 44px; + padding: 0 var(--space-2); + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text-secondary); + font-size: var(--text-xs); + line-height: 22px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: pointer; +} + +.irc-room-tab:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.irc-room-tab.active { + background: var(--bg-active); + border-color: var(--border-default); + color: var(--text-primary); +} + +.irc-room-tab.has-unread::after { + content: ""; + display: inline-block; + width: 5px; + height: 5px; + margin-left: var(--space-1); + border-radius: 50%; + background: var(--protocol-irc); + vertical-align: middle; +} + +.irc-terminal { + position: relative; + min-width: 0; + min-height: 0; + overflow: hidden; + padding: var(--space-2); +} + +.irc-terminal .xterm { + height: 100%; +} + +.irc-composer { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--space-2); + padding: var(--space-2); + background: var(--bg-surface); + border-top: 1px solid var(--border-subtle); +} + +.irc-input { + min-width: 0; + height: 26px; + padding: 0 var(--space-2); + border: 1px solid var(--border-default); + border-radius: var(--radius-sm); + background: var(--bg-input); + color: var(--text-primary); + font-family: var(--font-mono); + font-size: var(--text-sm); +} + +.irc-input:focus, +.irc-join-input:focus, +.irc-filter-input:focus { + outline: none; + border-color: var(--border-focus); +} + +.irc-send { + height: 26px; +} + +.irc-sidebar { + display: grid; + grid-template-rows: minmax(88px, auto) minmax(120px, 1fr) minmax(120px, 1fr); + min-width: 0; + min-height: 0; + background: var(--bg-surface); + border-left: 1px solid var(--border-subtle); +} + +.irc-sidebar-section { + min-height: 0; + padding: var(--space-2); + border-bottom: 1px solid var(--border-subtle); + overflow: hidden; +} + +.irc-users-section { + border-bottom: 0; +} + +.irc-sidebar-title, +.irc-sidebar-title-row { + min-height: 24px; + color: var(--text-tertiary); + font-size: var(--text-xs); + font-weight: var(--weight-semibold); + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.irc-sidebar-title-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-2); +} + +.irc-join-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: var(--space-1); + margin-bottom: var(--space-2); +} + +.irc-join-input, +.irc-filter-input { + min-width: 0; + height: 24px; + padding: 0 var(--space-2); + border: 1px solid var(--border-default); + border-radius: var(--radius-sm); + background: var(--bg-input); + color: var(--text-primary); + font-size: var(--text-xs); +} + +.irc-filter-input { + width: 100%; + margin-bottom: var(--space-2); +} + +.irc-sidebar-list, +.irc-room-list, +.irc-user-list { + min-height: 0; + max-height: 100%; + overflow-y: auto; +} + +.irc-room-row, +.irc-user-row, +.irc-listed-room-main { + width: 100%; + min-width: 0; + border: 0; + background: transparent; + color: var(--text-secondary); + cursor: pointer; + text-align: left; +} + +.irc-room-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: var(--space-2); + height: 26px; + padding: 0 var(--space-2); + border-radius: var(--radius-sm); +} + +.irc-room-row:hover, +.irc-user-row:hover, +.irc-listed-room:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.irc-room-row.active { + background: var(--bg-active); + color: var(--text-primary); +} + +.irc-room-name, +.irc-listed-room-name, +.irc-listed-room-topic { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.irc-room-count, +.irc-listed-room-count { + color: var(--text-tertiary); + font-size: 10px; + font-variant-numeric: tabular-nums; +} + +.irc-listed-room { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: var(--space-2); + min-height: 34px; + padding: var(--space-1) var(--space-2); + border-radius: var(--radius-sm); +} + +.irc-listed-room-main { + display: grid; + gap: 1px; +} + +.irc-listed-room-name { + color: var(--text-primary); + font-size: var(--text-xs); +} + +.irc-listed-room-topic { + color: var(--text-tertiary); + font-size: 10px; +} + +.irc-user-row { + display: block; + height: 24px; + padding: 0 var(--space-2); + border-radius: var(--radius-sm); + font-size: var(--text-xs); + line-height: 24px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.irc-empty { + padding: var(--space-2); + color: var(--text-tertiary); + font-size: var(--text-xs); +} + +@media (max-width: 900px) { + .irc-viewer { + grid-template-columns: minmax(0, 1fr); + } + + .irc-sidebar { + display: none; + } +} diff --git a/frontend/src/css/sidebar.css b/frontend/src/css/sidebar.css index 6cb5009..2253040 100644 --- a/frontend/src/css/sidebar.css +++ b/frontend/src/css/sidebar.css @@ -304,6 +304,9 @@ .sidebar-item .protocol-indicator.proto-vnc::before { content: "⊞"; } +.sidebar-item .protocol-indicator.proto-irc::before { + content: "#"; +} .sidebar-item .protocol-indicator.proto-local::before { content: "›_"; } @@ -328,6 +331,10 @@ background: rgba(232, 93, 74, 0.18); color: var(--protocol-vnc); } +.sidebar-item .protocol-indicator.proto-irc { + background: rgba(216, 178, 76, 0.18); + color: var(--protocol-irc); +} .sidebar-item .protocol-indicator.proto-local { background: rgba(139, 195, 74, 0.18); color: var(--protocol-local); diff --git a/frontend/src/css/tabs.css b/frontend/src/css/tabs.css index 593ab55..e9851b0 100644 --- a/frontend/src/css/tabs.css +++ b/frontend/src/css/tabs.css @@ -118,6 +118,10 @@ background: rgba(232, 93, 74, 0.15); color: var(--protocol-vnc); } +.tab-badge.badge-irc { + background: rgba(216, 178, 76, 0.15); + color: var(--protocol-irc); +} .tab-badge.badge-local { background: rgba(139, 195, 74, 0.15); color: var(--protocol-local); diff --git a/frontend/src/css/tokens.css b/frontend/src/css/tokens.css index a99e991..540a535 100644 --- a/frontend/src/css/tokens.css +++ b/frontend/src/css/tokens.css @@ -58,6 +58,7 @@ --protocol-raw: #ab47bc; --protocol-github: #7c8aff; --protocol-vnc: #e85d4a; + --protocol-irc: #d8b24c; --protocol-local: #8bc34a; /* ----------------------------------------------------------------------- diff --git a/frontend/src/js/app.js b/frontend/src/js/app.js index bebff0a..03faf58 100644 --- a/frontend/src/js/app.js +++ b/frontend/src/js/app.js @@ -35,6 +35,7 @@ this.terminals = new TerminalManager(this); this.profileEditor = new ProfileEditor(this); this.rawViewer = new RawViewer(this); + window.NexusTerminal.IRCViewer?.setApp(this); this.settings = new Settings(this); this.commandPalette = new CommandPalette(this); this.secretPrompt = new SecretPrompt(this); @@ -239,6 +240,7 @@ setTimeout(fit, 50); setTimeout(fit, 300); setTimeout(fit, 800); + setTimeout(() => window.NexusTerminal.IRCViewer?.fit(payload.sessionID), 50); } }); @@ -288,6 +290,12 @@ ); }); + // IRC events + window.runtime.EventsOn("session:irc:event", (payload) => { + window.NexusTerminal.IRCViewer.handleEvent(payload); + this.tabs.markActivity(payload.sessionID); + }); + // App-wide commands from the native menu window.runtime.EventsOn("app:open-about", () => { this.settings.openTab("about"); @@ -801,7 +809,12 @@ const decision = await this._confirmReuseSession(profile, preferred); if (decision === "existing") { this.tabs.setActive(preferred.id); - if (preferred.protocol !== "raw" && preferred.protocol !== "github") { + if ( + preferred.protocol !== "raw" && + preferred.protocol !== "github" && + preferred.protocol !== "vnc" && + preferred.protocol !== "irc" + ) { this.terminals.focusTerminal(preferred.id); } return; @@ -845,6 +858,15 @@ profile, ); } + } else if (profile.protocol === "irc") { + const container = document.getElementById(panelId); + if (container) { + window.NexusTerminal.IRCViewer.createViewer( + sessionInfo.id, + container, + profile, + ); + } } else { this.terminals.createTerminal(sessionInfo.id, panelId, profile); } @@ -901,6 +923,7 @@ +
@@ -915,6 +938,10 @@
+