orbit-codex-bridge 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,56 +0,0 @@
1
- <!doctype html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta name="theme-color" content="#153c37"><title>连接电脑 · Orbit</title><link rel="icon" href="/icon.svg" type="image/svg+xml"><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/layout.css"><script src="/connect.js" defer></script>
5
- </head>
6
- <body>
7
- <main class="pair-screen connect-screen">
8
- <section class="pair-art">
9
- <a class="brand light" href="/"><img src="/icon.svg" alt=""><span>orbit<span class="brand-dot">.</span></span><small>SECURE CLOUD</small></a>
10
- <div class="pair-copy"><span class="eyebrow">ONE-TIME DEVICE AUTHORIZATION</span><h1>安全连接<br>你的电脑。</h1><p>连接码只对当前电脑有效,并在数分钟后失效。</p></div>
11
- <span class="art-caption">DEVICE-BOUND / EXPIRES IN 5 MINUTES</span>
12
- </section>
13
- <section class="pair-form-wrap">
14
- <div class="pair-form connect-form">
15
- <span class="mini-badge">COMPUTER CONNECTION</span>
16
- <section id="connect-login">
17
- <div class="connect-auth-tabs"><button id="connect-show-login" class="active" type="button" aria-pressed="true">登录</button><button id="connect-show-register" type="button" aria-pressed="false">创建账号</button></div>
18
- <h2 id="connect-auth-title">登录后确认连接</h2>
19
- <p id="connect-auth-description">请登录你要用于这台电脑的 Orbit 账号。</p>
20
- <form id="connect-login-form">
21
- <label for="connect-username">用户名</label><input id="connect-username" autocomplete="username" required>
22
- <label for="connect-password">密码</label><input id="connect-password" type="password" autocomplete="current-password" required>
23
- <button class="primary full" type="submit">安全登录 <span>↗</span></button>
24
- <p id="connect-login-error" class="error" role="alert"></p>
25
- </form>
26
- <form id="connect-register-form" hidden>
27
- <label for="connect-register-username">用户名</label><input id="connect-register-username" autocomplete="username" minlength="3" maxlength="64" autocapitalize="none" spellcheck="false" required>
28
- <label for="connect-register-password">设置密码</label><input id="connect-register-password" type="password" autocomplete="new-password" minlength="6" maxlength="256" placeholder="至少 6 个字符" required>
29
- <label for="connect-register-confirm">确认密码</label><input id="connect-register-confirm" type="password" autocomplete="new-password" minlength="6" maxlength="256" required>
30
- <button class="primary full" type="submit">创建账号并确认 <span>↗</span></button>
31
- <p id="connect-register-error" class="error" role="alert"></p>
32
- </form>
33
- <a id="connect-home" href="/">返回 Orbit 工作台</a>
34
- </section>
35
- <section id="connect-approval" hidden>
36
- <h2>确认添加这台电脑</h2>
37
- <p>检查电脑名称和账号,批准后专属凭证只交给发起请求的电脑。</p>
38
- <dl><dt>电脑名称</dt><dd id="connect-computer-name"></dd><dt>连接码</dt><dd id="connect-user-code"></dd><dt>连接到账号</dt><dd id="connect-account"></dd></dl>
39
- <p id="connect-approval-error" class="error" role="alert"></p>
40
- <button id="connect-approve" class="primary full" type="button">确认连接这台电脑 <span>↗</span></button>
41
- </section>
42
- <section id="connect-complete" hidden>
43
- <h2>电脑已授权</h2>
44
- <p id="connect-complete-message">授权凭证正在安全发送到你的电脑。完成后回到工作台即可查看连接状态。</p>
45
- <a id="connect-workspace" class="primary full" href="/">返回 Orbit 工作台 <span>↗</span></a>
46
- </section>
47
- <section id="connect-expired" hidden>
48
- <h2>连接请求已过期</h2>
49
- <p>出于安全考虑,连接请求只能使用一次。请回到电脑重新运行连接命令。</p>
50
- <a class="primary full" href="/">返回 Orbit <span>↗</span></a>
51
- </section>
52
- </div>
53
- </section>
54
- </main>
55
- </body>
56
- </html>
package/public/connect.js DELETED
@@ -1,126 +0,0 @@
1
- const select=selector=>document.querySelector(selector);
2
- const parameters=new URLSearchParams(location.search);
3
- const userCode=(parameters.get('code')||'').trim().toUpperCase();
4
- const state={authenticated:false,device:null};
5
-
6
- async function api(url,{method='GET',body}={}){
7
- const response=await fetch(url,{
8
- method,
9
- credentials:'same-origin',
10
- cache:'no-store',
11
- headers:body===undefined?{}:{'Content-Type':'application/json'},
12
- ...(body===undefined?{}:{body:JSON.stringify(body)})
13
- });
14
- const result=await response.json();
15
- if(!response.ok)throw Object.assign(new Error(result.error||'请求失败。'),{status:response.status,code:result.code});
16
- return result;
17
- }
18
-
19
- function show(section){
20
- for(const name of ['login','approval','complete','expired'])select(`#connect-${name}`).hidden=name!==section;
21
- }
22
-
23
- function setAuthMode(register){
24
- select('#connect-login-form').hidden=register;
25
- select('#connect-register-form').hidden=!register;
26
- select('#connect-auth-title').textContent=register?'创建账号并添加电脑':'登录后确认连接';
27
- select('#connect-auth-description').textContent=register?'创建成功后会自动将这台电脑绑定到新账号。':'请登录你要用于这台电脑的 Orbit 账号。';
28
- select('#connect-show-login').classList.toggle('active',!register);
29
- select('#connect-show-register').classList.toggle('active',register);
30
- select('#connect-show-login').setAttribute('aria-pressed',String(!register));
31
- select('#connect-show-register').setAttribute('aria-pressed',String(register));
32
- }
33
-
34
- async function loadRequest(){
35
- if(!state.authenticated){
36
- try{
37
- const profile=await api('/api/cloud/me');
38
- state.authenticated=true;
39
- await showRequest(profile.user);
40
- }catch(error){
41
- if(error.status!==401)throw error;
42
- show('login');
43
- }
44
- return;
45
- }
46
- const profile=await api('/api/cloud/me');
47
- await showRequest(profile.user);
48
- }
49
-
50
- async function showRequest(user){
51
- try{
52
- const request=await api(`/api/cloud/device-auth/request?code=${encodeURIComponent(userCode)}`);
53
- state.device=request;
54
- select('#connect-computer-name').textContent=request.name;
55
- select('#connect-user-code').textContent=request.userCode.match(/.{1,2}/g).join('-');
56
- select('#connect-account').textContent=user.displayName||user.username;
57
- select('#connect-approval-error').textContent='';
58
- show('approval');
59
- }catch(error){
60
- if(error.status===410){show('expired');return}
61
- throw error;
62
- }
63
- }
64
-
65
- select('#connect-show-login').onclick=()=>setAuthMode(false);
66
- select('#connect-show-register').onclick=()=>setAuthMode(true);
67
- select('#connect-login-form').addEventListener('submit',async event=>{
68
- event.preventDefault();
69
- const button=event.submitter;
70
- button.disabled=true;
71
- select('#connect-login-error').textContent='';
72
- try{
73
- await api('/api/cloud/login',{method:'POST',body:{
74
- username:select('#connect-username').value,
75
- password:select('#connect-password').value
76
- }});
77
- select('#connect-password').value='';
78
- state.authenticated=true;
79
- await loadRequest();
80
- }catch(error){select('#connect-login-error').textContent=error.message}
81
- finally{button.disabled=false}
82
- });
83
- select('#connect-register-form').addEventListener('submit',async event=>{
84
- event.preventDefault();
85
- const button=event.submitter;
86
- button.disabled=true;
87
- select('#connect-register-error').textContent='';
88
- if(select('#connect-register-password').value!==select('#connect-register-confirm').value){
89
- select('#connect-register-error').textContent='两次输入的密码不一致。';
90
- select('#connect-register-confirm').focus();
91
- button.disabled=false;
92
- return;
93
- }
94
- try{
95
- await api('/api/cloud/register',{method:'POST',body:{
96
- username:select('#connect-register-username').value,
97
- password:select('#connect-register-password').value
98
- }});
99
- select('#connect-register-form').reset();
100
- state.authenticated=true;
101
- await loadRequest();
102
- }catch(error){
103
- if(error.status===410)show('expired');
104
- else select('#connect-register-error').textContent=error.message;
105
- }finally{button.disabled=false}
106
- });
107
- select('#connect-approve').addEventListener('click',async event=>{
108
- const button=event.currentTarget;
109
- button.disabled=true;
110
- select('#connect-approval-error').textContent='';
111
- try{
112
- await api('/api/cloud/device-auth/approve',{method:'POST',body:{userCode:state.device.userCode}});
113
- select('#connect-complete-message').textContent=`“${state.device.name}”已添加到你的账号。授权凭证已安全发送到电脑。`;
114
- show('complete');
115
- }catch(error){
116
- if(error.status===410)show('expired');
117
- else select('#connect-approval-error').textContent=error.message;
118
- }finally{button.disabled=false}
119
- });
120
-
121
- select('#connect-workspace').href=location.origin;
122
- if(!/^[0-9A-F]{10}$/.test(userCode))show('expired');
123
- else loadRequest().catch(error=>{
124
- select('#connect-approval-error').textContent=error.message;
125
- show(state.authenticated?'approval':'login');
126
- });
package/public/goal.css DELETED
@@ -1,7 +0,0 @@
1
- .goal-button.active{background:#edf3e3;border-color:#dce7cd;color:#456337}
2
- #goal-dialog label{display:block;font-size:11px;color:#718563;margin:16px 0 7px}
3
- #goal-dialog textarea,#goal-dialog input{display:block;width:100%;border:1px solid #dce4d1;border-radius:8px;padding:11px 12px;background:#fff;color:#405139;font-size:12px}
4
- #goal-dialog textarea{resize:vertical;min-height:100px;line-height:1.7}
5
- #goal-dialog textarea:disabled,#goal-dialog input:disabled{background:#f0f2ec;color:#929b87}
6
- #goal-dialog .dialog-actions{flex-wrap:wrap}
7
- @media(max-width:760px){#goal-dialog{padding:22px 18px}#goal-dialog .dialog-actions{gap:7px}#goal-dialog .dialog-actions button{font-size:10px;padding:8px 10px}}
package/public/icon.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="18" fill="#153c37"/><circle cx="32" cy="32" r="13" fill="none" stroke="#d6edab" stroke-width="5"/><ellipse cx="32" cy="32" rx="25" ry="10" transform="rotate(-38 32 32)" fill="none" stroke="#d6edab" stroke-width="2.5"/><circle cx="52" cy="16" r="4" fill="#d6edab"/></svg>
package/public/index.html DELETED
@@ -1,60 +0,0 @@
1
- <!doctype html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta name="theme-color" content="#153c37"><meta name="description" content="Orbit,让原生 Codex 会话随行。"><title>Orbit · 会话随行</title><link rel="icon" href="/icon.svg" type="image/svg+xml"><link rel="manifest" href="/manifest.webmanifest"><link rel="stylesheet" href="/style.css"><script src="/vendor/marked.js" defer></script><script src="/vendor/purify.js" defer></script><script src="/app.js" defer></script>
5
- <link rel="stylesheet" href="/layout.css">
6
- <link rel="stylesheet" href="/goal.css">
7
- </head>
8
- <body>
9
- <section id="cloud-login-screen" class="pair-screen" hidden>
10
- <div class="pair-art"><a class="brand light" href="/"><img src="/icon.svg" alt=""><span>orbit<span class="brand-dot">.</span></span><small>SECURE CLOUD</small></a><div class="pair-copy"><span class="eyebrow">YOUR DEVICES. YOUR SESSION.</span><h1>连接你的<br>个人工作空间。</h1><p>通过账号连接云端工作台。会话仍留在你的电脑上。</p></div><span class="art-caption">DEVICE-BOUND / LOCAL-FIRST</span></div>
11
- <div class="pair-form-wrap"><div class="pair-form">
12
- <span class="mini-badge">CLOUD WORKSPACE</span>
13
- <div id="cloud-auth-tabs" class="cloud-auth-tabs" role="group" aria-label="账号入口" hidden><button id="cloud-show-login" class="active" type="button" aria-pressed="true">登录</button><button id="cloud-show-register" type="button" aria-pressed="false">创建账号</button></div>
14
- <h2 id="cloud-auth-title">登录 Orbit</h2><p id="cloud-auth-description">登录后,继续连接你的电脑和原有会话。</p>
15
- <form id="cloud-login-form">
16
- <label for="cloud-username">用户名</label><input id="cloud-username" autocomplete="username" maxlength="64" autocapitalize="none" spellcheck="false" required>
17
- <label for="cloud-password">密码</label><input id="cloud-password" type="password" autocomplete="current-password" maxlength="256" required>
18
- <button class="primary full" type="submit">安全登录 <span>↗</span></button><p id="cloud-login-error" class="error" role="alert"></p>
19
- </form>
20
- <form id="cloud-register-form" hidden>
21
- <label for="register-username">用户名</label><input id="register-username" autocomplete="username" minlength="3" maxlength="64" autocapitalize="none" spellcheck="false" aria-describedby="register-username-help" required>
22
- <small id="register-username-help" class="field-help">3–64 位,可用英文字母、数字、点、连字符和下划线。</small>
23
- <label for="register-display-name">显示名称 <span class="optional">选填</span></label><input id="register-display-name" autocomplete="nickname" maxlength="120" placeholder="你希望我们如何称呼你">
24
- <label for="register-password">设置密码</label><input id="register-password" type="password" autocomplete="new-password" minlength="6" maxlength="256" placeholder="至少 6 个字符" required>
25
- <label for="register-password-confirm">确认密码</label><input id="register-password-confirm" type="password" autocomplete="new-password" minlength="6" maxlength="256" required>
26
- <p class="registration-note">免费账号可连接 1 台电脑。注册后即可添加电脑。</p>
27
- <button class="primary full" type="submit">创建账号并继续 <span>↗</span></button><p id="cloud-register-error" class="error" role="alert"></p>
28
- </form>
29
- <p id="cloud-registration-notice" class="field-help" hidden>当前暂停自助注册,请联系管理员开通账号。</p><p class="cloud-admin-link"><a href="/admin">管理员入口</a></p>
30
- </div></div>
31
- </section>
32
- <section id="cloud-device-screen" class="devices-view cloud-device-screen" hidden>
33
- <header class="cloud-device-header"><div><span class="eyebrow">YOUR CONNECTED COMPUTERS</span><h1 id="cloud-devices-title">选择要连接的电脑</h1><p id="cloud-device-message">只显示绑定到你账号的设备。</p><p id="cloud-device-quota"></p></div><div class="cloud-device-actions"><button id="cloud-refresh-devices" class="button small" type="button">刷新状态</button><button id="cloud-add-computer" class="primary" type="button">添加电脑</button><button id="cloud-logout-picker" class="button small" type="button">退出登录</button></div></header>
34
- <section id="cloud-onboarding" class="cloud-onboarding" hidden><div><span class="mini-badge">FIRST CONNECTION</span><h2>一条命令,连接你的电脑</h2><p>电脑需要运行 Codex 桌面应用。Windows 用户复制下面命令到 PowerShell 运行。</p><ol><li>命令会检查 Node.js 24+,缺少时通过 winget 安装。</li><li>浏览器打开后登录此 Orbit 账号,确认添加这台电脑。</li><li>手机登录同一账号,选择已上线的电脑即可使用,无需扫码或输入配对密钥。</li></ol><div class="cloud-command-box"><pre class="cloud-install-command"><code id="cloud-install-command"></code></pre><button id="cloud-copy-install" class="button small" type="button">复制命令</button></div><p id="cloud-install-status" class="cloud-install-status" role="status"></p><p class="cloud-release-note">如果电脑没有 winget,请先从 nodejs.org 安装 Node.js 24+。winget 可能会请求系统安装授权。</p><p class="cloud-release-note">macOS / Linux:先安装 Node.js 24+,再运行 <code>npx --yes --package=orbit-codex-bridge@latest orbit login</code>。</p></div></section>
35
- <div id="cloud-devices-list" class="device-grid cloud-device-list"></div></section>
36
- <section id="pair-screen" class="pair-screen" hidden>
37
- <div class="pair-art"><a class="brand light" href="/"><img src="/icon.svg" alt=""><span>orbit<span class="brand-dot">.</span></span></a><div class="orbital-art"><div class="orbit-ring ring-one"></div><div class="orbit-ring ring-two"></div><div class="orbit-core">o</div><span class="orbit-point"></span></div><div class="pair-copy"><span class="eyebrow">YOUR WORK. IN YOUR ORBIT.</span><h1>会话留在电脑,<br>灵感随你出发。</h1><p>不复制上下文,不从头再来。<br>从熟悉的项目,找到原来的对话。</p></div><span class="art-caption">LOCAL FIRST / BUILT FOR CONTINUITY</span></div>
38
- <div class="pair-form-wrap"><div class="pair-form"><span class="mini-badge">PRIVATE WORKSPACE</span><h2>连接你的工作台</h2><p>输入电脑上的配对密钥。会话内容仅向已配对的设备开放。</p><form id="pair-form"><label for="pair-code">设备配对密钥</label><input id="pair-code" type="password" placeholder="粘贴本机配对密钥" autocomplete="off" required><button class="primary full" type="submit">安全连接 <span>↗</span></button><p id="pair-error" class="error" role="alert"></p></form><div class="pair-help"><span data-icon="shield"></span><div><strong>原会话始终留在电脑</strong><p>选择原会话后,消息和操作会发送到这台电脑上正在运行的 Codex。</p></div></div><details><summary>在哪里找到密钥?</summary><p>在 Orbit 目录运行 <code>Get-Content .runtime\pairing-key</code>。仅用于可信设备;它不是模型 API Key。</p></details></div><footer>ORBIT <span>原生会话 · 电脑执行</span></footer></div>
39
- </section>
40
- <div id="workspace" class="workspace" hidden>
41
- <button id="scrim" class="scrim" aria-label="收起导航" hidden></button>
42
- <aside id="sidebar" class="sidebar">
43
- <a class="brand light" href="/"><img src="/icon.svg" alt=""><span>orbit<span class="brand-dot">.</span></span><small>PREVIEW</small></a><div class="workspace-label"><span class="workspace-avatar">O</span><div><strong>我的工作空间</strong><small>个人 · 本地优先</small></div><span data-icon="chevron"></span></div><span class="nav-label">工作台</span>
44
- <nav><button class="nav-item active" data-view="sessions"><span data-icon="inbox"></span>所有会话 <span id="nav-count" class="nav-count">—</span></button><button class="nav-item" data-view="archived"><span data-icon="archive"></span>已归档</button><button class="nav-item" data-view="devices"><span data-icon="devices"></span>设备与连接 <i class="tiny-dot"></i></button></nav><div class="project-heading"><span class="nav-label">项目空间</span><span data-icon="folder"></span></div><div id="projects" class="projects"></div>
45
- <div class="sidebar-bottom"><div class="device-mini"><span class="device-icon" data-icon="monitor"></span><div><strong id="device-name">正在连接</strong><small><i class="tiny-dot"></i> 本机桥接在线</small></div><span data-icon="chevron"></span></div><p><span data-icon="shield"></span> 不上传模型密钥</p></div>
46
- </aside>
47
- <main class="main">
48
- <header class="topbar"><div class="breadcrumbs"><button id="menu-button" class="icon-button mobile-only" aria-label="展开导航"><span data-icon="menu"></span></button><span data-icon="orbit"></span><span>工作空间</span><span class="slash">/</span><strong id="breadcrumb-current">所有会话</strong></div><div class="top-actions"><span class="live-pill" id="connection-pill"><i class="tiny-dot"></i> 正在连接</span><button id="connect-device" class="button small"><span data-icon="devices"></span><span class="desktop-label">连接设备</span></button><button id="cloud-device-switcher" class="button small" type="button" hidden></button><button id="logout" class="avatar-button" title="断开当前设备" aria-label="断开当前设备">O</button></div></header>
49
- <section id="sessions-view" class="sessions-view">
50
- <section class="thread-pane"><div class="pane-heading"><span class="eyebrow">YOUR CONVERSATIONS</span><div><h1 id="list-title">所有会话</h1><span id="thread-count" class="count-badge">—</span><button id="refresh" class="icon-button" aria-label="刷新会话"><span data-icon="refresh"></span></button></div><p>原来的项目,原来的上下文。</p></div><div class="search-box"><span data-icon="search"></span><input id="search" type="search" placeholder="搜索会话或项目" aria-label="搜索会话或项目"><kbd>⌘ K</kbd></div><div class="filters" role="group" aria-label="会话筛选"><button class="active" data-filter="all" aria-pressed="true">全部</button><button data-filter="locked" aria-pressed="false">已占用</button><button data-filter="pinned" aria-pressed="false">已置顶</button><button data-filter="archived" aria-pressed="false">已归档</button></div><div id="thread-list" class="thread-list" aria-label="原生会话列表"></div><div class="list-footer"><span data-icon="database"></span><span id="list-footer-text">读取 Codex 原生记录</span><i class="tiny-dot"></i></div></section>
51
- <section id="conversation" class="conversation"><div id="welcome" class="welcome"></div><div id="chat" class="chat" hidden><header class="chat-header"><button id="back" class="icon-button mobile-only" aria-label="返回会话列表"><span data-icon="back"></span></button><div class="chat-title"><h2 id="chat-title"></h2><p id="chat-subtitle"></p></div><button id="details-toggle" class="icon-button" aria-label="查看会话详情"><span data-icon="info"></span></button><button id="enable-control" class="button small">接续会话</button></header><div id="chat-details" class="chat-details" hidden></div><div id="mode-banner" class="mode-banner"></div><div id="messages" class="messages" aria-live="polite"></div><div id="approvals" class="approvals"></div><div class="composer-wrap"><div class="session-settings-bar"><button id="model-settings-button" class="model-settings-button" type="button" title="选择模型和推理级别" aria-label="选择模型和推理级别" disabled><span data-icon="settings"></span><span id="model-settings-label">当前模型</span></button><button id="browse-files" class="button small" type="button" aria-label="浏览电脑项目文件">电脑文件</button><div id="context-meter" class="context-meter" title="暂无准确的上下文用量"><span>上下文</span><strong id="context-label">暂无用量</strong><span class="context-track" role="progressbar" aria-label="上下文已用比例" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" hidden><span id="context-fill"></span></span></div></div><form id="composer" class="composer"><textarea id="message-input" placeholder="沿着这条会话继续…" rows="2" aria-label="发送消息"></textarea><div id="composer-attachments" class="composer-attachments" aria-live="polite"></div><div class="composer-toolbar"><label class="turn-mode-label" for="turn-mode">模式</label><select id="turn-mode" class="turn-mode-select" aria-label="本轮对话模式"><option value="default">普通</option><option value="plan">计划</option><option value="goal">目标(本轮)</option></select><input id="file-input" type="file" multiple hidden><button id="attach-files" class="button small" type="button" aria-label="添加附件">添加文件</button><span id="composer-mode"><span data-icon="shield"></span>只读镜像</span><button id="stop-turn" class="button small" type="button" hidden>停止任务</button><button id="send-message" class="send-button" aria-label="发送消息" disabled><span data-icon="arrow"></span></button></div></form><p class="composer-note" id="composer-note">查看不会唤醒会话,也不会打断电脑上的执行。</p></div></div></section>
52
- </section><section id="devices-view" class="devices-view" hidden></section>
53
- </main>
54
- </div>
55
- <dialog id="goal-dialog"><form id="goal-form"><h2>会话目标</h2><p id="goal-description">设置 Codex 持续追求的原生目标;目标会保存在这条原会话中。</p><label for="goal-objective">目标</label><textarea id="goal-objective" rows="4" maxlength="5000" placeholder="描述希望 Codex 持续完成的结果"></textarea><label for="goal-token-budget">Token 预算(可选)</label><input id="goal-token-budget" type="number" min="1" max="10000000" step="1" placeholder="不设置"><p id="goal-current-status" class="model-note"></p><p id="goal-error" class="error" role="alert"></p><div class="dialog-actions"><button id="clear-goal" type="button" class="button">清除目标</button><button id="pause-goal" type="button" class="button">暂停目标</button><button id="cancel-goal" type="button" class="button">取消</button><button id="save-goal" type="submit" class="primary">开始追求目标</button></div></form></dialog>
56
- <dialog id="control-dialog"><form id="control-form"><span class="mini-badge">CONTINUE, NOT FORK</span><h2 id="control-title">授权热接管</h2><p id="control-description">保持原会话与桌面执行实例,不创建新会话或独立执行器。</p><div class="notice"><span data-icon="shield"></span><span id="control-notice">指令会交给原 Codex 桌面会话执行,并沿用其权限与审批策略。</span></div><label class="check-row" id="allow-write-row"><input id="allow-write" type="checkbox"><span>允许修改此会话的工作区文件<small>默认只读。扩大权限仍需要审批;不会启用无限制执行。</small></span></label><label class="check-row"><input id="control-confirm" type="checkbox" required><span id="control-confirm-copy">我确认授权此设备控制这条原生会话。</span></label><div class="dialog-actions"><button type="button" id="cancel-control" class="button">先只读查看</button><button class="primary" id="confirm-control-button" type="submit">授权热接管 <span>↗</span></button></div><p id="control-error" class="error" role="alert"></p></form></dialog>
57
- <dialog id="model-dialog"><form id="model-form"><h2>会话参数</h2><p id="model-description">更改将在这条会话的下一轮生效。</p><label for="model-select">模型</label><select id="model-select" required></select><label for="effort-select">推理级别</label><select id="effort-select"></select><p id="model-note" class="model-note"></p><p id="model-error" class="error" role="alert"></p><div class="dialog-actions"><button id="cancel-model" type="button" class="button">取消</button><button id="authorize-model" type="button" class="primary" hidden>先授权热接管</button><button id="apply-model" type="submit" class="primary">应用到下一轮</button></div></form></dialog>
58
- <dialog id="file-browser-dialog" class="file-browser-dialog"><section class="file-browser-panel"><header><div><h2>电脑项目文件</h2><p id="file-browser-path">项目根目录</p></div><button id="close-file-browser" class="button small" type="button">关闭</button></header><p class="file-browser-notice">仅浏览当前会话工作目录;系统目录、凭证文件及符号链接不会开放。单个文件最大 8 MiB。</p><div id="file-browser-list" class="file-browser-list" aria-live="polite"></div><p id="file-browser-error" class="error" role="alert"></p></section></dialog>
59
- <div id="toast" class="toast" role="status" hidden></div>
60
- </body></html>
package/public/layout.css DELETED
@@ -1,73 +0,0 @@
1
- .devices-view{padding:35px 40px;overflow:auto;flex:1}.devices-heading h1{font-size:26px;font-weight:550;margin:0 0 9px}.devices-heading p{font-size:12px;color:#93a086}.device-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:28px;max-width:980px}.device-card{border:1px solid #e1e7d6;background:#fff;border-radius:12px;padding:25px}.device-card h2{font-size:15px;font-weight:550;margin:0 0 10px;display:flex;align-items:center;gap:9px}.device-card p{font-size:11px;color:#929e84;line-height:1.9;overflow-wrap:anywhere}.device-big-icon{width:48px;height:48px;border-radius:12px;background:#edf3e3;display:grid;place-items:center;color:#739557;margin-bottom:18px}.device-big-icon svg{width:24px;height:24px}.device-card dl{font-size:10px;line-height:1.9;display:grid;grid-template-columns:80px 1fr;gap:8px;margin:24px 0 0}.device-card dt{color:#9aa68c}.device-card dd{margin:0;color:#5f7450;overflow-wrap:anywhere}.device-card.wide{grid-column:1/-1}.capability-row{display:flex;gap:15px;align-items:center;padding:15px 0;border-bottom:1px solid #edf0e6;font-size:11px}.capability-row:last-child{border:0}.capability-row>span:first-child{flex:1}.capability-state{font-size:9px;background:#edf3e3;color:#739057;padding:5px 8px;border-radius:5px}.capability-state.pending{background:#f5f0e4;color:#ad9155}.connection-url{display:flex;align-items:center;gap:7px;margin-top:12px}.connection-url code{font-size:10px;padding:10px;background:#f1f5e9;border-radius:6px;flex:1;overflow-wrap:anywhere}.qr-wrap{margin-top:18px;text-align:center}.qr-wrap img{width:200px;height:200px;max-width:100%;border:1px solid #e1e7d7;border-radius:12px}.qr-wrap p{font-size:10px;color:#ad8c52}.notice{display:flex;align-items:flex-start;gap:10px;background:#f2f1e7;border:1px solid #e9e5cf;color:#97865c;padding:12px 13px;border-radius:8px;font-size:11px;line-height:1.9}.notice svg{margin-top:2px;width:15px;height:15px}.primary{padding:12px 18px;background:#214d37;color:#eff6df;border-radius:8px;font-size:12px;display:inline-flex;align-items:center;justify-content:center;gap:18px;min-height:40px}.primary:hover{background:#2c6045}.primary.full{width:100%;justify-content:space-between}.mini-badge{font-size:9px;letter-spacing:1.5px;color:#7e9967}.error{font-size:11px;line-height:1.8;color:#b6634f!important;min-height:12px}dialog{border:1px solid #dfe6d3;border-radius:15px;padding:30px;color:#293a30;max-width:480px;width:calc(100% - 32px);background:#fbfcf7;box-shadow:0 30px 120px #123a2929}dialog::backdrop{background:#102c2266;backdrop-filter:blur(5px)}dialog h2{font-size:21px;font-weight:550;margin:16px 0}dialog p{font-size:12px;line-height:1.9;color:#8b9881}.check-row{display:flex;align-items:flex-start;gap:9px;font-size:11px;margin-top:18px;line-height:1.8}.check-row input{accent-color:#3f704b;margin-top:4px}.check-row small{display:block;font-size:9px;color:#9aa48f;line-height:1.8;margin-top:3px}.dialog-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:28px}.toast{position:fixed;bottom:25px;left:50%;transform:translateX(-50%);z-index:100;padding:12px 19px;max-width:min(540px,90vw);border-radius:9px;background:#23452f;color:#e8f1db;font-size:11px;line-height:1.8;box-shadow:0 6px 30px #203d2133}.pair-screen{display:grid;grid-template-columns:1fr 1fr;min-height:100dvh;background:#fbfcf8}.pair-art{background:#163b31;position:relative;overflow:hidden;padding:45px 50px;color:#e9f2d9;min-height:720px}.orbital-art{height:310px;position:relative;width:300px;margin:35px auto 5px;display:grid;place-items:center}.orbit-ring{position:absolute;width:260px;height:260px;border:1px solid #cee8a34a;border-radius:50%;transform:rotate(-35deg) scaleY(.45)}.ring-two{transform:rotate(35deg) scaleY(.45)}.orbit-core{font-size:140px;line-height:1;color:#d6e9b3;font-weight:300;letter-spacing:-10px;margin-top:-25px}.orbit-point{position:absolute;top:90px;left:35px;width:13px;height:13px;background:#dcecb9;border-radius:50%;box-shadow:0 0 45px #d0ea91aa}.pair-copy{position:relative;max-width:450px}.pair-copy .eyebrow{color:#a9c38e;font-size:9px}.pair-copy h1{font-size:37px;font-weight:450;line-height:1.7;letter-spacing:-1px;margin:15px 0 17px}.pair-copy p{font-size:12px;color:#9fb596;line-height:2.2}.art-caption{display:block;position:absolute;bottom:30px;font-size:8px;letter-spacing:2px;color:#819d77}.pair-form-wrap{padding:70px 60px 25px;display:flex;flex-direction:column;justify-content:center}.pair-form{width:100%;max-width:350px;margin:auto}.pair-form h2{font-size:29px;font-weight:500;letter-spacing:-1px;margin:16px 0}.pair-form>p{font-size:12px;color:#929e87;line-height:2;margin-bottom:32px}.pair-form label{display:block;font-size:11px;color:#718563;margin-bottom:9px}.pair-form input{display:block;width:100%;border:1px solid #dce4d1;border-radius:8px;padding:13px;font-size:12px;background:#fff;margin-bottom:15px;color:#49613a}.pair-form input::placeholder{color:#afb79f}.pair-help{display:flex;gap:10px;padding:20px 0;margin-top:10px;border-top:1px solid #e4e8dd}.pair-help>span{color:#86a76c}.pair-help strong{font-size:11px;font-weight:500}.pair-help p{font-size:10px;color:#9aa68d;line-height:1.9}.pair-form details{font-size:10px;color:#9aa68d;line-height:2}.pair-form details summary{cursor:pointer}.pair-form code{font-size:10px;color:#70825e;overflow-wrap:anywhere}.pair-form-wrap footer{display:flex;justify-content:space-between;font-size:8px;color:#a1ab95;margin-top:40px;letter-spacing:.8px}.mobile-only{display:none}.approvals{max-height:270px;overflow:auto;padding:0 22px}.approval-card{border:1px solid #e0d9b9;background:#faf8ed;border-radius:9px;padding:14px;margin:8px 0;font-size:11px}.approval-card p{color:#927e4c;line-height:1.8;overflow-wrap:anywhere}.approval-card pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:10px}.approval-card input{display:block;width:100%;margin:8px 0;padding:8px;border:1px solid #e3dcc3;border-radius:5px}.approval-card .button{margin-right:8px}.approval-card label{font-size:10px;display:block;line-height:1.8}@keyframes enter{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}
2
- @media(min-width:1550px){.sidebar{width:250px}.thread-pane{width:370px}.welcome{padding:50px 55px;max-width:1100px;margin:auto}.hero{min-height:240px;padding:36px}.hero h2{font-size:35px}.hero-orbit{right:15px;width:250px;height:250px}.messages{padding:30px 50px}.message-body{font-size:13px}.composer-wrap{padding:10px 40px 20px}}
3
- @media(max-width:1180px){.sidebar{width:200px;padding-left:12px;padding-right:12px}.thread-pane{width:300px;padding-left:14px;padding-right:14px}.welcome{padding:30px 24px}.hero{padding:25px 22px}.hero h2{font-size:25px}.hero-orbit{right:-100px}.date-label{display:none}.topbar{padding:0 24px}.top-actions{gap:12px}.stat-card{padding:13px 12px}.pair-art{padding:35px}.pair-form-wrap{padding:55px 40px}}
4
- @media(max-width:950px){.sidebar{width:185px}.sidebar .brand{font-size:27px}.sidebar .brand small{display:none}.thread-pane{width:277px}.welcome{padding:25px 19px}.welcome-heading h2{font-size:20px}.hero{padding:22px 20px}.hero h2{font-size:23px}.hero p{max-width:230px}.hero-orbit{display:none}.stats-grid{gap:7px}.stat-card{padding:12px 8px}.stat-card>span{font-size:8px;gap:4px}.stat-card strong{font-size:23px}.stat-card small{font-size:7px}.device-grid{grid-template-columns:1fr}.device-card.wide{grid-column:auto}.devices-view{padding:25px}.topbar{padding:0 18px}.breadcrumbs{gap:8px}.top-actions{gap:10px}.desktop-label{display:none}.chat-header{padding:16px 15px}.chat-header .button{font-size:9px}.messages{padding:22px 17px}.mode-banner{margin-left:14px;margin-right:14px}.composer-wrap{padding-left:14px;padding-right:14px}.pair-copy h1{font-size:30px}.orbital-art{width:250px}.pair-form h2{font-size:25px}}
5
- @media(max-width:760px){.mobile-only{display:inline-flex}.sidebar{position:fixed;inset:0 auto 0 0;width:240px;transform:translateX(-100%);transition:transform .2s;box-shadow:15px 0 50px #18312422}.workspace.nav-open .sidebar{transform:translateX(0)}.scrim{position:fixed;inset:0;background:#152e2655;z-index:19;backdrop-filter:blur(2px)}.topbar{height:61px;padding:0 14px}.breadcrumbs{gap:8px;font-size:10px}.breadcrumbs>span[data-icon],.breadcrumbs>.slash,.breadcrumbs>span:not([data-icon]):not(.slash){display:none}.top-actions{gap:11px}.top-actions>.button{display:none}.top-actions>.live-pill{font-size:9px}.avatar-button{width:26px;height:26px}.sessions-view{display:block;position:relative;overflow:hidden}.thread-pane{width:100%;border:0;height:100%;padding:23px 20px 0}.pane-heading{padding:0 3px}.pane-heading h1{font-size:27px}.pane-heading p{font-size:12px}.search-box{padding:12px 13px}.search-box input{font-size:12px}.filters{margin:15px 0 6px}.filters button{font-size:11px}.thread-item{padding:17px 12px;border-bottom:1px solid #e8ecdf;border-radius:0;margin:0}.thread-item.selected{border-radius:8px}.thread-meta{font-size:10px}.thread-meta time{font-size:10px}.thread-item h3{font-size:13px;margin-top:9px}.thread-item p{font-size:11px}.tag{font-size:9px}.conversation{display:none;height:100%;position:absolute;inset:0}.workspace.chat-open .conversation{display:flex}.workspace.chat-open .thread-pane{display:none}.welcome{display:none}.chat-header{min-height:74px;padding:14px 12px;gap:5px}.chat-title h2{font-size:13px}.chat-header .button{padding:7px 9px;font-size:10px}.mode-banner{font-size:10px;margin:12px 15px 0}.messages{padding:22px 18px 8px}.message-body{font-size:12px;padding-left:0;line-height:1.95}.message.user .message-body{margin-left:25px;padding:12px}.composer-wrap{padding:8px 12px max(12px,env(safe-area-inset-bottom))}.composer textarea{font-size:13px}.devices-view{padding:25px 20px}.device-grid{gap:14px;margin-top:22px}.device-card{padding:21px}.pair-screen{display:flex;flex-direction:column}.pair-art{padding:25px;min-height:260px}.pair-art>.brand{font-size:26px}.pair-art>.brand img{width:29px;height:29px}.orbital-art{position:absolute;right:-50px;top:-15px;width:250px;height:230px;margin:0;opacity:.6}.orbit-ring{width:200px;height:200px}.orbit-core{font-size:100px}.orbit-point{left:10px;top:70px}.pair-copy{margin-top:35px}.pair-copy h1{font-size:25px;line-height:1.55;margin:10px 0 5px}.pair-copy>.eyebrow{font-size:7px;letter-spacing:1.3px}.pair-copy>p,.art-caption{display:none}.pair-form-wrap{padding:37px 26px 25px;flex:1}.pair-form{max-width:420px}.pair-form h2{font-size:26px}.pair-form-wrap footer{margin-top:30px}.pair-form>p{margin-bottom:25px}dialog{padding:24px}dialog h2{font-size:19px}.dialog-actions{gap:8px}.dialog-actions button{font-size:11px}}
6
- @media(prefers-reduced-motion:reduce){*{animation:none!important;scroll-behavior:auto!important;transition:none!important}}
7
- .session-settings-bar{display:flex;align-items:center;justify-content:space-between;gap:14px;min-height:34px;margin-bottom:8px}
8
- .model-settings-button{display:inline-flex;align-items:center;gap:7px;max-width:55%;min-width:0;min-height:32px;padding:5px 8px;border:1px solid #e0e7d8;border-radius:6px;background:#fff;color:#4e6247;text-align:left;font-size:10px}
9
- .model-settings-button:hover:not(:disabled){background:#f1f5eb}
10
- .model-settings-button span:last-child{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
11
- .model-settings-button svg{width:15px;height:15px}
12
- .model-settings-button:disabled{opacity:.68}
13
- .context-meter{display:flex;align-items:center;gap:7px;min-width:0;font-size:9px;color:#889781;white-space:nowrap}
14
- .context-meter strong{font-size:10px;font-weight:550;color:#5b704e}
15
- .context-track{height:5px;width:52px;flex:none;background:#e5ebdd;border-radius:3px;overflow:hidden}
16
- .context-track>span{display:block;height:100%;background:#6a9871;transition:width .2s}
17
- .context-meter.warning .context-track>span{background:#be9a53}
18
- .context-meter.critical .context-track>span{background:#c87562}
19
- #model-dialog{max-width:440px;border-radius:8px}
20
- #model-dialog h2{margin:0 0 8px}
21
- #model-dialog p{margin:0 0 16px}
22
- #model-dialog label{display:block;margin:17px 0 7px;font-size:11px;color:#566a4e}
23
- #model-dialog select{width:100%;min-height:41px;padding:8px 10px;border:1px solid #dce5d3;border-radius:6px;background:#fff;color:#344a37;font-size:12px}
24
- #model-dialog .model-note{margin:18px 0 0;color:#8e9a83;font-size:10px}
25
- #model-dialog .error{margin:8px 0}
26
- @media(max-width:760px){.session-settings-bar{gap:8px}.model-settings-button{max-width:52%;font-size:9px}.context-meter{gap:5px;font-size:8px}.context-meter strong{font-size:9px}.context-track{width:28px}}
27
- .mode-banner .mode-copy{flex:1;min-width:0;overflow-wrap:anywhere}
28
- .mode-banner #reconnect-desktop{flex:none;white-space:nowrap}
29
- @media(max-width:760px){.mode-banner{align-items:flex-start;flex-wrap:wrap}.mode-banner .mode-copy{flex-basis:calc(100% - 30px)}.mode-banner #reconnect-desktop{margin-left:auto}}
30
- .cloud-admin-link{margin-top:22px!important;text-align:center}
31
- .cloud-admin-link a{color:#658649;text-decoration:underline;text-underline-offset:3px}
32
- .cloud-auth-tabs{display:flex;gap:5px;padding:4px;margin-top:20px;background:#edf2e5;border-radius:9px}
33
- .cloud-auth-tabs button{flex:1;padding:10px;border:0;border-radius:6px;background:transparent;color:#7a8e6a;font:inherit;font-size:12px;cursor:pointer}
34
- .cloud-auth-tabs button.active{background:#fff;color:#214d37;box-shadow:0 1px 5px #294f1a12}
35
- .field-help{display:block;font-size:10px;line-height:1.8;color:#8b9881}
36
- #register-username-help{margin:-8px 0 15px}
37
- .optional{color:#a0ab93;font-size:10px}
38
- .registration-note{font-size:11px;line-height:1.8;color:#718563}
39
- .cloud-device-screen{display:block;min-height:100dvh}
40
- .cloud-device-header{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;max-width:900px;margin:0 auto}
41
- .cloud-device-header h1{margin:8px 0;font-size:23px;font-weight:600;color:#344638}
42
- .cloud-device-header p{margin:7px 0;color:#88937f;font-size:11px}
43
- .cloud-device-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px}
44
- .cloud-device-actions .primary{padding:9px 12px;min-height:35px;font-size:11px}
45
- .cloud-onboarding{display:grid;grid-template-columns:minmax(0,1fr);gap:20px;max-width:900px;margin:26px auto;padding:28px;border:1px solid #dce6d1;border-radius:12px;background:#fff}
46
- .cloud-onboarding h2{font-size:20px;font-weight:550;color:#344638;margin:12px 0}
47
- .cloud-onboarding p,.cloud-onboarding li{font-size:12px;line-height:1.9;color:#849377}
48
- .cloud-onboarding ol{padding-left:20px}
49
- .cloud-command-box{display:flex;align-items:stretch;gap:8px;max-width:720px}
50
- .cloud-install-command{flex:1;min-width:0;margin:0;padding:13px 15px;border:1px solid #e4eadd;border-radius:7px;background:#f7f9f4;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.9 Consolas,monospace;color:#405a39}
51
- .cloud-command-box #cloud-copy-install{align-self:center;flex:none;min-width:90px}
52
- .cloud-install-status{min-height:16px;margin:8px 0!important;color:#52734b!important}
53
- .cloud-release-note{font-size:10px!important;color:#a28755!important}
54
- .cloud-onboarding code{overflow-wrap:anywhere}
55
- .cloud-device-list{grid-template-columns:repeat(auto-fit,minmax(min(100%,250px),1fr));max-width:900px;margin:18px auto}
56
- .cloud-device-option{width:100%;text-align:left;cursor:pointer}
57
- .cloud-device-option:hover:not(:disabled){border-color:#b7c99f;background:#fbfcf8}
58
- .cloud-computer-card{border:1px solid #e1e7d6;border-radius:12px;background:#fff;overflow:hidden;min-width:0}
59
- .cloud-computer-card .device-card{border:0;border-radius:0}
60
- .cloud-computer-card footer{padding:0 20px 15px;display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:10px;color:#8b9881}
61
- .cloud-computer-card footer button{color:#ac6653}
62
- .connect-screen{min-height:100dvh}
63
- .connect-form{max-width:420px}
64
- .connect-form h2{margin-top:22px}
65
- .connect-auth-tabs{display:flex;gap:5px;padding:4px;margin-top:20px;background:#edf2e5;border-radius:9px}
66
- .connect-auth-tabs button{flex:1;padding:10px;border:0;border-radius:6px;background:transparent;color:#7a8e6a;font:inherit;font-size:12px;cursor:pointer}
67
- .connect-auth-tabs button.active{background:#fff;color:#214d37}
68
- .connect-form dl{display:grid;grid-template-columns:100px minmax(0,1fr);gap:12px;margin:24px 0;padding:17px;border:1px solid #e4eadd;border-radius:8px;background:#fff;font-size:12px}
69
- .connect-form dt{color:#8b9881}
70
- .connect-form dd{margin:0;color:#344638;font-weight:550;overflow-wrap:anywhere}
71
- #connect-home{display:block;margin-top:22px;text-align:center;color:#658649;font-size:11px;text-underline-offset:3px}
72
- @media(max-width:760px){.cloud-device-screen{padding:24px 16px}.cloud-device-header{align-items:flex-start;flex-direction:column}.cloud-device-header h1{font-size:19px}.cloud-device-actions{justify-content:flex-start}.cloud-onboarding{grid-template-columns:1fr;gap:12px;padding:20px;margin:20px auto}.cloud-command-box{align-items:stretch;flex-direction:column}.cloud-command-box #cloud-copy-install{align-self:flex-start}.cloud-device-list{grid-template-columns:1fr}.cloud-device-option{padding:18px}#cloud-register-form input{font-size:16px}}
73
- @media(max-width:760px){.top-actions>#cloud-device-switcher{display:inline-flex;max-width:104px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:7px 8px;font-size:9px}.top-actions>#cloud-device-switcher:not([hidden]){display:inline-flex}}
@@ -1 +0,0 @@
1
- {"name":"Orbit · 会话随行","short_name":"Orbit","start_url":"/","display":"standalone","background_color":"#f7f8f5","theme_color":"#153c37","icons":[{"src":"/icon.svg","sizes":"any","type":"image/svg+xml","purpose":"any"}]}
package/public/style.css DELETED
@@ -1,70 +0,0 @@
1
- :root{font-family:Inter,"Segoe UI","Microsoft YaHei",sans-serif;color:#293a30;background:#f7f8f4;font-size:14px;--green:#173e36;--line:#e4e8dd;--lime:#d8edb2}*{box-sizing:border-box}body{margin:0}button,input,textarea{font:inherit}button{cursor:pointer;border:0;background:transparent;color:inherit}button:disabled{cursor:not-allowed;opacity:.5}a{color:inherit;text-decoration:none}[hidden]{display:none!important}svg{width:18px;height:18px;flex:none;stroke-width:1.7;vertical-align:middle}button span[data-icon]{display:inline-flex}button,a,input,textarea{outline-offset:4px}.workspace{display:flex;height:100dvh;overflow:hidden}.brand{display:flex;align-items:center;gap:9px;font-size:32px;font-weight:650;letter-spacing:-1.5px}.brand img{width:34px;height:34px;border-radius:11px}.brand-dot{color:#c6e89e}.brand small{font-size:8px;letter-spacing:1px;color:#a9beab;border:1px solid #69817461;padding:4px 6px;border-radius:5px;margin-left:5px}.light{color:#fff}.sidebar{width:228px;flex:none;background:#16372f;color:#b7c5b8;display:flex;flex-direction:column;padding:28px 17px 16px;position:relative;z-index:20}.sidebar>.brand{padding:0 10px}.workspace-label{display:flex;gap:10px;align-items:center;border:1px solid #ffffff17;padding:12px 10px;border-radius:10px;margin:30px 0}.workspace-label>div{flex:1}.workspace-label strong{font-size:12px;color:#eff4e9;font-weight:500}.workspace-label small{display:block;font-size:10px;color:#a1b3a1;margin-top:5px}.workspace-label>span:last-child svg{width:13px}.workspace-avatar{width:31px;height:32px;background:#355347;border-radius:8px;color:#d7e9c2;display:grid;place-items:center}.nav-label{font-size:10px;letter-spacing:1.3px;color:#91a591;margin:0 12px 13px;display:block}.nav-item{width:100%;display:flex;align-items:center;gap:11px;border-radius:8px;padding:12px 13px;color:#adc0af;text-align:left;font-size:12px;margin-bottom:5px}.nav-item:hover{background:#ffffff08}.nav-item.active{background:#d7eab4;color:#183e30;font-weight:600}.nav-count{margin-left:auto;font-size:10px;background:#173f3120;min-width:22px;text-align:center;border-radius:5px;padding:2px}.tiny-dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:#8bb976;flex:none}.nav-item .tiny-dot{margin-left:auto}.project-heading{display:flex;align-items:center;justify-content:space-between;margin-top:33px;padding-right:15px}.project-heading .nav-label{margin-bottom:0}.project-heading svg{width:14px;color:#9fb29e}.projects{overflow:auto;margin-top:13px;scrollbar-width:thin}.project-item{display:flex;align-items:center;width:100%;gap:10px;padding:11px 13px;font-size:11px;text-align:left;color:#b7c7b6}.project-item:hover,.project-item.active{background:#ffffff09;border-radius:7px;color:#f0f5e9}.project-dot{width:7px;height:7px;border:1.5px solid #a7c18b;border-radius:2px;flex:none}.project-item:nth-child(3n+2) .project-dot{border-color:#d4b78e}.project-item:nth-child(3n) .project-dot{border-color:#94b6bd}.project-item>span:nth-child(2){overflow:hidden;white-space:nowrap;text-overflow:ellipsis;flex:1}.project-item small{font-size:10px;color:#8fa48b}.sidebar-bottom{margin-top:auto;padding-top:24px}.device-mini{display:flex;gap:10px;border-top:1px solid #ffffff14;padding:18px 7px 0;align-items:center}.device-icon{display:grid;place-items:center;width:33px;height:34px;background:#2b4b3d;border-radius:8px;color:#c6d8b7}.device-mini>div{min-width:0;flex:1}.device-mini strong{display:block;font-size:10px;color:#d8e4d1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.device-mini small{display:flex;align-items:center;gap:5px;font-size:9px;margin-top:7px;color:#97af93}.device-mini>span:last-child svg{width:12px}.sidebar-bottom>p{font-size:9px;color:#8fa58a;margin:20px 10px 1px;display:flex;gap:6px;align-items:center}.sidebar-bottom>p svg{width:12px;height:12px}.main{flex:1;min-width:0;display:flex;flex-direction:column}.topbar{height:73px;flex:none;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 30px;background:#fcfdf9}.breadcrumbs{display:flex;align-items:center;gap:13px;font-size:11px;color:#899184}.breadcrumbs strong{color:#46553f;font-weight:500}.slash{color:#c7cdc0}.top-actions{display:flex;align-items:center;gap:18px}.live-pill{font-size:10px;display:inline-flex;align-items:center;gap:7px;color:#69805b}.live-pill.offline{color:#a37743}.live-pill.offline i{background:#ca985b}.button{border:1px solid #dfe5d8;background:#fff;border-radius:7px;padding:10px 15px;display:inline-flex;align-items:center;justify-content:center;gap:8px;font-weight:500}.button:hover{background:#f4f7ed}.button.small{font-size:11px;padding:8px 11px}.button.small svg{width:14px;height:14px}.avatar-button{background:#e9eddf;width:29px;height:29px;border-radius:50%;font-size:11px;color:#506447}.icon-button{width:31px;height:31px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:7px;color:#8b9785}.icon-button:hover{background:#edf0e7}.sessions-view{flex:1;display:flex;min-height:0}.thread-pane{width:335px;flex:none;border-right:1px solid var(--line);background:#fcfdfa;display:flex;flex-direction:column;padding:26px 18px 0}.pane-heading{padding:0 5px}.eyebrow{font-size:9px;letter-spacing:1.7px;color:#8c9983;font-weight:600}.pane-heading>div{display:flex;align-items:center;gap:9px;margin-top:10px}.pane-heading h1{font-size:24px;letter-spacing:-.7px;font-weight:600;margin:0;line-height:1.5}.count-badge{border:1px solid #e2e7d8;border-radius:6px;color:#7d8c70;font-size:10px;padding:2px 6px;background:#f6f8f0}.pane-heading .icon-button{margin-left:auto}.pane-heading p{font-size:11px;color:#919b87;margin:7px 0 21px}.search-box{display:flex;align-items:center;gap:8px;border:1px solid #e1e6d8;background:#f4f6ef;border-radius:8px;padding:9px 10px;color:#97a18d}.search-box svg{width:14px;height:14px}.search-box input{border:0;background:transparent;min-width:0;flex:1;font-size:11px;color:#405139;outline:0}.search-box input::placeholder{color:#99a18f}kbd{font-size:9px;font-family:inherit;color:#a7ae9e;white-space:nowrap}.filters{display:flex;gap:5px;margin:16px 0 11px;padding-bottom:12px;border-bottom:1px solid var(--line)}.filters button{border-radius:6px;padding:6px 12px;font-size:10px;color:#8f9984}.filters button.active{background:#e9efdf;color:#3f6136;font-weight:600}.thread-list{overflow:auto;flex:1;scrollbar-width:thin;padding-bottom:15px}.thread-item{display:block;text-align:left;width:100%;padding:15px 11px;border-radius:9px;margin:2px 0 5px;border:1px solid transparent;transition:background .16s,border-color .16s}.thread-item:hover{background:#f4f6ee}.thread-item.selected{background:#eef3e5;border-color:#dce7cd}.thread-meta{display:flex;align-items:center;gap:7px;font-size:9px;color:#99a18f;margin-bottom:9px}.project-initial{width:18px;height:18px;border:1px solid #e1e7d7;background:#f1f4eb;display:grid;place-items:center;border-radius:5px;font-size:9px;color:#70815e;flex:none}.project-label{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px}.thread-meta time{margin-left:auto;font-size:9px;white-space:nowrap}.thread-item h3{font-size:12px;font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0 0 8px;color:#3b4a33}.thread-item p{font-size:10px;color:#929c87;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0;line-height:1.8}.thread-badges{display:flex;gap:5px;margin-top:10px;flex-wrap:wrap}.tag{display:inline-flex;align-items:center;gap:4px;background:#eff2e8;padding:3px 6px;color:#82916f;border-radius:4px;font-size:8px;line-height:1.5}.tag.busy{background:#f7f1e5;color:#ad8d4b}.tag.busy i{background:#cba85b;width:4px;height:4px}.tag.archived{background:#edeef0;color:#969aa0}.list-footer{display:flex;align-items:center;gap:7px;font-size:9px;color:#99a18e;border-top:1px solid var(--line);padding:13px 3px 14px;flex:none}.list-footer svg{width:12px;height:12px}.list-footer i{margin-left:auto;width:5px;height:5px}.conversation{flex:1;min-width:0;min-height:0;display:flex;background:#f8f9f5;position:relative}.welcome{padding:43px 35px 28px;overflow:auto;width:100%}.welcome-heading{display:flex;justify-content:space-between;align-items:flex-start;gap:15px}.welcome-heading h2{font-size:23px;letter-spacing:-.6px;font-weight:550;margin:0 0 9px}.welcome-heading p{font-size:11px;color:#929b87;margin:0}.date-label{font-size:10px;color:#99a18d;white-space:nowrap;margin-top:6px}.hero{background:#1c4134;border-radius:14px;position:relative;overflow:hidden;padding:33px 29px 30px;color:#f1f6e8;min-height:213px;margin-top:28px}.hero-grid{position:absolute;inset:0;opacity:.12;background-image:linear-gradient(#d6e5bb38 1px,transparent 1px),linear-gradient(90deg,#d6e5bb38 1px,transparent 1px);background-size:32px 32px;mask-image:linear-gradient(90deg,transparent,#000)}.hero .eyebrow{color:#b9ce97;font-size:8px}.hero h2{font-size:28px;font-weight:500;line-height:1.6;letter-spacing:-.7px;position:relative;margin:13px 0;max-width:330px}.hero p{font-size:10px;line-height:1.9;color:#adbea0;position:relative;max-width:350px}.hero-orbit{position:absolute;width:210px;height:210px;border:1px solid #bcd68b3d;border-radius:50%;right:-35px;top:5px;transform:rotate(-32deg) scaleY(.48)}.hero-orbit:before,.hero-orbit:after{content:"";position:absolute;inset:20px -25px;border:1px solid #bcd68b42;border-radius:50%;transform:rotate(65deg)}.hero-orbit:after{inset:45px -30px;transform:rotate(-70deg)}.hero-orbit>i{position:absolute;width:11px;height:11px;background:#d6ecb3;border-radius:50%;top:38px;left:17px;box-shadow:0 0 30px #d4ebac66}.hero-status{display:inline-flex;align-items:center;gap:6px;font-size:8px;border:1px solid #abc18d38;border-radius:20px;padding:5px 9px;position:relative;color:#c6dba7}.stats-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:19px 0 30px}.stat-card{background:#fcfdf9;border:1px solid #e6eadd;border-radius:10px;padding:17px 16px}.stat-card>span{font-size:9px;color:#8a9780;display:flex;align-items:center;gap:7px}.stat-card svg{width:14px;height:14px}.stat-card strong{display:block;font-size:27px;line-height:1.5;font-weight:500;margin-top:5px;color:#3b5030;font-variant-numeric:tabular-nums}.stat-card small{font-size:8px;color:#a4ac9a}.section-heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.section-heading h3{font-size:12px;font-weight:550;margin:0}.section-heading span{font-size:9px;color:#95a088}.recent-card{border:1px solid #e4e8db;border-radius:9px;display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:14px 15px;background:#fcfdf9;margin-bottom:9px}.recent-card:hover{border-color:#c4d5ad;background:#f5f8ed}.recent-icon{width:31px;height:32px;background:#ecf1e2;border:1px solid #e4eadb;border-radius:8px;display:grid;place-items:center;color:#7d9465}.recent-card>div{flex:1;min-width:0}.recent-card strong{font-size:11px;font-weight:500;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.recent-card p{font-size:9px;color:#9ba38e;margin:6px 0 0}.recent-card>span:last-child{font-size:15px;color:#94a17e}.honesty-note{margin-top:26px;display:flex;gap:9px;align-items:flex-start;color:#9a987e;font-size:9px;line-height:1.9;padding-top:16px;border-top:1px dashed #e3e6d8}.honesty-note svg{width:13px;height:13px;margin-top:2px}.chat{display:flex;flex-direction:column;width:100%;min-height:0}.chat-header{display:flex;gap:9px;align-items:center;padding:18px 22px;background:#fcfdf9;border-bottom:1px solid var(--line);min-height:78px}.chat-title{flex:1;min-width:0}.chat-title h2{font-size:14px;font-weight:550;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0 0 7px}.chat-title p{font-size:9px;color:#909c84;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mode-banner{margin:15px 22px 0;border-radius:7px;font-size:10px;line-height:1.7;color:#8c8c6c;background:#eeefe4;padding:9px 12px;display:flex;align-items:center;gap:8px;flex:none}.mode-banner svg{width:13px;height:13px}.mode-banner.controlled{background:#e9f1df;color:#5e7947}.messages{overflow:auto;flex:1;padding:24px 27px 12px;scrollbar-width:thin}.message{margin-bottom:25px;animation:enter .2s ease-out}.message-head{display:flex;align-items:center;gap:8px;margin-bottom:10px;font-size:10px;color:#7f8f70}.message-avatar{height:22px;width:22px;display:grid;place-items:center;background:#e9edde;border-radius:6px;font-size:9px;color:#607751}.message.assistant .message-avatar{background:#1f4534;color:#d7e7ba}.message-head strong{font-size:10px;font-weight:550}.message-head time{font-size:8px;color:#a8ad9f;margin-left:auto}.message-body{font-size:12px;line-height:1.95;overflow-wrap:anywhere;color:#4c5b41;padding-left:30px}.message.user .message-body{background:#eef2e7;border:1px solid #e5eadb;padding:12px 15px;border-radius:2px 10px 10px 10px;margin-left:30px}.message-body>:first-child{margin-top:0}.message-body>:last-child{margin-bottom:0}.message-body pre{background:#1f2e26;color:#d8e5cd;border-radius:8px;padding:14px;overflow:auto;font-size:10px;line-height:1.8;max-width:100%}.message-body code{font-family:Consolas,monospace;font-size:.92em;background:#e9eddf;padding:2px 4px;border-radius:3px}.message-body pre code{background:none;padding:0}.message-body a{color:#658649;text-decoration:underline;text-underline-offset:3px}.message-body h1,.message-body h2,.message-body h3{font-size:14px;line-height:1.7}.message-body blockquote{margin:12px 0;padding:2px 12px;border-left:3px solid #cbd8b7;color:#8a957f}.message-body table{border-collapse:collapse;display:block;overflow:auto}.message-body td,.message-body th{border:1px solid #dee5d3;padding:6px 8px}.activity-message{margin:7px 0 12px 30px;border:1px solid #e1e7d8;border-radius:6px;font-size:10px;color:#8e9a82;padding:8px 10px}.activity-message summary{cursor:pointer;overflow-wrap:anywhere;line-height:1.6}.activity-message pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:10px}.load-more{display:block;margin:0 auto 24px;color:#7e9368;font-size:10px;border:1px solid #e1e7d7;border-radius:15px;padding:5px 13px}.composer-wrap{padding:10px 22px 15px;background:linear-gradient(transparent,#f8f9f5 15%);flex:none}.composer{background:#fff;border:1px solid #dde5d2;border-radius:11px;padding:12px 13px;box-shadow:0 4px 16px #3e542005}.composer:focus-within{border-color:#a3bc8a;box-shadow:0 0 0 3px #dae6cb44}.composer textarea{width:100%;background:transparent;resize:none;border:0;outline:0;color:#506045;font-size:12px;line-height:1.7;max-height:160px}.composer textarea::placeholder{color:#abb19f}.composer-toolbar{display:flex;align-items:center;gap:9px;margin-top:9px;min-height:26px}.composer-toolbar>span{display:flex;align-items:center;gap:5px;font-size:9px;color:#9da78d;flex:1}.composer-toolbar>span svg{width:12px;height:12px}.send-button{width:29px;height:29px;background:#284b36;color:#e9f3d4;border-radius:7px;display:grid;place-items:center}.send-button svg{width:16px;height:16px}.composer-note{text-align:center;font-size:8px;color:#a5ac98;margin:9px 0 0;line-height:1.7}.chat-details{padding:13px 22px;background:#f0f3e9;border-bottom:1px solid var(--line);font-size:10px;line-height:2;overflow-wrap:anywhere}.chat-details dt{color:#9aa58c}.chat-details dd{margin:0 0 5px;color:#667958;font-family:Consolas,monospace}.empty{padding:45px 15px;text-align:center;color:#9ca48f;font-size:11px;line-height:2}
2
- @media(max-width:760px){.filters{gap:3px}.filters button{flex:1;min-width:0;padding:7px 4px}}
3
- .filters button:focus-visible,.message-copy:focus-visible,.copy-code:focus-visible{outline:2px solid #8aa973;outline-offset:2px}
4
- .tag.pinned{background:#f2efe3;color:#8a7345}
5
- .message-head .message-copy{width:25px;height:25px;margin-left:0;opacity:0;transition:opacity .12s,color .12s}
6
- .message:hover .message-copy,.message:focus-within .message-copy,.message-copy:focus-visible{opacity:1}
7
- .message-copy svg,.copy-code svg{width:14px;height:14px}
8
- .message-body pre{position:relative;padding-top:38px}
9
- .copy-code{position:absolute;top:6px;right:6px;width:28px;height:26px;display:grid;place-items:center;border:1px solid #ffffff2b;border-radius:5px;color:#c9d8be;background:#ffffff0d;opacity:0;transition:opacity .12s,background .12s}
10
- .message-body pre:hover .copy-code,.copy-code:focus-visible{opacity:1}
11
- .copy-code:hover{background:#ffffff1c}
12
- .message-attachments{display:flex;flex-wrap:wrap;gap:7px;margin:9px 0 0 30px}
13
- .attachment-label{display:inline-flex;align-items:center;gap:6px;border:1px solid #dfe6d5;border-radius:5px;background:#fbfcf8;padding:5px 8px;color:#738264;font-size:10px}
14
- .attachment-label svg{width:14px;height:14px}
15
- .activity-message{padding:0;margin:8px 0 13px 30px;overflow:hidden;color:#58654e}
16
- .activity-message[open]{background:#fbfcf8}
17
- .activity-message summary{min-height:40px;display:flex;align-items:center;gap:8px;padding:8px 10px;list-style:none;color:#77856a}
18
- .activity-message summary::-webkit-details-marker{display:none}
19
- .activity-message summary::before{content:"›";font-size:17px;line-height:1;transform:rotate(0deg);transition:transform .12s}
20
- .activity-message[open] summary::before{transform:rotate(90deg)}
21
- .activity-symbol{display:inline-flex;color:#7d8e6d}
22
- .activity-symbol svg{width:15px;height:15px}
23
- .activity-label{flex:none;font-weight:600;color:#657552}
24
- .activity-summary{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
25
- .activity-meta{flex:none;color:#9a9a82;font-size:9px;text-align:right}
26
- .activity-content{padding:0 12px 12px 34px}
27
- .activity-command,.activity-output,.activity-diff{max-width:100%;margin:7px 0;background:#202f28;color:#dbe6d1;border-radius:5px;padding:12px;overflow:auto;font:10px/1.65 Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}
28
- .activity-command{color:#d6e6c3}
29
- .activity-command code,.activity-output code,.activity-diff code{background:none;padding:0;font:inherit;color:inherit}
30
- .activity-section-label{margin:10px 0 3px;color:#8b957b;font-size:9px}
31
- .activity-path{overflow-wrap:anywhere;color:#929b89;font:9px/1.6 Consolas,monospace}
32
- .activity-file{margin-top:10px}
33
- .activity-file-head{display:flex;align-items:center;justify-content:space-between;gap:10px;overflow-wrap:anywhere;color:#607351;font-size:10px}
34
- .activity-file-head small{flex:none;color:#9b9d88;font-size:9px}
35
- .activity-diff{margin-top:6px;color:#cfd9c7}
36
- .activity-diff code span{display:block;min-height:1em}
37
- .activity-diff .diff-added{color:#a8d89a;background:#47614845}
38
- .activity-diff .diff-removed{color:#edb0a7;background:#70474445}
39
- .activity-empty{margin:8px 0;color:#9ba38e;font-size:10px}
40
- .turn-mode-label{font-size:10px;color:#8b9785}
41
- .turn-mode-select{max-width:95px;border:1px solid #e2e7dc;background:#f7f8f4;border-radius:6px;padding:5px 7px;color:#506045;font-size:10px}
42
- .turn-mode-select:disabled{opacity:.55}
43
- .composer-toolbar .turn-mode-label,.composer-toolbar .turn-mode-select,.composer-toolbar #attach-files{flex:none}
44
- .composer-toolbar #composer-mode{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
45
- .composer-attachments{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
46
- .composer-attachment{display:inline-flex;align-items:center;gap:6px;max-width:100%;padding:5px 7px;border:1px solid #dfe6d5;border-radius:6px;background:#f7f9f2;color:#68795a;font-size:9px}
47
- .composer-attachment>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
48
- .composer-attachment svg{width:13px;height:13px}
49
- .composer-attachment button{width:20px;height:20px;border-radius:4px;color:#8b9785;font-size:16px;line-height:1}
50
- .composer-attachment button:hover{background:#e8eddf}
51
- .composer-attachment button:disabled{opacity:.5}
52
- .file-browser-dialog{width:min(560px,calc(100vw - 28px));max-width:560px;max-height:min(78dvh,760px);padding:0;border:1px solid #dfe5d7;border-radius:13px;background:#fbfcf8;color:#354536;box-shadow:0 20px 80px #17251a40}
53
- .file-browser-dialog::backdrop{background:#1b2c22a8;backdrop-filter:blur(3px)}
54
- .file-browser-panel{display:flex;flex-direction:column;max-height:min(78dvh,760px);padding:20px}
55
- .file-browser-panel>header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;border-bottom:1px solid var(--line);padding-bottom:12px}
56
- .file-browser-panel h2{margin:0;font-size:17px;font-weight:550}
57
- .file-browser-panel header p{margin:5px 0 0;max-width:360px;overflow-wrap:anywhere;color:#7d8a72;font-size:10px}
58
- .file-browser-notice{font-size:10px;line-height:1.7;color:#899380}
59
- .file-browser-list{overflow:auto;min-height:100px;max-height:52dvh;border:1px solid var(--line);border-radius:8px;background:#fff}
60
- .file-browser-entry{width:100%;min-height:43px;display:flex;align-items:center;gap:10px;padding:8px 12px;border-bottom:1px solid #eef0e9;text-align:left}
61
- .file-browser-entry:last-child{border-bottom:0}
62
- .file-browser-entry:hover{background:#f3f6ed}
63
- .file-browser-entry>span{color:#80916d;flex:none}
64
- .file-browser-entry svg{width:15px;height:15px}
65
- .file-browser-entry strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:450}
66
- .file-browser-entry small{margin-left:auto;flex:none;color:#929d88;font-size:9px}
67
- .file-browser-parent{background:#f8faf4;color:#647653}
68
- .file-browser-empty{padding:16px;color:#909b84;font-size:10px}
69
- @media(max-width:760px){.composer-toolbar{flex-wrap:wrap}.composer-toolbar #composer-mode{flex:1 1 65px}.turn-mode-select{max-width:92px}.composer-attachment{max-width:100%}.file-browser-panel{padding:16px}.session-settings-bar{gap:5px}.session-settings-bar #browse-files{padding:6px 8px;font-size:9px}}
70
- @media(max-width:760px){.message-head .message-copy,.copy-code{opacity:1}.activity-message{margin-left:0}.activity-content{padding-left:14px}.activity-meta{max-width:40%;white-space:normal}.message-attachments{margin-left:0}.message-body pre{padding-top:38px}}