From 2150d2dcf61fd9d54a2517c00fe777d4013e0aa2 Mon Sep 17 00:00:00 2001 From: yaojin Date: Tue, 18 Aug 2026 16:35:10 +0800 Subject: [PATCH] feat(mobile): add session archive support to LAN mobile bridge Add workspace.archiveSession to the RPC allowlist and an archive button to each session row in the mobile UI, so users can archive sessions directly from their phone. Archiving a session removes it from the list (matching the desktop behavior). Closes #14 --- src/main/mobile/lan-mobile-bridge.ts | 1 + src/main/mobile/lan-mobile-pages.ts | 8 ++++++-- test/lan-mobile-bridge.test.ts | 7 +++++++ test/lan-mobile-pages.test.ts | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/mobile/lan-mobile-bridge.ts b/src/main/mobile/lan-mobile-bridge.ts index d4eed6c4..ff256d9f 100644 --- a/src/main/mobile/lan-mobile-bridge.ts +++ b/src/main/mobile/lan-mobile-bridge.ts @@ -11,6 +11,7 @@ const PAIRING_TTL_MS = 5 * 60 * 1000 const RPC_ALLOWLIST = new Set([ 'workspace.list', + 'workspace.archiveSession', 'session.list', 'session.history', 'session.create', diff --git a/src/main/mobile/lan-mobile-pages.ts b/src/main/mobile/lan-mobile-pages.ts index b3d461cf..b54aa653 100644 --- a/src/main/mobile/lan-mobile-pages.ts +++ b/src/main/mobile/lan-mobile-pages.ts @@ -23,7 +23,7 @@ export function renderMobilePage({ locale }: MobilePageOptions): string { header{height:52px;display:flex;flex:none;align-items:center;justify-content:space-between;border-bottom:1px solid var(--line);padding:0 4px}.brand{font-size:15px;font-weight:600;display:flex;align-items:center;gap:8px}.brand img{width:35px;height:20px;object-fit:contain}.brand .dark-logo{display:none}@media(prefers-color-scheme:dark){.brand .light-logo{display:none}.brand .dark-logo{display:block}}.status{width:8px;height:8px;border-radius:50%;background:#35a867;box-shadow:0 0 0 0 rgba(53,168,103,.42);animation:connectedPulse 1.8s ease-out infinite}.status.connecting{background:var(--muted);animation:none}.status.error-state{background:#e34d59;animation:none}@keyframes connectedPulse{70%{box-shadow:0 0 0 7px rgba(53,168,103,0)}100%{box-shadow:0 0 0 0 rgba(53,168,103,0)}} .view{display:none;min-height:0}.view.active{display:flex;flex:1;flex-direction:column}.toolbar{display:flex;flex:none;gap:8px;align-items:center;padding:12px 0}.toolbar select{flex:1;min-width:0}.session-actions{display:flex;gap:7px;padding:12px 0 6px}.session-actions select{flex:1;min-width:0;height:40px}.icon-button{display:grid;place-items:center;width:40px;height:40px;padding:0}.icon-button.refreshing svg{animation:spin .65s linear infinite}.new-session{height:40px;flex:none;font-weight:500;padding:7px 11px;border-radius:10px}.new-session:disabled{cursor:default;opacity:.45}.workspace-hint{color:var(--muted);font-size:12px;line-height:1.4;padding:4px 10px 10px}.workspace-hint[hidden]{display:none}.chat-toolbar{border-bottom:1px solid var(--line);padding:8px 0;min-height:50px}.back{flex:none;border:none!important;padding-left:4px!important;font-weight:500}.back svg{vertical-align:-2px;margin-right:4px}.chat-title{min-width:0;flex:1;text-align:center;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:52px} select,button,textarea{border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:10px;padding:10px 12px}button{cursor:pointer}button:hover{background:var(--hover)}button.primary{display:grid;place-items:center;flex:none;background:var(--brand);color:white;border-color:var(--brand);border-radius:50%;width:42px;height:42px;padding:0}button.quiet{background:transparent} - .list{display:grid;align-content:start;gap:2px;overflow-y:auto;padding:2px 0 12px}.row{width:100%;display:flex;align-items:center;gap:12px;text-align:left;background:transparent;border:0;border-radius:9px;padding:11px 12px}.row:hover,.row:active{background:var(--hover)}.row strong{display:block;min-width:0;flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.row time{flex:none;color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums} + .list{display:grid;align-content:start;gap:2px;overflow-y:auto;padding:2px 0 12px}.row{width:100%;display:flex;align-items:center;gap:12px;text-align:left;background:transparent;border:0;border-radius:9px;padding:11px 12px;cursor:pointer}.row:hover,.row:active{background:var(--hover)}.row strong{display:block;min-width:0;flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.row time{flex:none;color:var(--muted);font-size:12px;font-variant-numeric:tabular-nums}.row .archive-btn{flex:none;width:30px;height:30px;padding:0;display:grid;place-items:center;background:transparent;border:0;border-radius:6px;color:var(--muted);cursor:pointer}.row .archive-btn:hover,.row .archive-btn:focus{background:var(--hover);color:var(--ink)} .empty{padding:56px 14px;text-align:center;color:var(--muted)}.messages{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:0;padding:18px 4px}.message{max-width:100%;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--ink);line-height:1.65}.message.user{align-self:flex-end;max-width:82%;background:var(--hover);border-radius:22px;padding:9px 15px;margin:8px 0 14px}.message.assistant{align-self:stretch;padding:0 4px;margin:0}.message .role{display:none} .markdown{white-space:normal}.markdown>*:first-child{margin-top:0}.markdown>*:last-child{margin-bottom:0}.markdown p{margin:0 0 10px}.markdown h1,.markdown h2,.markdown h3{line-height:1.3;margin:18px 0 8px}.markdown h1{font-size:22px}.markdown h2{font-size:19px}.markdown h3{font-size:16px}.markdown ul{margin:8px 0;padding-left:22px}.markdown pre{overflow:auto;background:var(--hover);border:1px solid var(--line);border-radius:10px;padding:12px;font:13px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}.markdown code{background:var(--hover);border-radius:5px;padding:2px 5px;font:13px ui-monospace,SFMono-Regular,Menlo,monospace}.markdown pre code{padding:0;background:none}.markdown a{color:var(--brand)}.table-wrap{width:100%;overflow-x:auto;margin:10px 0;-webkit-overflow-scrolling:touch}.markdown table{width:100%;min-width:420px;border-collapse:collapse;font-size:13px;line-height:1.5}.markdown th,.markdown td{padding:9px 10px;border:1px solid var(--line);text-align:left;vertical-align:top}.markdown th{background:var(--hover);font-weight:600}.thinking,.tool{border:1px solid var(--line);border-radius:8px;background:var(--card);margin:2px 0;white-space:normal}.thinking summary,.tool summary{cursor:pointer;list-style:none;padding:7px 10px;color:var(--muted);font-size:12px;line-height:20px;display:flex;align-items:center;justify-content:space-between;gap:10px}.thinking summary::-webkit-details-marker,.tool summary::-webkit-details-marker{display:none}.thinking-body,.tool-body{border-top:1px solid var(--line);padding:9px 10px;color:var(--muted);font-size:12px}.tool-name{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.tool-status.error{color:#e34d59}.tool pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;background:var(--hover);border-radius:7px;padding:8px}.streaming:after{content:' ';display:inline-block;width:6px;height:15px;margin-left:2px;vertical-align:-2px;background:var(--brand);animation:pulse .7s infinite alternate} .composer{flex:none;padding:10px 0 2px;background:var(--paper)}.composer-inner{display:flex;gap:8px;align-items:flex-end;border:1px solid var(--line);border-radius:20px;padding:8px 8px 8px 14px;background:var(--card);box-shadow:0 4px 16px rgba(0,0,0,.05)}.composer textarea{flex:1;min-height:42px;max-height:150px;resize:none;border:0;outline:0;border-radius:0;padding:10px 0;background:transparent;font-size:16px}.primary.cancel{background:var(--ink);border-color:var(--ink)}.primary[hidden]{display:none}.error{color:#e34d59;font-size:13px;margin:8px 0}.toast{position:fixed;left:50%;top:calc(64px + env(safe-area-inset-top));z-index:5;transform:translate(-50%,-8px);background:var(--ink);color:var(--paper);border-radius:999px;padding:6px 11px;font-size:12px;opacity:0;pointer-events:none;transition:.18s ease}.toast.show{opacity:1;transform:translate(-50%,0)}.loading{display:grid;gap:12px;padding:22px 4px}.skeleton{height:14px;border-radius:7px;background:var(--hover);animation:pulse 1.1s ease-in-out infinite alternate}.skeleton:nth-child(2){width:82%}.skeleton:nth-child(3){width:64%}@keyframes pulse{to{opacity:.35}}@keyframes spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.skeleton,.status,.icon-button.refreshing svg{animation:none}} @@ -46,6 +46,8 @@ export function renderMobilePage({ locale }: MobilePageOptions): string {