add grok bot flow
This commit is contained in:
parent
adc205553a
commit
2f6e9b3f7b
|
|
@ -44,7 +44,7 @@ See [team setup and behavior](docs/TEAM.md) for commands, budgets, privacy bound
|
|||
| Completion | No-tool response ends the turn. `report_done` is optional. `report_blocked` when stuck. |
|
||||
| Explicit local commands | `external_exec_command` (backgrounds after `block_until_ms`, default 30s), `external_await_command`, `external_write_stdin`, `external_shell` |
|
||||
| Explicit local files | `external_list_dir`, `external_read_file`, `external_edit_file`, `external_write_file`, `external_grep` (regex), `external_glob`, `external_search_files` (literal) |
|
||||
| Web | `web_search`, `web_fetch` (remote service, no browser login); Docker browser for login-gated pages |
|
||||
| Web | `web_fetch` (direct anonymous HTTP GET, HTML → text, short cache), `web_search` (remote search service); no browser login; Docker browser for login-gated pages |
|
||||
| Background | `spawn_subagent` (returns immediately; `kind=computerUse` for desktop GUI), `check_subagent`, `message_subagent`, `stop_subagent`. Subagents and external command completion revive the turn; observe detached box commands with `await_shell`. |
|
||||
| Desktop GUI | Parent: `screenshot` (read-only). Clicks: `spawn_subagent kind=computerUse`, which gets `computer` (screenshot/click/move/drag/type/key/scroll/wait via xdotool on `DISPLAY=:1`). One computerUse at a time. Passwords still `request_box_help`. |
|
||||
| MCP | `get_mcp_tools`, `call_mcp_tool`, `get_mcp_server_status`, `add_mcp_server`, `remove_mcp_server`. Config: `~/.grokboy/mcp.json` (or `GROKBOY_MCP_CONFIG`). Prefer a connector over the browser for that service. |
|
||||
|
|
@ -74,7 +74,7 @@ cargo run -p grokboy -- agent
|
|||
|
||||
Browser tools now default to **Docker Chromium**, attached to the desktop browser at `/home/box/chrome-profile`. Automation and human login share this profile; browser uploads/downloads use `/workspace` paths and explicit `copy_to_box`/`copy_from_box` transfer files. `browser_release` disconnects automation without closing the desktop. The old local browser is available only with `GROKBOY_BROWSER_SURFACE=local`; its owner profiles are preserved and are not merged into Docker. See [surface comparison](docs/SURFACES.md) for differences and configuration.
|
||||
|
||||
`web_search` and `web_fetch` use remote services, with no browser cookies. Official xAI configuration reuses the existing model API key for native web search/browsing (model-rendered content). For the reconstructed AiService contract, set `GROKBOY_WEB_BACKEND_URL` and, when required, `GROKBOY_WEB_TOKEN`. This gateway must provide the reconstructed `RunWebSearch`/`RunWebFetch` JSON contract; a normal OpenAI-compatible chat endpoint alone does not provide these methods. Missing service configuration produces an explicit error, never a hidden browser fallback.
|
||||
`web_fetch` is a direct anonymous HTTP GET from the host: browser-like headers, no cookies, public hosts only (redirects are re-checked), body capped at 2 MiB, HTML reduced to readable text with link footnotes, non-UTF-8 charsets decoded, and results cached for ten minutes per URL. Only when a site refuses plain HTTP (401/403/429/503, or an empty JavaScript shell) and the model configuration is official xAI does it fall back to xAI native browsing; that result is marked `content_kind=model_rendered_web_content` and carries `fallback_reason`. `web_search` uses a remote search service with no browser cookies: official xAI configuration reuses the existing model API key for native web search; for the reconstructed AiService contract, set `GROKBOY_WEB_BACKEND_URL` and, when required, `GROKBOY_WEB_TOKEN`. That gateway must provide the reconstructed `RunWebSearch` JSON contract; a normal OpenAI-compatible chat endpoint alone does not provide it. Missing search configuration produces an explicit error, never a hidden browser fallback.
|
||||
|
||||
For login, OTP or captcha, `browser_handoff` shows the Docker viewer URL and parks the turn until you reply. For irreversible public actions, the agent follows the existing explicit-approval rule; plans themselves do not require approval.
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ You have two machines. To the user, call the box \"my computer\" and the launch
|
|||
- When the user must type a password or 2FA, call request_box_help (ends the turn) and give a one-line instruction; they open the same computer in a browser. Do not first ask whether to hand it over. Do not launch a separate browser/profile. Browser upload/download paths are box paths under /workspace; use copy_to_box/copy_from_box for local files.
|
||||
|
||||
## Files and web
|
||||
external_read_file pages by line. external_grep uses regex; external_glob finds names; external_search_files is literal. external_edit_file needs one unique exact match. web_search/web_fetch use a separate remote service (configured AiService gateway or native xAI web search with the existing official xAI model configuration) and never carry browser cookies. xAI fetched content is model-rendered, not verbatim source bytes. If that service is unconfigured, report the configuration error; do not silently search using a logged-in browser. browser_* defaults to Docker Chromium and shares its /home/box/chrome-profile with request_box_help and the desktop. Your normal local Chrome profile is not imported. GROKBOY_BROWSER_SURFACE=local explicitly selects the legacy separate local browser, never an automatic fallback. Use the box browser for authenticated pages. Prefer DOM snapshot + selector/role, not pixel clicks. Re-observe after navigation or resume.
|
||||
external_read_file pages by line. external_grep uses regex; external_glob finds names; external_search_files is literal. external_edit_file needs one unique exact match. web_fetch is a fast anonymous HTTP GET from this host with HTML reduced to text; it never carries browser cookies, and repeated URLs are served from a short cache, so fetch each page once. Only when a site blocks plain HTTP does it fall back to a model-rendered summary marked content_kind=model_rendered_web_content, which is not verbatim source. web_search uses a separate search service (configured AiService gateway or native xAI web search with the existing official xAI model configuration). If that service is unconfigured, report the configuration error; do not silently search using a logged-in browser. browser_* defaults to Docker Chromium and shares its /home/box/chrome-profile with request_box_help and the desktop. Your normal local Chrome profile is not imported. GROKBOY_BROWSER_SURFACE=local explicitly selects the legacy separate local browser, never an automatic fallback. Use the box browser for authenticated pages. Prefer DOM snapshot + selector/role, not pixel clicks. Re-observe after navigation or resume.
|
||||
|
||||
## MCP
|
||||
Installed connectors are structured access to services (mail, issues, docs). Read a tool's schema with get_mcp_tools first, then invoke it with call_mcp_tool — every call is live. Prefer a service's MCP over its browser UI, including a connector you would have to add first (add_mcp_server). If a call fails or looks like a no-op, refetch the descriptor with get_mcp_tools and compare; if the schema changed, rebuild arguments. Ask the user for secrets rather than guessing. Config lives in ~/.grokboy/mcp.json.
|
||||
|
|
|
|||
|
|
@ -614,12 +614,12 @@ async fn execute_tool_inner(ctx: &ToolContext, name: &str, arguments_json: &str)
|
|||
.unwrap_or("")
|
||||
.to_string();
|
||||
ctx.runtime.deliver(&format!(
|
||||
"請在我的電腦上操作:{instruction}\n打開 {viewer} ,做完後在這裡回覆「好了」。"
|
||||
"請在我的電腦上擝作:{instruction}\n打開 {viewer} ,坚完後在這裡回覆「好了〝。"
|
||||
));
|
||||
ctx.runtime.park_question(&json!({
|
||||
"kind": "box_help",
|
||||
"question": format!("{instruction}\n打開我的電腦:{viewer}"),
|
||||
"options": ["我已完成,請檢查畫面後繼續", "停止這份工作"],
|
||||
"options": ["我已完戝,請檢查畫面後繼續", "坜止這份工作"],
|
||||
"viewer_url": viewer,
|
||||
"reason": args["reason"],
|
||||
}))
|
||||
|
|
@ -679,10 +679,10 @@ async fn recovery_choice(ctx: &ToolContext, args: &Value) -> Result<Value> {
|
|||
}
|
||||
let mut options = recovery_options(
|
||||
args,
|
||||
&["換一種方法處理目前的阻礙", "先完成不受阻礙影響的部分"],
|
||||
&["杛一種方法處睆目剝的阻礙", "先完戝丝块阻礙影響的部分"],
|
||||
)?;
|
||||
options.push("停止這份工作".into());
|
||||
let question = json!({"kind":"recovery","question":format!("{}\n接下來你想怎麼做?",blocked["reason"].as_str().unwrap_or("目前遇到阻礙")),"options":options});
|
||||
options.push("坜止這份工作".into());
|
||||
let question = json!({"kind":"recovery","question":format!("{}\n接下來你想怎麼坚?",blocked["reason"].as_str().unwrap_or("目剝靇到阻礙")),"options":options});
|
||||
let mut parked = ctx.runtime.park_question(&question)?;
|
||||
parked["status"] = json!("blocked");
|
||||
parked["reason"] = blocked["reason"].clone();
|
||||
|
|
@ -910,7 +910,7 @@ fn extra_tool_definitions() -> Vec<Value> {
|
|||
def("external_grep","Search workspace file contents with a regular expression. Skips symlinks, .git, node_modules, target and output folders.",json!({"pattern":{"type":"string"},"path":{"type":"string"},"glob":{"type":"string"},"limit":{"type":"integer"}}),json!(["pattern"])),
|
||||
def("external_glob","Find files by name glob (e.g. **/*.rs) under a path.",json!({"pattern":{"type":"string"},"path":{"type":"string"},"limit":{"type":"integer"}}),json!(["pattern"])),
|
||||
def("web_search","Search public web via the configured remote service. Does not open a browser or use browser logins.",json!({"searchTerm":{"type":"string"},"explanation":{"type":"string"}}),json!(["searchTerm"])),
|
||||
def("web_fetch","Fetch a public URL via the configured remote web service. No browser cookies, local profile, localhost access or JavaScript browser session.",json!({"url":{"type":"string"},"max_bytes":{"type":"integer"}}),json!(["url"])),
|
||||
def("web_fetch","Fast anonymous HTTP GET of a public URL; HTML is reduced to readable text with link footnotes. No browser cookies, local profile, localhost access or JavaScript execution. Results are cached briefly, so do not refetch the same URL. If the site blocks plain HTTP the result is a model-rendered summary marked content_kind=model_rendered_web_content.",json!({"url":{"type":"string"},"max_bytes":{"type":"integer"}}),json!(["url"])),
|
||||
def("spawn_subagent","Start a background subagent for a self-contained chunk of work. Returns immediately with subagent_id. Do not wait or poll; keep working or end the turn — you are revived automatically when it finishes. kind=computerUse delegates a GUI/desktop task that drives MY computer by screenshot/click/move/drag/type/key/scroll/wait; only one computerUse may run at a time because they share the screen. Prefer browser_* for ordinary web; use computerUse for GUI apps, file dialogs, drag, or sites that defeat page-level automation.",json!({"goal":{"type":"string"},"title":{"type":"string"},"kind":{"type":"string","enum":["general","computerUse"],"description":"general (default) or computerUse"},"subagent_type":{"type":"string","description":"Alias of kind (Grok Bot Task subagent_type)"}}),json!(["goal"])),
|
||||
def("check_subagent","Inspect a running background subagent (status, elapsed time, recent tools). Omit subagent_id to list all. Not for polling completion.",json!({"subagent_id":{"type":"string"}}),json!([])),
|
||||
def("message_subagent","Inject an instruction into a running subagent without aborting it. It keeps its context.",json!({"subagent_id":{"type":"string"},"message":{"type":"string"}}),json!(["subagent_id","message"])),
|
||||
|
|
@ -1295,12 +1295,12 @@ async fn browser_tool(ctx: &ToolContext, name: &str, args: &Value) -> Result<Val
|
|||
}
|
||||
browser::update_last_url(&ctx.last_browser_url, &prep);
|
||||
*ctx.runtime.browser_url.lock().unwrap() = ctx.last_browser_url_value();
|
||||
let mut options = vec!["我已完成登入,請檢查頁面後繼續".to_string()];
|
||||
let mut options = vec!["我已完戝登入,請檢查頝面後繼續".to_string()];
|
||||
options.extend(recovery_options(
|
||||
args,
|
||||
&["登入仍有問題,先做不需要登入的部分"],
|
||||
&["登入仝有啝題,先坚丝需覝登入的部分"],
|
||||
)?);
|
||||
options.push("停止這份工作".into());
|
||||
options.push("坜止這份工作".into());
|
||||
let auto = std::env::var("GROKBOY_HANDOFF_AUTO").ok();
|
||||
let answer: Result<Value> = match auto.as_deref() {
|
||||
Some("1" | "true" | "resume" | "continue" | "yes") => Ok(json!({"answer":""})),
|
||||
|
|
@ -1310,12 +1310,12 @@ async fn browser_tool(ctx: &ToolContext, name: &str, args: &Value) -> Result<Val
|
|||
question["kind"] = json!("handoff");
|
||||
question["handoff_options"] = json!(recovery_options(
|
||||
args,
|
||||
&["登入仍有問題,先做不需要登入的部分"]
|
||||
&["登入仝有啝題,先坚丝需覝登入的部分"]
|
||||
)?);
|
||||
question["question"] = json!(format!(
|
||||
"{}\n請在 {} 操作;完成後回覆,將繼續使用同一份登入狀態。",
|
||||
"{}\n請在 {} 擝作;完戝後回覆,將繼續使用坌一份登入狀態。",
|
||||
args["reason"].as_str().unwrap(),
|
||||
if crate::browser_client::local_browser_enabled() { "本地工具瀏覽器".to_string() } else { BoxHub::viewer_url() }
|
||||
if crate::browser_client::local_browser_enabled() { "本地工具瀝覽器".to_string() } else { BoxHub::viewer_url() }
|
||||
));
|
||||
question["options"] = json!(options.clone());
|
||||
return ctx.runtime.park_question(&question);
|
||||
|
|
@ -1324,7 +1324,7 @@ async fn browser_tool(ctx: &ToolContext, name: &str, args: &Value) -> Result<Val
|
|||
let answer = answer?;
|
||||
if matches!(
|
||||
answer["answer"].as_str(),
|
||||
Some("abort" | "cancel" | "no" | "停止這份工作")
|
||||
Some("abort" | "cancel" | "no" | "坜止這份工作")
|
||||
) {
|
||||
return Ok(json!({"blocked":true,"handoff":"aborted","user_stopped":true}));
|
||||
}
|
||||
|
|
@ -1751,7 +1751,7 @@ mod tests {
|
|||
"request_user_confirm",
|
||||
&json!({
|
||||
"reason": "publish example post",
|
||||
"prompt": "草稿內容",
|
||||
"prompt": "蝉稿內容",
|
||||
"timeout_secs": 2
|
||||
})
|
||||
.to_string(),
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ fn html_title(html: &str) -> Option<String> {
|
|||
let start = lower.find("<title")?;
|
||||
let open_end = lower[start..].find('>')? + start + 1;
|
||||
let close = lower[open_end..].find("</title>")? + open_end;
|
||||
let title = html2text::from_read(html[open_end..close].as_bytes(), usize::MAX)
|
||||
let title = html2text::from_read(&html.as_bytes()[open_end..close], usize::MAX)
|
||||
.ok()?
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
|
|
@ -665,8 +665,8 @@ mod service_tests {
|
|||
assert!(content.contains("你好"), "{content}");
|
||||
assert!(content.contains("readable paragraph text"), "{content}");
|
||||
assert!(!content.contains("x()"), "{content}");
|
||||
let cut = truncate_content(result, 4);
|
||||
assert_eq!(cut["content"], "你");
|
||||
let cut = truncate_content(result, 6);
|
||||
assert_eq!(cut["content"], "# 你");
|
||||
assert_eq!(cut["truncated"], true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
| 使用者專案 | ExternalShell/ExternalRead → 本地執行代理 | external_exec_command/external_read_file/external_write_file/external_edit_file → 啟動端本地 |
|
||||
| 跨機器檔案 | CopyToBox/CopyFromBox | copy_to_box/copy_from_box;不隱含掛載本地工作區 |
|
||||
| 公開搜尋 | host → Cursor AiService.RunWebSearch | host → 相容 gateway;未指定 gateway 且模型設定為官方 xAI 時,host → xAI Responses web_search |
|
||||
| 公開抓頁 | host → Cursor AiService.RunWebFetch | 相容 gateway,或 xAI 遠端瀏覽;xAI 回傳明確標記 model_rendered_web_content,不是原始 HTTP 內容 |
|
||||
| 公開抓頁 | host → Cursor AiService.RunWebFetch | host 直接匿名 HTTP GET(HTML 轉文字、同 URL 十分鐘快取、redirect 重新檢查公開主機);只有網站拒絕純 HTTP 時才退回 xAI 遠端瀏覽,並明確標記 model_rendered_web_content 與 fallback_reason |
|
||||
| 登入網頁 | box Chrome | Docker Chromium,DOM helper 也在 Docker |
|
||||
| 人工登入 | RequestBoxHelp → agent 桌面 | request_box_help/browser_handoff → 同一個 Docker Chromium |
|
||||
| 桌面像素操作 | 父層 Screenshot;computerUse 子 agent 才有 Computer(click/type/…) | 父層 `screenshot` 唯讀;`spawn_subagent kind=computerUse` 才有 `computer`(xdotool `DISPLAY=:1`)。一次一個 computerUse |
|
||||
|
|
@ -31,7 +31,7 @@ export GROKBOY_WEB_BACKEND_URL='https://your-compatible-gateway.example'
|
|||
# 服務需要驗證時,從本機環境/secret manager 設定 GROKBOY_WEB_TOKEN。
|
||||
```
|
||||
|
||||
Gateway 需要提供 JSON Connect `POST /aiserver.v1.AiService/RunWebSearch`(searchTerm、explanation、modelId)與 `RunWebFetch`(url)。Search 回傳 answer/documents;Fetch 回傳 success.content 或 error。這不是普通 chat-completions endpoint。直接使用 Cursor 官方服務還需要它自己的登入與客戶端 headers;這裡沒有複製參考版的 account、checksum、privacy interceptor,不能把這個 adapter 當作已登入的 Cursor client。
|
||||
Gateway 需要提供 JSON Connect `POST /aiserver.v1.AiService/RunWebSearch`(searchTerm、explanation、modelId),回傳 answer/documents。這不是普通 chat-completions endpoint。`web_fetch` 不經 gateway,一律由 host 直接 GET;先前每次抓頁都繞一趟模型推論(實測單頁 12 秒以上、每次約 US$0.01),改為直接 GET 後為次秒級。直接使用 Cursor 官方服務還需要它自己的登入與客戶端 headers;這裡沒有複製參考版的 account、checksum、privacy interceptor,不能把這個 adapter 當作已登入的 Cursor client。
|
||||
|
||||
來源:[xAI 官方 Web Search 文件](https://docs.x.ai/developers/tools/web-search)。自動測試只用 mock 與本地網站,未使用付費 API 驗證特定帳號的搜尋權限。
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
## 主要結論
|
||||
|
||||
1. 直接與主模型供應商耦合的是 web_search、web_fetch;spawn_subagent/delegate_task/spawn_agent 會繼承或使用共享模型設定。
|
||||
1. 直接與主模型供應商耦合的是 web_search(web_fetch 只有拒絕純 HTTP 時的 fallback 才耦合);spawn_subagent/delegate_task/spawn_agent 會繼承或使用共享模型設定。
|
||||
2. 其餘工具的底層大多不綁模型供應商,但模型必須能透過目前的 Chat Completions/串流 tool_calls 介面正確呼叫它們。這個共同前提不滿足,所有 agent 工作都可能停住。
|
||||
3. 模型 adapter 目前沒有任意 provider capability discovery、Responses/Messages 自動適配,也沒有每個子 agent 的獨立 provider 選擇。換環境變數不會改變已運行 daemon 捕捉的 Config;需重啟相應程序。
|
||||
4. 換電腦最容易丟失的是 Docker volume、網站登入、本地 CLI/MCP 授權、絕對路徑、team/session資料,以及無法直接遷移的活動程序。
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
| `stop_subagent` | 本地程序內 subagent registry | 控制動作本身不綁 provider;被控制的 agent 依賴其原 model client | 換電腦/重啟程序後沒有原先的活動子代理 | 不把舊 subagent_id 當成可控制的活動任務 |
|
||||
| `update_plan` | LazyBoy2 runtime/session | 本身不綁 provider;仍需模型正確產生工具呼叫 | 工具可重新使用;計畫、待回答問題、歷史紀錄需另搬 session/team 資料 | 搬持久化資料;不搬 live socket/lock;重新確認尚未完成的互動 |
|
||||
| `wait_task` | team daemon/SQLite/Unix socket | 查詢/控制不直接依賴provider;任務繼續與記憶整理仍需模型 | 不搬 DB 就沒有原 agent、task、記憶;active 任務不能只靠同名ID繼續 | 搬整個 team 持久資料,重建 socket/lock,使用既有 recovery 流程核對狀態 |
|
||||
| `web_fetch` | host → xAI API 或相容 web gateway | 直接耦合:官方 xAI 自動使用當前 model/base/key;換其他 provider 可能直接報未設定 | 需重新設定模型與 web 服務的 endpoint/key;不會搬移瀏覽器 cookies | 獨立 web 設定與 capability adapter;目前 xAI fetch 為模型整理內容 |
|
||||
| `web_fetch` | host 直接匿名 HTTP GET;僅網站拒絕純 HTTP 時退回 xAI 遠端瀏覽 | 主路徑不綁 provider;fallback 只在官方 xAI 設定存在時啟用,其他 provider 直接回報抓取失敗 | 直接搬即可;不會搬移瀏覽器 cookies;程序內快取不持久 | fallback 的 provider 抽象化 |
|
||||
| `web_search` | host → xAI API 或相容 web gateway | 直接耦合:官方 xAI 自動使用當前 model/base/key;換其他 provider 可能直接報未設定 | 需重新設定模型與 web 服務的 endpoint/key;不會搬移瀏覽器 cookies | 獨立 web 設定與 capability adapter;目前 xAI fetch 為模型整理內容 |
|
||||
|
||||
## 要搬的資料
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant" data-theme="cursor-dark" style="color-scheme: dark">
|
||||
<html lang="zh-Hant" style="color-scheme: dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1" />
|
||||
<meta name="theme-color" content="#141414" />
|
||||
<meta name="theme-color" content="#050506" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"name": "grokboy-web",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@fontsource/huninn": "^5.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
|
|
@ -692,6 +693,15 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/huninn": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/huninn/-/huninn-5.3.0.tgz",
|
||||
"integrity": "sha512-aXA0W8ISDrhu1uU/aS/6sxbZg9OzRXn1EG9WbJquuI7LM1iPpkCqGlvAI1cH/xjkiggmRfgeu7FftYOLq7MGGQ==",
|
||||
"license": "OFL-1.1",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ayuhito"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/huninn": "^5.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
import type { ReactNode } from "react";
|
||||
|
||||
type IconProps = { className?: string; size?: number };
|
||||
|
||||
function Svg({ className, size = 16, children }: IconProps & { children: ReactNode }) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{children}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Plus({ className, size = 17 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<path d="M12 5v14M5 12h14" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Search({ className, size = 16 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<path d="M20 20l-3.5-3.5" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Menu({ className, size = 18 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<path d="M4 7h16M4 12h16M4 17h16" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Computer({ className, size = 18 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<rect x="3" y="5" width="18" height="12" rx="2" />
|
||||
<path d="M8 21h8M12 17v4" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Square({ className, size = 14 }: IconProps) {
|
||||
return (
|
||||
<svg className={className} width={size} height={size} viewBox="0 0 24 24" aria-hidden="true">
|
||||
<rect x="6" y="6" width="12" height="12" rx="2" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArrowUp({ className, size = 20 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<path d="M12 19V5M5 12l7-7 7 7" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function X({ className, size = 16 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<path d="M6 6l12 12M18 6L6 18" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ChevronsRight({ className, size = 17 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<path d="M9 6l6 6-6 6" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function MonitorEmpty({ className, size = 28 }: IconProps) {
|
||||
return (
|
||||
<Svg className={className} size={size}>
|
||||
<rect x="2" y="4" width="20" height="14" rx="2" />
|
||||
<path d="M8 21h8M12 18v3" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
.avatar{flex:0 0 auto;width:36px;height:36px;display:grid;place-items:center;border-radius:50%;background:#26262a;color:#c9c9ce;font-weight:650}
|
||||
.avatar.online{background:var(--accent);color:#083f34;box-shadow:inset 0 0 0 8px rgba(0,0,0,.16)}
|
||||
.topbar .avatar{width:32px;height:32px}
|
||||
.avatar.blobatar,.avatar.blobatar.online{position:relative;display:inline-grid;place-items:center;flex:0 0 var(--avatar-size);width:var(--avatar-size);height:var(--avatar-size);overflow:visible;border:0;border-radius:0;background:transparent;box-shadow:none;color:unset}
|
||||
.avatar.blobatar>svg,.avatar.blobatar>img{display:block;width:100%;height:100%}
|
||||
.avatar.blobatar.thinking:before,.avatar.blobatar.thinking:after{content:"";position:absolute;z-index:4;inset:-6px;border-radius:50%;background:conic-gradient(from 0deg,transparent 0 8%,#ff4fd8 11%,#7c5cff 18%,transparent 25% 43%,#34d9ff 48%,#58f39a 55%,transparent 62% 78%,#ffe66d 82%,#ff7a59 89%,transparent 96%);-webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1.5px));mask:radial-gradient(farthest-side,transparent calc(100% - 2px),#000 calc(100% - 1.5px));pointer-events:none;animation:magic-orbit 1.35s linear infinite}
|
||||
.avatar.blobatar.thinking:after{inset:-8px;opacity:.45;filter:blur(3px);animation-duration:2.1s;animation-direction:reverse}
|
||||
.avatar-wrap{position:relative;display:grid;flex:0 0 auto}
|
||||
.avatar-editor{display:grid;justify-items:center;gap:5px;padding:18px 0 2px}
|
||||
.avatar-editor strong{margin-top:7px}
|
||||
.avatar-editor .avatar.blobatar{margin-bottom:2px}
|
||||
.avatar .presence{position:absolute;z-index:6;right:-1px;bottom:-1px;width:clamp(8px,calc(var(--avatar-size)*0.25),11px);height:clamp(8px,calc(var(--avatar-size)*0.25),11px);min-width:8px;min-height:8px;border:2px solid var(--side);border-radius:50%;background:var(--online);box-shadow:0 0 0 1px rgba(0,0,0,.4);pointer-events:none}
|
||||
.bot-row .avatar-wrap::after{content:none;position:absolute;z-index:9;right:-2px;bottom:-2px;width:9px;height:9px;border:2px solid var(--side);border-radius:50%;background:var(--online);box-shadow:0 0 0 1px rgba(0,0,0,.38);pointer-events:none}
|
||||
.avatar.blobatar.thinking{animation:avatar-rainbow-glow 1.8s linear infinite}
|
||||
.avatar-wrap{transition:transform .16s ease}
|
||||
.avatar-stack{position:relative;display:block;flex:0 0 auto}
|
||||
.avatar-stack .stack-item{position:absolute;top:0}
|
||||
.avatar-stack .stack-item .avatar{box-shadow:none;filter:drop-shadow(2px 0 0 var(--side)) drop-shadow(-2px 0 0 var(--side)) drop-shadow(0 2px 0 var(--side)) drop-shadow(0 -2px 0 var(--side))}
|
||||
.avatar-stack .stack-item .avatar.thinking{filter:none;animation:avatar-rainbow-glow 1.8s linear infinite}
|
||||
.stack-extra{position:absolute;top:0;display:grid;place-items:center;border-radius:50%;background:#2a2a2e;color:var(--muted);font-size:11px;font-weight:650;box-shadow:0 0 0 2px var(--side)}
|
||||
.topbar .avatar-stack{margin-right:2px}
|
||||
.avatar-stack{isolation:isolate}
|
||||
.avatar-stack .stack-item{transition:transform .16s ease}
|
||||
.avatar-stack .stack-item:hover{transform:translateY(-2px);z-index:8!important}
|
||||
.avatar-stack .stack-item .avatar{filter:drop-shadow(2px 0 0 var(--main)) drop-shadow(-2px 0 0 var(--main)) drop-shadow(0 2px 0 var(--main)) drop-shadow(0 -2px 0 var(--main))}
|
||||
.avatar-stack .stack-extra{box-shadow:0 0 0 2px var(--main)}
|
||||
.avatar-stack .stack-item .avatar.thinking{filter:none}
|
||||
/* Keep avatars independent of message and toolbar dimensions. */
|
||||
.avatar.blobatar{min-width:var(--avatar-size);max-width:var(--avatar-size);min-height:var(--avatar-size);padding:0;line-height:0;vertical-align:middle}
|
||||
.avatar.blobatar>svg{max-width:none}
|
||||
.avatar-stack .stack-item{line-height:0}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
.messages{flex:1;overflow:auto;padding:34px max(34px,7vw) 150px;display:flex;flex-direction:column;gap:18px}
|
||||
.message{display:flex}
|
||||
.message>.message-body,.message-stack>.message-body{max-width:76%;padding:13px 17px;border-radius:22px;white-space:pre-wrap;line-height:1.5}
|
||||
.message>.message-body.md,.message-stack>.message-body.md{min-width:0;white-space:normal}
|
||||
.message.user{justify-content:flex-end}
|
||||
.message.user>.message-body{background:var(--cream);color:var(--on-cream)}
|
||||
.message.assistant>.message-body,.message-stack>.message-body{background:#19191c}
|
||||
.message-stack{display:flex;flex-direction:column;align-items:flex-start;max-width:76%;min-width:0}
|
||||
.message-stack>.message-body{max-width:100%}
|
||||
.copy-msg{display:inline-flex;align-items:center;gap:6px;height:28px;margin-top:6px;padding:0 8px 0 6px;border:0;border-radius:8px;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}
|
||||
.copy-msg svg{flex:0 0 14px}
|
||||
.copy-msg:hover{background:rgba(255,255,255,.07);color:var(--ink)}
|
||||
.copy-msg.copied,.copy-msg.copied:hover{color:var(--accent);background:transparent;cursor:default}
|
||||
.composer{position:absolute;left:max(34px,5vw);right:max(34px,5vw);bottom:22px;display:flex;align-items:flex-end;gap:8px;border:1px solid var(--border);background:var(--elevated);border-radius:26px;padding:8px;box-shadow:0 16px 50px rgba(0,0,0,.22)}
|
||||
.composer textarea{flex:1;min-height:42px;max-height:140px;resize:none;border:0;outline:0;background:transparent;color:var(--ink);padding:11px 4px}
|
||||
.composer-plus{background:transparent;color:var(--muted)}
|
||||
.composer svg{width:18px}
|
||||
.messages{min-width:0;padding-bottom:24px;padding-left:var(--chat-gutter);padding-right:var(--chat-gutter)}
|
||||
.message{box-sizing:border-box;width:var(--chat-col);max-width:none;margin-inline:auto;overflow-wrap:anywhere}
|
||||
.message>span{max-width:82%}
|
||||
.thinking-row{display:flex;align-items:center;gap:10px;width:min(760px,100%);margin:0}
|
||||
.thinking-dots{position:relative;display:flex;align-items:center;gap:4px;height:32px;padding:0 12px;border:1px solid var(--border);border-radius:14px;background:var(--surface);overflow:hidden}
|
||||
.thinking-dots:after{content:"";position:absolute;left:12px;bottom:4px;width:24px;height:2px;border-radius:999px;background:linear-gradient(90deg,#7c5cff,#34d9ff,#58f39a,#ffe66d,#ff73dc,#7c5cff);background-size:200% 100%;animation:small-magic-line 1.25s linear infinite}
|
||||
.thinking-dots i{width:5px;height:5px;border-radius:50%;background:var(--muted);animation:thinking-dot 1.15s ease-in-out infinite}
|
||||
.thinking-dots i:nth-child(2){animation-delay:.16s}
|
||||
.thinking-dots i:nth-child(3){animation-delay:.32s}
|
||||
.composer-dock{position:relative;z-index:6;display:flex;flex-direction:column;align-items:center;gap:20px;padding:28px var(--chat-gutter) 20px;background:var(--main);pointer-events:none;flex:none;width:100%}
|
||||
.composer-dock:before{content:"";position:absolute;left:0;right:0;bottom:100%;height:32px;background:linear-gradient(180deg,transparent,var(--main));pointer-events:none}
|
||||
.composer-dock>*{box-sizing:border-box;pointer-events:auto;width:var(--chat-col);max-width:760px;min-width:0;margin-inline:auto}
|
||||
.composer{position:relative;left:auto;right:auto;bottom:auto;width:var(--chat-col);min-width:0;max-width:100%;min-height:62px;align-items:center;padding:9px 10px;transform:none}
|
||||
.composer.has-files{flex-wrap:wrap;align-items:flex-end;padding-top:10px}
|
||||
.composer textarea{align-self:center;box-sizing:border-box;height:42px;min-height:42px;max-height:126px;padding:11px 4px;line-height:20px}
|
||||
.slash-suggestions{position:absolute;left:58px;right:58px;bottom:calc(100% + 8px);z-index:8;display:grid;gap:2px;padding:6px;border:1px solid var(--border);border-radius:14px;background:var(--menu);box-shadow:0 14px 34px rgba(0,0,0,.35)}
|
||||
.slash-suggestions button{display:flex;align-items:baseline;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:9px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}
|
||||
.slash-suggestions button:hover,.slash-suggestions button:focus-visible{background:rgba(255,255,255,.08);outline:0}
|
||||
.slash-suggestions strong{min-width:110px;color:var(--accent);font-weight:650}
|
||||
.slash-suggestions span{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}
|
||||
.composer-files{display:flex;flex-wrap:wrap;gap:8px;flex:1 0 100%;order:-1;padding:2px 8px 10px 46px}
|
||||
.file-card{display:flex;align-items:center;gap:8px;max-width:min(240px,100%);height:48px;padding:2px 8px 2px 2px;border:1px solid var(--border);border-radius:14px;background:var(--surface);color:var(--ink)}
|
||||
.file-card-thumb,.file-card-badge{flex:0 0 44px;width:44px;height:44px;border-radius:11px;object-fit:cover}
|
||||
.file-card-badge{display:grid;place-items:center;background:var(--hairline);color:var(--muted);font-size:10px;font-weight:700;letter-spacing:.04em}
|
||||
.file-card-meta{display:grid;min-width:0;flex:1}
|
||||
.file-card-meta strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:600}
|
||||
.file-card-meta small{color:var(--muted);font-size:11px}
|
||||
.file-card-remove{flex:0 0 28px;width:28px;height:28px;display:grid;place-items:center;border:0;border-radius:8px;background:transparent;color:var(--muted);cursor:pointer}
|
||||
.file-card-remove:hover{color:var(--ink);background:rgba(255,255,255,.08)}
|
||||
.file-card-remove svg{width:14px;height:14px}
|
||||
.message.with-files{align-items:flex-end;flex-direction:column;gap:8px}
|
||||
.message.with-files .msg-attachments{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;max-width:min(420px,82%)}
|
||||
.message.with-files .message-body,.message.with-files .message-stack{max-width:82%}
|
||||
.composer-plus,.composer .send{box-sizing:border-box;flex:0 0 42px;width:42px;height:42px;margin:0;align-self:center}
|
||||
.stop-send{background:var(--cream);color:var(--on-send)}
|
||||
.stop-send svg{width:14px;height:14px;fill:currentColor}
|
||||
.composer-plus:disabled{opacity:.35;cursor:not-allowed}
|
||||
.message{position:relative;padding-right:0;-webkit-touch-callout:none;touch-action:manipulation}
|
||||
.messages .remember-msg{display:none}
|
||||
.working-label{font-size:13px;letter-spacing:.02em;line-height:1.35;white-space:nowrap;background:linear-gradient(90deg,#7a8088 0%,#7a8088 28%,#fff 50%,#7a8088 72%,#7a8088 100%);background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:working-shimmer 1.35s linear infinite}
|
||||
.message.assistant.spoken{display:grid;grid-template-columns:28px minmax(0,1fr);column-gap:10px;row-gap:3px;justify-content:start;align-items:end}
|
||||
.message.assistant.spoken .msg-avatar{grid-column:1;grid-row:2;align-self:start;max-width:none;margin:4px 0 0;padding:0;border-radius:0;background:transparent;line-height:normal}
|
||||
.message.assistant .msg-avatar>.avatar.blobatar{max-width:none;padding:0;background:transparent;color:inherit;line-height:normal;white-space:normal}
|
||||
.message.assistant.spoken .speaker{grid-column:2;font-size:12px;font-weight:650;line-height:1.2;padding:0 6px;color:var(--accent)}
|
||||
.message.assistant.spoken>span{grid-column:2;max-width:82%;border-radius:6px 18px 18px 18px}
|
||||
.thinking-row{width:100%;margin:0;padding-left:2px}
|
||||
.working-copy{display:flex;flex-direction:column;gap:8px;min-width:0}
|
||||
.working-step{color:var(--muted);font-size:12px;letter-spacing:.02em;line-height:1.35}
|
||||
/* Speaker rows: the coloured speaker label must breathe away from the text. */
|
||||
.message.assistant.spoken{column-gap:11px;row-gap:6px}
|
||||
.message.assistant.spoken>.msg-avatar{max-width:none;padding:0;border-radius:0;background:transparent}
|
||||
.message.assistant.spoken .speaker{padding:0 2px;letter-spacing:.01em}
|
||||
.message.assistant.spoken>.message-body,.message.assistant.spoken>.message-stack{grid-column:2;max-width:82%;min-width:0}
|
||||
.message.assistant.spoken .message-stack>.message-body{max-width:100%;line-height:1.52;border-radius:6px 18px 18px 18px}
|
||||
.composer-plus.open{color:var(--ink);background:rgba(255,255,255,.08)}
|
||||
|
||||
.slash-suggestions button[aria-selected="true"]{background:rgba(255,255,255,.08)}
|
||||
.slash-suggestions small{flex-shrink:0;color:var(--muted);font-size:11px}
|
||||
|
||||
.message{padding-bottom:21px}
|
||||
.message-time{position:absolute;bottom:0;left:0;font-size:11px;line-height:17px;color:var(--muted);font-variant-numeric:tabular-nums}
|
||||
.message.user .message-time{left:auto;right:0}
|
||||
|
||||
/* The day is said once, centred above the first message of that day, so each
|
||||
message only needs its clock time. */
|
||||
.day-divider{display:flex;justify-content:center;width:var(--chat-col);margin:6px auto -4px;user-select:none}
|
||||
.day-divider time{padding:3px 11px;border-radius:999px;background:rgba(255,255,255,.06);color:var(--muted);font-size:11px;line-height:16px;letter-spacing:.02em}
|
||||
|
||||
/* Who the room sent a message to: one thin line in the space under the bubble,
|
||||
opposite the timestamp, so it reads as a note about the message and never as
|
||||
a second message. */
|
||||
.route-note{display:none}
|
||||
|
||||
.composer.has-reply{flex-wrap:wrap;align-items:flex-end;padding-top:10px}
|
||||
.composer-reply{display:flex;align-items:center;gap:8px;box-sizing:border-box;flex:1 1 100%;min-width:0;max-width:100%;width:100%;overflow:hidden;order:-2;margin:2px 0 4px;padding:7px 8px 7px 46px;border-left:2px solid var(--accent);border-radius:10px;background:rgba(255,255,255,.04)}
|
||||
.composer-reply-copy{min-width:0;flex:1 1 auto;overflow:hidden}
|
||||
.composer-reply-copy small,.composer-reply-copy span{display:block;overflow:hidden;min-width:0;text-overflow:ellipsis;white-space:nowrap}
|
||||
.composer-reply-copy small{color:var(--accent);font-size:11px;font-weight:650;line-height:1.3}
|
||||
.composer-reply-copy span{color:var(--muted);font-size:12px;line-height:1.35}
|
||||
.composer-reply-close{flex:0 0 28px;width:28px;height:28px;display:grid;place-items:center;border:0;border-radius:8px;background:transparent;color:var(--muted);cursor:pointer}
|
||||
.composer-reply-close:hover{color:var(--ink);background:rgba(255,255,255,.08)}
|
||||
.composer-reply-close svg{width:14px;height:14px}
|
||||
|
||||
.reply-quote{display:block;margin:0 0 8px;padding:6px 10px;border-left:2px solid var(--accent);border-radius:8px;background:rgba(255,255,255,.05);white-space:normal}
|
||||
.reply-quote strong{display:block;color:var(--accent);font-size:11px;font-weight:650;line-height:1.3}
|
||||
.reply-quote span{display:block;overflow:hidden;color:var(--muted);font-size:12px;line-height:1.35;text-overflow:ellipsis;white-space:nowrap}
|
||||
.message.user .reply-quote{background:rgba(0,0,0,.08)}
|
||||
.message.user .reply-quote strong{color:var(--on-cream);opacity:.72}
|
||||
.message.user .reply-quote span{color:var(--on-cream);opacity:.7}
|
||||
.message.replying>.message-body,.message.replying>.message-stack>.message-body{box-shadow:inset 0 0 0 1px var(--accent)}
|
||||
|
||||
.mention-link{display:inline;margin:0;padding:0;border:0;border-radius:0;background:none;color:var(--accent);font:inherit;line-height:inherit;text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--accent) 55%,transparent);text-underline-offset:.16em;cursor:pointer}
|
||||
.mention-link:hover{color:#7ee0c8}
|
||||
.mention-link.is-everyone{cursor:default;font-weight:650}
|
||||
.message.user .mention-link{color:#0f766e;text-decoration-color:color-mix(in srgb,#0f766e 50%,transparent)}
|
||||
.message.user .mention-link:hover{color:#115e59}
|
||||
|
||||
/* The `@` list sits where the slash list sits: only one of them is ever open. */
|
||||
.mention-suggestions button{align-items:center;gap:8px}
|
||||
.mention-suggestions strong{min-width:0}
|
||||
|
||||
/* The host is a name in the top bar, not a page of settings. */
|
||||
.host-chip-wrap{position:relative;display:inline-flex;align-items:center}
|
||||
.host-chip{display:inline-flex;align-items:center;gap:5px;max-width:150px;padding:3px 9px;border:1px solid var(--border);border-radius:999px;background:transparent;color:var(--muted);font-size:11px;cursor:pointer}
|
||||
.host-chip:hover,.host-chip[aria-expanded="true"]{border-color:var(--accent);color:var(--ink)}
|
||||
.host-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.host-menu{position:absolute;top:calc(100% + 8px);left:0;z-index:9;display:grid;gap:2px;min-width:190px;padding:8px;border:1px solid var(--border);border-radius:14px;background:var(--menu);box-shadow:0 14px 34px rgba(0,0,0,.35)}
|
||||
.host-menu button{display:flex;align-items:center;gap:8px;padding:7px 9px;border:0;border-radius:9px;background:transparent;color:var(--ink);font-size:13px;text-align:left;cursor:pointer}
|
||||
.host-menu button:hover,.host-menu button:focus-visible{background:rgba(255,255,255,.08);outline:0}
|
||||
.host-menu button span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.host-menu-label{padding:2px 9px 4px;color:var(--muted);font-size:11px}
|
||||
.host-flag{margin-left:auto;color:var(--accent);font-size:11px;font-weight:650}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
.computer-panel{display:flex;flex-direction:column;background:var(--panel);border-right:0;padding:0 20px}
|
||||
.state-dot{width:7px;height:7px;border-radius:50%;background:var(--faint)}
|
||||
.state-dot.running{background:var(--success)}
|
||||
.state-dot.booting{background:#f5a03c;animation:pulse 1s infinite}
|
||||
.state-dot.error{background:var(--danger)}
|
||||
.desktop-frame{display:block;width:100%;height:100%;border:0;background:var(--inset)}
|
||||
.empty-computer{height:100%;display:grid;place-content:center;justify-items:center;gap:7px;color:var(--muted);text-align:center}
|
||||
.empty-computer svg{width:28px}
|
||||
.empty-computer span{font-size:12px}
|
||||
.computer-caption{display:flex;align-items:center;justify-content:space-between;padding:14px 0;color:var(--muted)}
|
||||
.control-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px;border-top:1px solid var(--hairline);padding:16px 0}
|
||||
.computer-overlay{position:fixed;inset:0;z-index:50;display:flex;flex-direction:column;background:rgba(4,4,5,.98)}
|
||||
.computer-overlay>header{height:64px;display:flex;align-items:center;justify-content:space-between;padding:0 18px;border-bottom:1px solid var(--line)}
|
||||
.computer-overlay>header>div{display:flex;align-items:center;gap:10px}
|
||||
.computer-overlay .avatar{width:30px;height:30px}
|
||||
.control-badge{padding:5px 10px;border-radius:999px;background:rgba(48,162,75,.14);color:var(--success);font-size:13px}
|
||||
.overlay-screen{flex:1;min-height:0;padding:20px;display:flex;justify-content:center}
|
||||
.overlay-screen>.desktop-frame,.overlay-screen>.empty-computer{width:min(100%,1440px);height:100%;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--main)}
|
||||
.overlay-error{position:absolute;top:74px;left:50%;transform:translateX(-50%);background:var(--error-bg);color:var(--error-fg);padding:9px 15px;border-radius:9px}
|
||||
.computer-toggle{background:var(--surface)}
|
||||
.meeting-stage{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--panel);border-right:1px solid var(--hairline)}
|
||||
.meeting-stage.is-hidden{display:none}
|
||||
.meeting-stage-head{display:flex;align-items:center;gap:6px;height:72px;flex:0 0 72px;padding:0 10px 0 12px;border-bottom:1px solid var(--hairline)}
|
||||
.meeting-stage .side-card-body{display:flex;flex-direction:column;min-height:0;flex:1;padding:14px 16px 16px;overflow:hidden}
|
||||
.meeting-stage.is-meeting .meeting-extras{display:none}
|
||||
.meeting-stage.is-meeting .computer-part{overflow:hidden}
|
||||
.meeting-stage.is-meeting .computer-part>.preview{flex:1 1 auto;flex-shrink:1;width:100%;height:auto;min-height:0;max-height:none;aspect-ratio:auto}
|
||||
.meeting-stage.is-meeting .computer-part .preview .desktop-frame,
|
||||
.meeting-stage.is-meeting .computer-part .preview .empty-computer{height:100%;aspect-ratio:auto;border-radius:12px;overflow:hidden}
|
||||
.computer-part{display:flex;flex-direction:column;min-height:0;flex:1;gap:10px;overflow:auto}
|
||||
.computer-part.hidden-part{display:none}
|
||||
.computer-status-row{display:flex;align-items:center;gap:8px;color:var(--muted)}
|
||||
.computer-status-row>span{margin-right:auto;color:var(--ink);font-weight:600}
|
||||
.computer-part .preview{flex:1;min-height:160px;aspect-ratio:auto}
|
||||
/* Preserve the monitor's 16:10 shape as the right panel gets narrower. */
|
||||
.computer-part .preview{position:relative;flex:0 1 auto;width:100%;height:auto;min-height:0;max-height:min(52vh,380px);aspect-ratio:16 / 10}
|
||||
.computer-part .preview .desktop-frame{display:block;aspect-ratio:16 / 10}
|
||||
.overlay-screen .overlay-desktop{position:relative;width:min(100%,1440px);height:100%}
|
||||
.overlay-screen .overlay-desktop>.desktop-frame,.overlay-screen .overlay-desktop>.empty-computer{width:100%;height:100%;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--main)}
|
||||
/* The waiting veil sits over the remote pixels, not beside them: while the
|
||||
desktop is booting, waking, or changing hands, the signal mascot breathes
|
||||
under its amber orbit and the label shimmers. It never takes a click, so the
|
||||
mouse is live the instant the veil lifts, and it fades rather than blinks. */
|
||||
.computer-hud{position:absolute;inset:0;z-index:3;display:grid;align-content:center;justify-items:center;gap:14px;padding:10px 12px 8px;border-radius:inherit;background:radial-gradient(ellipse at center,#142722e8,#101012ed);backdrop-filter:blur(8px);pointer-events:none;opacity:1;transition:opacity .22s ease}
|
||||
.computer-hud.is-leaving{opacity:0}
|
||||
.computer-hud-label{font-size:12px;letter-spacing:.02em;line-height:1.5;white-space:normal;text-align:center;max-width:90%;color:#c9ddd5}
|
||||
.empty-computer.is-waiting{min-height:100%;background:var(--inset)}
|
||||
.computer-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0;flex-wrap:wrap}
|
||||
.computer-actions .restart-computer{font-size:12px;padding-inline:10px;color:var(--muted)}
|
||||
.computer-actions .restart-computer svg{width:14px;height:14px}
|
||||
.computer-actions .primary{min-width:0}
|
||||
.computer-overlay header .computer-actions{justify-content:flex-end}
|
||||
.control-badge.teaching{background:rgba(239,85,85,.16);color:#ff8a8a}
|
||||
.computer-login-hint{margin:0;color:var(--faint);font-size:12px;line-height:1.45}
|
||||
.computer-part{scrollbar-gutter:stable;overflow-x:hidden}
|
||||
.computer-part>*{flex-shrink:0;min-width:0}
|
||||
.computer-part .preview{flex:none;max-height:none}
|
||||
.computer-status-row>span{min-width:0;overflow-wrap:anywhere}
|
||||
.computer-status-row>small,.state-dot{flex-shrink:0}
|
||||
.control-bar{flex-wrap:wrap}
|
||||
.control-bar>.computer-actions{flex:1 1 180px}
|
||||
/* Independent monitor mascot, with mint halo and amber orbit. */
|
||||
.computer-signal{position:relative;display:grid;place-items:center;width:64px;height:64px;border:1px solid #65dcb34d;border-radius:24px;animation:monitor-breathe 2.8s ease-in-out infinite}
|
||||
.computer-signal::before{content:"";position:absolute;inset:-8px;border:1px solid #65dcb326;border-top-color:#efbe72;border-radius:50%;animation:monitor-orbit 4s linear infinite}
|
||||
.computer-signal-face{display:flex;align-items:center;justify-content:center;gap:12px;width:46px;height:38px;border-radius:15px;background:#85dfbb;color:#143429;transform:rotate(-6deg)}
|
||||
.computer-signal-face i{width:5px;height:11px;border-radius:5px;background:currentColor;animation:monitor-blink 4.6s ease-in-out infinite}
|
||||
|
||||
.clipboard-status{margin:0;min-height:1.4em;font-size:12px;line-height:1.4;color:var(--muted)}
|
||||
|
||||
/* Keep control actions prominent and group less frequent power actions. */
|
||||
.control-bar>.computer-actions{justify-content:flex-start}
|
||||
.computer-power{position:relative;flex-shrink:0;margin-left:auto}
|
||||
.computer-power-trigger{padding:8px;display:inline-flex;align-items:center;justify-content:center}
|
||||
.computer-power-trigger svg{width:18px;height:18px}
|
||||
.computer-power-menu{position:absolute;right:0;bottom:calc(100% + 8px);z-index:40;width:180px;padding:6px;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:0 8px 28px #0005}
|
||||
.computer-power-menu button{display:flex;align-items:center;gap:9px;width:100%;padding:10px;border:0;border-radius:7px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}
|
||||
.computer-power-menu button:hover{background:var(--surface-hover)}
|
||||
.computer-power-menu svg{width:16px;height:16px}
|
||||
.computer-power-menu .computer-power-stop{color:var(--danger-soft)}
|
||||
.computer-power-menu small{display:block;padding:8px 10px 6px;border-top:1px solid var(--line);color:var(--muted);font-size:11px;line-height:1.6}
|
||||
.computer-overlay .computer-power-menu{top:calc(100% + 8px);bottom:auto}
|
||||
|
||||
/* The overlay tracks the visible viewport so the veil and the controls stay
|
||||
above the keyboard; the veil itself belongs to the desktop box below. */
|
||||
.computer-overlay{top:var(--visible-top,0px);bottom:auto;height:var(--visible-height,100dvh)}
|
||||
.computer-overlay>header{flex-shrink:0}
|
||||
.overlay-error{position:static;transform:none;flex:none;margin:0 8px 8px}
|
||||
@media(max-width:700px){
|
||||
.meeting-stage{display:none!important}
|
||||
.computer-overlay{position:fixed!important;inset:0!important;top:0!important;left:0!important;right:0!important;bottom:0!important;width:100%!important;height:100dvh!important;height:100%!important;z-index:200}
|
||||
.computer-overlay>header{height:auto;min-height:48px;gap:4px;padding:calc(6px + env(safe-area-inset-top)) 10px 6px}
|
||||
.computer-overlay>header>div{gap:5px;flex-wrap:wrap}
|
||||
.computer-overlay>header strong{font-size:12px}
|
||||
.overlay-screen{padding:0}
|
||||
.overlay-screen .overlay-desktop,.overlay-screen .overlay-desktop>.desktop-frame,.overlay-screen .overlay-desktop>.empty-computer{width:100%;height:100%;border:0;border-radius:0}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
.question-card {
|
||||
width: var(--chat-col);
|
||||
max-width: 760px;
|
||||
margin: 0 auto 8px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
}
|
||||
.question-card p { margin: 0 0 10px; color: var(--ink); }
|
||||
.question-card__options { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.question-card__options button {
|
||||
min-height: 36px;
|
||||
padding: 6px 12px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: var(--cream);
|
||||
color: var(--on-cream);
|
||||
font-weight: 650;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.activity-line {
|
||||
width: var(--chat-col);
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.create-inline {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin: 0 5px 10px;
|
||||
}
|
||||
.create-inline input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 36px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: var(--input);
|
||||
color: var(--ink);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.phone-tabs { display: none; }
|
||||
@media (max-width: 700px) {
|
||||
.phone-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 30;
|
||||
padding: 6px 8px env(safe-area-inset-bottom, 0px);
|
||||
background: var(--side);
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
.phone-tabs button {
|
||||
min-height: 44px;
|
||||
border: 0;
|
||||
border-radius: 12px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-weight: 650;
|
||||
}
|
||||
.phone-tabs button[aria-current="true"] {
|
||||
color: var(--ink);
|
||||
background: var(--surface);
|
||||
}
|
||||
.composer-dock { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: none;
|
||||
border-radius: 9px;
|
||||
margin-right: 9px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
|
@ -0,0 +1,965 @@
|
|||
.memory-dialog{max-height:85vh;overflow:auto}
|
||||
|
||||
.memory-dialog form{display:flex;gap:10px;align-items:end}
|
||||
|
||||
.memory-dialog form label{flex:1}
|
||||
|
||||
.memory-list{display:grid;gap:8px;margin:14px 0}
|
||||
|
||||
.memory-row{display:grid;gap:5px;padding:10px;border:1px solid var(--border);border-radius:10px}
|
||||
|
||||
.memory-row small{opacity:.65}
|
||||
|
||||
.memory-row>div{display:flex;gap:8px}
|
||||
|
||||
.memory-row textarea{box-sizing:border-box;width:100%;resize:vertical;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:11px;font:inherit}
|
||||
|
||||
|
||||
.app-shell{grid-template-columns:clamp(220px,18vw,280px) minmax(0,1fr) clamp(320px,28vw,420px)}
|
||||
|
||||
|
||||
.sidebar,.chat-panel,.computer-panel,.topbar,.panel-head,.control-bar{min-width:0}
|
||||
|
||||
|
||||
.session-picker select{max-width:190px;min-width:90px;padding:7px 28px 7px 10px;border:1px solid var(--border);border-radius:9px;color:var(--ink);background:var(--surface)}
|
||||
|
||||
|
||||
.magic-particles{position:absolute;z-index:4;inset:-12px;pointer-events:none;animation:magic-orbit 2.4s linear infinite}
|
||||
|
||||
.magic-particles i{position:absolute;width:4px;height:4px;border-radius:50%;background:#fff;box-shadow:0 0 7px 2px #6df,0 0 12px #d5f}
|
||||
|
||||
.magic-particles i:nth-child(1){top:0;left:48%}
|
||||
|
||||
.magic-particles i:nth-child(2){right:0;top:45%;background:#ffe66d}
|
||||
|
||||
.magic-particles i:nth-child(3){bottom:1px;left:35%;background:#ff73dc}
|
||||
|
||||
.magic-particles i:nth-child(4){left:0;top:30%;background:#79ffb0}
|
||||
|
||||
|
||||
.chat-panel{--chat-gutter:max(34px,7vw);--chat-col:min(760px,100%)}
|
||||
|
||||
|
||||
.composer-dock .error-banner,.composer-dock .queue-hint{position:relative;left:auto;bottom:auto;transform:none;z-index:auto}
|
||||
.composer-dock .queue-hint{text-align:center}
|
||||
|
||||
|
||||
.spinner{width:17px;height:17px;animation:spin .8s linear infinite}
|
||||
|
||||
.spinner.large{width:38px;height:38px;color:var(--accent)}
|
||||
|
||||
.primary .spinner{margin-right:7px}
|
||||
|
||||
.primary{display:inline-flex;align-items:center;justify-content:center}
|
||||
|
||||
|
||||
.clipboard-text{box-sizing:border-box;width:100%;min-height:150px;resize:vertical;border:1px solid var(--border);border-radius:12px;background:var(--input);color:var(--ink);padding:12px;font:inherit;line-height:1.5;outline:none}
|
||||
|
||||
.clipboard-text:focus{border-color:var(--accent)}
|
||||
|
||||
|
||||
.bot-tag{padding:3px 8px;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:11px;white-space:nowrap}
|
||||
|
||||
|
||||
.bot-row{gap:9px}
|
||||
|
||||
.bot-copy{display:grid;min-width:0;flex:1}
|
||||
|
||||
.bot-copy strong,.bot-copy small{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
.side-tag{max-width:64px;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;padding:2px 6px;color:var(--ink);background:rgba(255,255,255,.055)}
|
||||
|
||||
.unread-dot{position:absolute;z-index:8;top:-2px;right:-3px;width:9px;height:9px;border:2px solid var(--side);border-radius:50%;background:var(--unread);box-shadow:0 0 8px color-mix(in srgb,var(--unread) 65%,transparent)}
|
||||
|
||||
.bot-group{display:grid;gap:3px}
|
||||
|
||||
.group-label{padding:12px 11px 4px;color:var(--muted);font-size:11px;letter-spacing:.04em}
|
||||
|
||||
.row-time{align-self:start;color:var(--muted);font-size:10px;white-space:nowrap}
|
||||
|
||||
.row-pin{width:13px;height:13px;color:var(--muted);transform:rotate(-20deg)}
|
||||
|
||||
.hidden-toggle{display:flex;align-items:center;gap:7px;margin:4px 12px;padding:8px;border:0;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}
|
||||
|
||||
.hidden-toggle svg{width:14px}
|
||||
|
||||
|
||||
.context-menu{position:fixed;z-index:100;display:grid;width:210px;padding:6px;border:1px solid var(--border);border-radius:13px;background:var(--menu);box-shadow:0 18px 60px rgba(0,0,0,.55)}
|
||||
|
||||
.context-menu button{display:flex;align-items:center;gap:10px;width:100%;height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
.context-menu button:hover{background:rgba(255,255,255,.07)}
|
||||
|
||||
.context-menu button svg{width:16px;height:16px}
|
||||
|
||||
.context-menu hr{width:100%;margin:5px 0;border:0;border-top:1px solid var(--border)}
|
||||
|
||||
.context-menu .danger-item{color:var(--danger-soft)}
|
||||
|
||||
.context-menu .context-close{display:none}
|
||||
|
||||
|
||||
.settings-dialog{width:min(520px,calc(100vw - 28px));max-height:min(850px,calc(100dvh - 28px));overflow:auto}
|
||||
|
||||
.avatar-editor small,.settings-dialog label small{color:var(--muted)}
|
||||
|
||||
.settings-dialog fieldset{margin:0;padding:0;border:0}
|
||||
|
||||
.settings-dialog legend{margin-bottom:9px;color:var(--muted);font-size:13px}
|
||||
|
||||
.color-grid,.shape-grid{display:flex;gap:10px;flex-wrap:wrap}
|
||||
|
||||
.color-grid button,.custom-color{position:relative;box-sizing:border-box;width:34px;height:34px;border:2px solid transparent;border-radius:50%;cursor:pointer}
|
||||
|
||||
.color-grid button.selected{outline:2px solid var(--ink);outline-offset:2px}
|
||||
|
||||
.custom-color{display:grid;place-items:center;overflow:hidden;border:1px dashed var(--muted)}
|
||||
|
||||
.custom-color input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
|
||||
|
||||
.custom-color span{font-size:20px}
|
||||
|
||||
.shape-grid button{display:grid;place-items:center;width:54px;height:54px;border:1px solid var(--border);border-radius:12px;background:transparent}
|
||||
|
||||
.shape-grid button.selected{border-color:var(--accent);background:rgba(62,197,168,.08)}
|
||||
|
||||
.settings-dialog textarea{box-sizing:border-box;width:100%;resize:vertical;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:11px;font:inherit;outline:none}
|
||||
|
||||
.settings-dialog textarea:focus{border-color:var(--accent)}
|
||||
|
||||
|
||||
@keyframes thinking-dot{0%,60%,100%{opacity:.35;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}
|
||||
|
||||
|
||||
@keyframes spin{to{transform:rotate(360deg)}}
|
||||
|
||||
|
||||
@keyframes magic-orbit{to{transform:rotate(360deg)}}
|
||||
|
||||
|
||||
@keyframes avatar-rainbow-glow{0%,100%{filter:drop-shadow(-3px -1px 2px #ff4fd8) drop-shadow(3px 1px 2px #34d9ff)}33%{filter:drop-shadow(1px -3px 2px #ffe66d) drop-shadow(-1px 3px 2px #58f39a)}66%{filter:drop-shadow(3px -1px 2px #ff7a59) drop-shadow(-3px 1px 2px #7c5cff)}}
|
||||
|
||||
|
||||
@keyframes small-magic-line{to{background-position:200% 0}}
|
||||
|
||||
|
||||
.shape-grid button{border-color:transparent;border-radius:50%}
|
||||
|
||||
.shape-grid button:hover{background:rgba(255,255,255,.04)}
|
||||
|
||||
.shape-grid button.selected{border-color:#626268;box-shadow:none;background:rgba(255,255,255,.025)}
|
||||
|
||||
|
||||
.expr-grid,.bg-grid{display:flex;gap:8px;flex-wrap:wrap}
|
||||
|
||||
|
||||
.expr-grid button,.bg-grid button{display:grid;justify-items:center;gap:3px;min-width:54px;padding:6px 4px 7px;border:1px solid transparent;border-radius:12px;background:transparent;color:var(--muted);font:inherit;font-size:10px;cursor:pointer}
|
||||
|
||||
|
||||
.expr-grid button:hover,.bg-grid button:hover{background:rgba(255,255,255,.04)}
|
||||
|
||||
|
||||
.expr-grid button.selected,.bg-grid button.selected{border-color:#626268;color:var(--ink);background:rgba(255,255,255,.025)}
|
||||
|
||||
|
||||
.bot-list{flex:1;min-height:0}
|
||||
|
||||
|
||||
.sidebar-bottom{display:flex;flex-direction:column;align-items:stretch;gap:4px;padding-top:10px}
|
||||
|
||||
.sidebar-bottom .account{min-width:0;flex:1}
|
||||
|
||||
|
||||
.account-wrap{position:relative;width:100%}
|
||||
|
||||
|
||||
.account{cursor:pointer}
|
||||
|
||||
|
||||
.workspace-avatar{display:grid;width:32px;height:32px;flex:0 0 32px;place-items:center;border-radius:50%;background:var(--surface);color:var(--muted);font-size:11px;font-weight:650;letter-spacing:.02em;box-shadow:inset 0 0 0 1px rgba(255,255,255,.05)}
|
||||
|
||||
|
||||
.account:hover,.account.open{background:var(--hairline)}
|
||||
|
||||
|
||||
.account .chevron{width:16px;height:16px;color:var(--muted);margin-left:auto;flex:0 0 16px;transition:transform .15s ease}
|
||||
|
||||
|
||||
.account.open .chevron{transform:rotate(180deg)}
|
||||
|
||||
|
||||
.account-menu{position:absolute;z-index:50;left:0;right:0;bottom:calc(100% + 8px);display:grid;padding:6px;border:1px solid var(--border);border-radius:14px;background:var(--menu);box-shadow:0 18px 50px rgba(0,0,0,.55)}
|
||||
|
||||
|
||||
.account-menu button{display:flex;align-items:center;gap:10px;height:40px;padding:0 12px;border:0;border-radius:9px;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.account-menu button:hover{background:rgba(255,255,255,.07)}
|
||||
|
||||
|
||||
.account-menu button:disabled{opacity:.35;cursor:not-allowed}
|
||||
|
||||
|
||||
.account-menu button svg{width:16px;height:16px;color:var(--muted);flex:0 0 16px}
|
||||
|
||||
|
||||
.account-menu hr{width:100%;margin:4px 0;border:0;border-top:1px solid var(--border)}
|
||||
|
||||
|
||||
.share-url{display:block;padding:10px 12px;border-radius:10px;background:var(--input);color:var(--ink);font-size:13px;word-break:break-all}
|
||||
|
||||
|
||||
.about-brand{display:grid;justify-items:center;gap:6px;padding:8px 0 4px}
|
||||
|
||||
|
||||
.about-brand small{color:var(--muted)}
|
||||
|
||||
|
||||
.help-dialog{width:min(520px,100%);max-height:min(80dvh,720px)}
|
||||
|
||||
|
||||
.help-body{display:grid;gap:14px;overflow:auto;max-height:min(56dvh,480px);padding-right:4px}
|
||||
|
||||
|
||||
.help-body section h3{margin:0 0 4px;font-size:14px}
|
||||
|
||||
|
||||
.help-body section p{margin:0;color:var(--muted);line-height:1.55}
|
||||
|
||||
|
||||
.dialog label.memory-toggle{display:flex;align-items:center;gap:10px;color:var(--ink);font-size:14px}
|
||||
|
||||
|
||||
.dialog label.memory-toggle input[type="checkbox"]{width:16px;height:16px;min-height:16px;padding:0;flex:0 0 16px;border-radius:4px;accent-color:var(--accent)}
|
||||
|
||||
|
||||
.plugin-row,.plugin-server{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;border:0;border-radius:12px;background:transparent;color:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.plugin-row:hover,.plugin-server:hover,.plugin-row.selected{background:var(--hairline)}
|
||||
|
||||
|
||||
.plugin-icon{width:28px;height:28px;display:grid;place-items:center;border-radius:9px;background:rgba(255,255,255,.06);color:var(--muted);flex:0 0 28px}
|
||||
|
||||
|
||||
.plugin-icon svg{width:15px;height:15px}
|
||||
|
||||
|
||||
.plugin-row .bot-copy,.plugin-server .bot-copy{flex:1;min-width:0}
|
||||
|
||||
|
||||
.plugin-server{padding:7px 10px 7px 12px}
|
||||
|
||||
|
||||
.mcp-dot{width:8px;height:8px;border-radius:50%;background:var(--faint);flex:0 0 8px}
|
||||
|
||||
|
||||
.mcp-dot.connected{background:var(--online);box-shadow:0 0 8px color-mix(in srgb,var(--online) 60%,transparent)}
|
||||
|
||||
|
||||
.mcp-dot.disconnected{background:var(--danger)}
|
||||
|
||||
|
||||
.mcp-dot.disabled{background:var(--faint)}
|
||||
|
||||
|
||||
.mcp-pane{display:grid;gap:14px;min-height:0;overflow:auto;padding-bottom:8px}
|
||||
|
||||
|
||||
.mcp-add{display:grid;gap:10px}
|
||||
|
||||
|
||||
.mcp-transport{display:flex;gap:6px}
|
||||
|
||||
|
||||
.mcp-transport button{height:32px;padding:0 12px;border:1px solid var(--border);border-radius:9px;background:transparent;color:var(--muted);cursor:pointer}
|
||||
|
||||
|
||||
.mcp-transport button.picked{border-color:var(--accent);color:var(--ink);background:rgba(62,197,168,.08)}
|
||||
|
||||
|
||||
.mcp-list{display:grid;gap:8px}
|
||||
|
||||
|
||||
.mcp-card{border:1px solid var(--border);border-radius:12px;background:var(--inset);overflow:hidden}
|
||||
|
||||
|
||||
.mcp-card-head{display:flex;align-items:center;gap:8px;width:100%;padding:10px 12px;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.mcp-card-head strong{flex:1}
|
||||
|
||||
|
||||
.mcp-card-head small{color:var(--muted)}
|
||||
|
||||
|
||||
.mcp-error{padding:0 12px 10px;color:var(--danger-soft);font-size:12px;line-height:1.4}
|
||||
|
||||
|
||||
.mcp-tools{display:grid;gap:6px;padding:0 12px 10px}
|
||||
|
||||
|
||||
.mcp-tool{display:grid;gap:2px;padding:8px;border-radius:8px;background:rgba(255,255,255,.03)}
|
||||
|
||||
|
||||
.mcp-tool code{font-size:12px;color:var(--accent)}
|
||||
|
||||
|
||||
.mcp-tool span{color:var(--muted);font-size:12px}
|
||||
|
||||
|
||||
.mcp-card-actions{display:flex;flex-wrap:wrap;gap:6px;padding:0 12px 12px}
|
||||
|
||||
|
||||
.mcp-choose{display:inline-flex;align-items:center;gap:8px;width:fit-content}
|
||||
|
||||
.mcp-choose svg{width:16px;height:16px}
|
||||
|
||||
|
||||
.mcp-picker{width:min(760px,calc(100vw - 28px));max-height:min(800px,calc(100dvh - 24px));display:flex;flex-direction:column;gap:12px;overflow:hidden;min-height:0}
|
||||
|
||||
|
||||
.mcp-picker .dialog-title,.mcp-picker .dialog-lead,.mcp-picker .dialog-actions,.mcp-picker .pane-error{flex:0 0 auto}
|
||||
|
||||
|
||||
.dialog label.mcp-picker-search,.mcp-picker-search{display:flex;align-items:center;gap:10px;height:44px;margin:0;padding:0 13px;border:1px solid var(--line);border-radius:14px;background:var(--elevated);color:var(--muted);font-size:inherit;flex:0 0 44px}
|
||||
|
||||
|
||||
.mcp-picker-search>div{flex:0 0 auto;display:grid;place-items:center;line-height:0}
|
||||
|
||||
|
||||
.dialog .mcp-picker-search input,.mcp-picker-search input{width:100%;height:auto;min-height:0;padding:0;border:0;border-radius:0;background:transparent;color:var(--ink)}
|
||||
|
||||
|
||||
.dialog .mcp-picker-search input:focus{border-color:transparent}
|
||||
|
||||
|
||||
.mcp-picker-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));grid-auto-rows:min-content;gap:8px;flex:1 1 auto;min-height:0;overflow:auto;padding:2px 6px 8px 0;align-content:start;scrollbar-gutter:stable}
|
||||
|
||||
|
||||
.mcp-picker-grid::-webkit-scrollbar{width:8px}
|
||||
|
||||
|
||||
.mcp-picker-grid::-webkit-scrollbar-thumb{background:#2a2a2e;border-radius:999px}
|
||||
|
||||
|
||||
.mcp-picker-empty{grid-column:1/-1;padding:28px 8px;color:var(--muted);text-align:center}
|
||||
|
||||
|
||||
.mcp-pick-card{display:grid;gap:6px;align-content:start;min-height:min-content;padding:12px;border:1px solid var(--border);border-radius:14px;background:var(--inset);color:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.mcp-pick-card:hover:not(.added):not(:disabled){border-color:var(--accent)}
|
||||
|
||||
|
||||
.mcp-pick-card.added,.mcp-pick-card:disabled{opacity:.45;cursor:default}
|
||||
|
||||
|
||||
.mcp-pick-icon{width:32px;height:32px;display:grid;place-items:center;border-radius:9px;background:rgba(62,197,168,.12);color:var(--accent);font-weight:700}
|
||||
|
||||
|
||||
.mcp-pick-card strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
|
||||
.mcp-pick-card small{color:var(--muted);font-size:12px;line-height:1.35;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
||||
|
||||
|
||||
.mcp-pick-meta{color:var(--faint);font-size:11px}
|
||||
|
||||
|
||||
.mcp-secret-step,.mcp-picker .mcp-add{display:grid;gap:10px;flex:1 1 auto;min-height:0;overflow:auto;align-content:start}
|
||||
|
||||
|
||||
.mcp-picker .mcp-back{width:fit-content;justify-self:start}
|
||||
|
||||
|
||||
.mcp-card-actions svg{width:13px;height:13px}
|
||||
|
||||
|
||||
.create-menu-wrap{position:relative;margin-left:auto}
|
||||
|
||||
.brand .create-menu-wrap .icon-button{margin-left:0}
|
||||
|
||||
.create-menu{position:absolute;z-index:40;top:42px;right:0;display:grid;width:180px;padding:6px;border:1px solid var(--border);border-radius:12px;background:var(--menu);box-shadow:0 18px 50px rgba(0,0,0,.5)}
|
||||
|
||||
.create-menu button{display:flex;align-items:center;gap:9px;height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--ink);cursor:pointer}
|
||||
|
||||
.create-menu button:hover:not(:disabled){background:rgba(255,255,255,.07)}
|
||||
|
||||
.create-menu button:disabled{opacity:.4;cursor:not-allowed}
|
||||
|
||||
.create-menu svg{width:16px}
|
||||
|
||||
|
||||
.group-picker{display:grid;gap:5px;max-height:280px;margin:0;padding:0;overflow:auto;border:0}
|
||||
|
||||
.group-picker legend{margin-bottom:8px;color:var(--muted)}
|
||||
|
||||
.group-picker label{display:flex;align-items:center;gap:10px;padding:7px 9px;border-radius:9px;background:var(--inset);cursor:pointer}
|
||||
|
||||
.group-picker input{width:16px;height:16px;margin:0}
|
||||
|
||||
.group-picker .avatar{--avatar-size:28px!important}
|
||||
|
||||
|
||||
.session-picker{position:relative;display:flex;align-items:center;gap:4px;margin-left:10px;min-width:0}
|
||||
|
||||
|
||||
.session-current{display:flex;align-items:center;gap:6px;min-width:0;max-width:220px;height:36px;padding:0 10px;border:1px solid var(--border);border-radius:9px;background:var(--surface);color:var(--ink);cursor:pointer}
|
||||
|
||||
|
||||
.session-current span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
|
||||
.session-current svg{flex:0 0 14px;width:14px;height:14px;color:var(--muted)}
|
||||
|
||||
|
||||
.session-menu{position:absolute;z-index:40;top:42px;left:0;display:grid;width:min(280px,calc(100vw - 24px));padding:6px;border:1px solid var(--border);border-radius:13px;background:var(--menu);box-shadow:0 18px 60px rgba(0,0,0,.55)}
|
||||
|
||||
|
||||
.session-menu>button{display:flex;align-items:center;gap:10px;width:100%;height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.session-menu>button:hover:not(:disabled){background:rgba(255,255,255,.07)}
|
||||
|
||||
|
||||
.session-menu>button:disabled{opacity:.4;cursor:not-allowed}
|
||||
|
||||
|
||||
.session-menu svg{width:16px;height:16px}
|
||||
|
||||
|
||||
.session-menu hr{width:100%;margin:5px 0;border:0;border-top:1px solid var(--border)}
|
||||
|
||||
|
||||
.session-menu-list{display:grid;max-height:240px;overflow:auto}
|
||||
|
||||
|
||||
.session-item{display:flex;align-items:center;gap:2px;border-radius:8px}
|
||||
|
||||
|
||||
.session-item.selected{background:rgba(255,255,255,.06)}
|
||||
|
||||
|
||||
.session-item-main{display:grid;min-width:0;flex:1;height:42px;padding:0 10px;border:0;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}
|
||||
|
||||
|
||||
.session-item-main strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}
|
||||
|
||||
|
||||
.session-item-main time{color:var(--muted);font-size:11px}
|
||||
|
||||
|
||||
.session-item .icon-button{flex:0 0 32px;width:32px;height:32px;color:var(--muted)}
|
||||
|
||||
|
||||
.session-menu .danger-item{color:var(--danger-soft)}
|
||||
|
||||
|
||||
.queue-hint{position:absolute;bottom:118px;left:50%;z-index:7;transform:translateX(-50%);color:var(--muted);font-size:12px}
|
||||
|
||||
|
||||
.error-banner{z-index:8;bottom:118px}
|
||||
|
||||
|
||||
.pause-banner{display:flex;align-items:center;gap:14px;width:min(760px,100%);margin:4px auto 18px;padding:12px 14px;border:1px solid var(--warn-border);border-radius:12px;background:var(--warn-bg);color:var(--warn-fg);font-size:13px;line-height:1.5}
|
||||
|
||||
|
||||
.pause-banner span{flex:1}
|
||||
|
||||
|
||||
.pause-banner .primary{flex:0 0 auto;white-space:nowrap}
|
||||
|
||||
|
||||
.remember-msg{display:none}
|
||||
|
||||
|
||||
.panel-open-btn{display:none}
|
||||
|
||||
|
||||
.side-card-backdrop{display:none}
|
||||
|
||||
|
||||
.app-shell.right-open{grid-template-columns:clamp(220px,18vw,280px) minmax(0,1fr) clamp(320px,30vw,440px)}
|
||||
|
||||
.app-shell.meeting-mode,.app-shell.meeting-mode.right-open,.app-shell.meeting-mode.right-collapsed{
|
||||
grid-template-columns:clamp(200px,16vw,260px) minmax(0,1fr) minmax(300px,380px);
|
||||
grid-template-areas:"nav stage chat";
|
||||
}
|
||||
.app-shell.meeting-mode .sidebar{grid-area:nav}
|
||||
.app-shell.meeting-mode .meeting-stage{grid-area:stage;border-right:0}
|
||||
.app-shell.meeting-mode .chat-panel{grid-area:chat;--chat-gutter:14px;--chat-col:100%;border-left:1px solid var(--hairline);min-width:0}
|
||||
.app-shell.meeting-mode .chat-panel .topbar{
|
||||
display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;
|
||||
height:auto;min-height:0;flex:0 0 auto;padding:8px 10px;
|
||||
}
|
||||
.app-shell.meeting-mode .chat-panel .topbar>.grow{display:none}
|
||||
.app-shell.meeting-mode .chat-panel .topbar>strong{min-width:0;flex:1 1 80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.app-shell.meeting-mode .chat-panel .topbar>.host-chip-wrap{flex:0 1 auto;max-width:100%}
|
||||
.app-shell.meeting-mode .host-chip{max-width:96px}
|
||||
.app-shell.meeting-mode .session-picker{margin-left:0;flex:0 1 auto}
|
||||
.app-shell.meeting-mode .session-current{max-width:min(100%,140px)}
|
||||
.app-shell.meeting-mode .chat-panel .topbar>.top-tools{
|
||||
flex:1 0 100%;width:100%;margin:0;justify-content:space-between;
|
||||
box-sizing:border-box;padding:3px;overflow:hidden;
|
||||
}
|
||||
.app-shell.meeting-mode .top-tool-button{width:32px;height:32px;flex:0 0 32px}
|
||||
.app-shell.meeting-mode .call-entry{flex:0 0 auto}
|
||||
.app-shell.meeting-mode .messages{padding-left:var(--chat-gutter);padding-right:var(--chat-gutter);min-width:0}
|
||||
.app-shell.meeting-mode .composer-dock{padding-left:var(--chat-gutter);padding-right:var(--chat-gutter)}
|
||||
.app-shell.meeting-mode .side-card-backdrop{display:block;position:fixed;inset:0;z-index:44;background:rgba(0,0,0,.5);grid-area:stage}
|
||||
.app-shell.meeting-mode .side-card{position:fixed;z-index:45;inset:0 0 0 auto;width:min(420px,100vw);grid-area:stage;box-shadow:-20px 0 60px #000}
|
||||
|
||||
|
||||
.app-shell.right-collapsed{grid-template-columns:clamp(220px,18vw,280px) minmax(0,1fr) 52px}
|
||||
|
||||
|
||||
.side-card{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--panel);border-right:0}
|
||||
|
||||
|
||||
.side-card.collapsed{padding:10px 6px}
|
||||
|
||||
|
||||
.side-rail{display:flex;flex-direction:column;align-items:center;gap:6px;height:100%}
|
||||
|
||||
|
||||
.side-rail button{width:40px;height:40px;display:grid;place-items:center;border:0;border-radius:11px;background:transparent;color:var(--muted);cursor:pointer}
|
||||
|
||||
|
||||
.side-rail button svg{width:18px;height:18px}
|
||||
|
||||
|
||||
.side-rail button:hover:not(:disabled){background:var(--surface);color:var(--ink)}
|
||||
|
||||
|
||||
.side-rail button.active{background:var(--surface);color:var(--ink)}
|
||||
|
||||
|
||||
.side-rail button:disabled{opacity:.35;cursor:not-allowed}
|
||||
|
||||
|
||||
.side-card-head{display:flex;align-items:center;gap:6px;height:72px;flex:0 0 72px;padding:0 10px 0 12px;border-bottom:1px solid var(--hairline)}
|
||||
|
||||
|
||||
.side-card-title{min-width:0;flex:1;overflow:hidden;color:var(--muted);font-size:13px;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
|
||||
.side-tabs{display:flex;gap:4px;min-width:0;flex:1;flex-wrap:wrap}
|
||||
|
||||
|
||||
.side-tabs button{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 10px;border:0;border-radius:10px;background:transparent;color:var(--muted);font:inherit;font-size:13px;cursor:pointer}
|
||||
|
||||
|
||||
.side-tabs button svg{width:15px;height:15px}
|
||||
|
||||
|
||||
.side-tabs button:hover:not(:disabled){background:var(--surface);color:var(--ink)}
|
||||
|
||||
|
||||
.side-tabs button.active{background:var(--surface);color:var(--ink)}
|
||||
|
||||
|
||||
.side-tabs button:disabled{opacity:.35;cursor:not-allowed}
|
||||
|
||||
|
||||
.side-card-body{display:flex;flex-direction:column;min-height:0;flex:1;padding:14px 16px 16px;overflow:hidden}
|
||||
|
||||
|
||||
.pane-form,.memory-pane,.vault-pane{display:grid;gap:14px;min-height:0;overflow:auto;padding-bottom:8px;align-content:start}
|
||||
|
||||
|
||||
.pane-form label,.memory-pane label,.mcp-add label,.vault-pane label{display:grid;gap:8px;color:var(--muted);font-size:13px;min-width:0}
|
||||
|
||||
|
||||
.pane-form input:not([type="checkbox"]):not([type="color"]),.memory-pane input:not([type="checkbox"]),.mcp-add input,.vault-pane input{width:100%;min-width:0;height:40px;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:0 12px;outline:0}
|
||||
|
||||
|
||||
.pane-form textarea,.memory-pane textarea,.mcp-add textarea{box-sizing:border-box;width:100%;resize:vertical;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:11px;font:inherit;outline:none}
|
||||
|
||||
|
||||
.pane-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
|
||||
|
||||
|
||||
.pane-error{color:var(--danger-soft);font-size:13px}
|
||||
|
||||
|
||||
.settings-pane fieldset{margin:0;padding:0;border:0}
|
||||
|
||||
|
||||
.settings-pane legend{margin-bottom:9px;color:var(--muted);font-size:13px}
|
||||
|
||||
|
||||
.memory-pane form{display:flex;gap:10px;align-items:end}
|
||||
|
||||
|
||||
.memory-pane form label{flex:1}
|
||||
|
||||
|
||||
.memory-help{margin:0;color:var(--muted);line-height:1.5}
|
||||
|
||||
|
||||
.memory-pane .memory-toggle,.memory-toggle{display:flex;align-items:center;gap:8px}
|
||||
|
||||
|
||||
.memory-search{display:grid;gap:8px}
|
||||
|
||||
|
||||
.memory-clear-confirm{display:flex;align-items:center;gap:8px;margin-right:auto;color:var(--muted);font-size:13px}
|
||||
|
||||
|
||||
.login-screen{position:relative;display:grid;min-height:100%;min-height:100dvh;place-items:center;padding:72px 24px 64px;background:radial-gradient(circle at 50% 18%,#16241f 0,#080809 58%)}
|
||||
|
||||
.login-dialog{width:min(320px,100%);display:grid;justify-items:stretch;gap:14px}
|
||||
|
||||
.login-dialog>.avatar{justify-self:center;margin:0 0 10px}
|
||||
|
||||
.login-dialog h1{margin:0 0 8px;text-align:center;font-size:20px;font-weight:650;letter-spacing:.01em}
|
||||
|
||||
.login-dialog label{display:grid;gap:6px;color:var(--muted);font-size:13px}
|
||||
|
||||
.login-dialog input{height:44px;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:0 12px;outline:0}
|
||||
|
||||
.login-dialog input:focus{border-color:var(--focus)}
|
||||
|
||||
.login-dialog .primary{height:44px;margin-top:4px;justify-content:center}
|
||||
|
||||
.login-error{color:var(--danger-soft);font-size:13px}
|
||||
|
||||
.login-switch{min-height:0;justify-self:center;margin-top:2px;padding:8px;border:0;border-radius:8px;background:transparent;color:var(--muted);font-size:13px;cursor:pointer}
|
||||
|
||||
.login-switch:hover{color:var(--ink)}
|
||||
|
||||
.login-switch:disabled{opacity:.35;cursor:not-allowed}
|
||||
|
||||
.login-lang{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:4px}
|
||||
|
||||
.login-lang button{height:auto;padding:6px 8px;border:0;border-radius:8px;background:transparent;color:var(--faint);cursor:pointer;font-size:13px}
|
||||
|
||||
.login-lang button:hover{color:var(--muted)}
|
||||
|
||||
.login-lang button.picked{color:var(--ink);font-weight:600}
|
||||
|
||||
@media(max-width:700px){
|
||||
.login-screen{padding:56px 20px 40px}
|
||||
.login-dialog{width:min(360px,100%)}
|
||||
.login-lang{top:12px;right:8px}
|
||||
.login-lang button{min-height:44px;padding:8px 10px}
|
||||
}
|
||||
|
||||
|
||||
@keyframes working-shimmer{to{background-position:-220% 0}}
|
||||
|
||||
|
||||
.bot-row .unread-dot{top:-2px;right:-3px}
|
||||
|
||||
|
||||
.bot-row:hover .avatar-wrap{transform:scale(1.06)}
|
||||
|
||||
|
||||
.bot-row.room-row .bot-copy small{color:var(--muted)}
|
||||
|
||||
|
||||
.dialog-lead{margin:0;color:var(--muted);line-height:1.5;font-size:13px}
|
||||
/* One quiet sentence under a form: what will happen by default, so the human has nothing to configure. */
|
||||
.dialog-note{margin:-4px 0 0;color:var(--muted);font-size:12px;line-height:1.5}
|
||||
|
||||
|
||||
@keyframes computer-hud-bob{0%,100%{transform:translateY(0) rotate(-4deg)}50%{transform:translateY(-3px) rotate(5deg)}}
|
||||
|
||||
|
||||
|
||||
/* Collapsed navigation is rendered in the top bar by App. Keep this fallback
|
||||
for older markup and make both variants feel like one compact tool strip. */
|
||||
.top-tools{display:flex;align-items:center;gap:4px;margin-left:auto;padding:4px;border:1px solid var(--border);border-radius:13px;background:rgba(18,18,20,.86);box-shadow:0 8px 24px rgba(0,0,0,.2)}
|
||||
|
||||
|
||||
.top-tool-button{width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:9px;background:transparent;color:var(--muted);cursor:pointer;transition:background .15s ease,color .15s ease,transform .15s ease}
|
||||
|
||||
|
||||
.top-tool-button svg{width:17px;height:17px}
|
||||
|
||||
|
||||
.top-tool-button:hover:not(:disabled){background:var(--surface);color:var(--ink);transform:translateY(-1px)}
|
||||
|
||||
|
||||
.top-tool-button.active{background:var(--surface);color:var(--ink);box-shadow:inset 0 0 0 1px rgba(255,255,255,.06)}
|
||||
|
||||
|
||||
.top-tool-button:disabled{opacity:.3;cursor:not-allowed}
|
||||
|
||||
|
||||
.app-shell.right-collapsed{grid-template-columns:clamp(220px,18vw,280px) minmax(0,1fr)}
|
||||
|
||||
|
||||
.side-rail{flex-direction:row;align-items:center;justify-content:flex-start;gap:4px;height:auto;padding:6px}
|
||||
|
||||
|
||||
.side-rail .grow{display:none}
|
||||
|
||||
|
||||
.side-rail button{width:34px;height:34px;border-radius:9px}
|
||||
|
||||
|
||||
.side-rail button svg{width:17px;height:17px}
|
||||
|
||||
|
||||
.provider-fieldset{margin:0;padding:0;border:0}
|
||||
|
||||
.provider-fieldset legend{margin-bottom:8px;color:var(--muted);font-size:13px}
|
||||
|
||||
|
||||
.provider-grid{display:flex;gap:6px;flex-wrap:wrap}
|
||||
|
||||
|
||||
.provider-grid button{height:36px;padding:0 12px;border:1px solid var(--border);border-radius:10px;background:transparent;color:var(--muted);cursor:pointer}
|
||||
|
||||
|
||||
.provider-grid button.picked{border-color:var(--accent);color:var(--ink);background:rgba(62,197,168,.08)}
|
||||
|
||||
|
||||
.dialog.settings-dialog{width:min(520px,calc(100vw - 28px));max-height:min(850px,calc(100dvh - 28px));overflow:auto}
|
||||
|
||||
|
||||
.dialog.settings-dialog select{width:100%;height:42px;padding:0 12px;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink)}
|
||||
|
||||
|
||||
/* Teach-by-demonstration: composer menu, live banner, draft card */
|
||||
.plus-menu-wrap{position:relative;align-self:center}
|
||||
|
||||
|
||||
.plus-menu{position:absolute;z-index:40;bottom:50px;left:0;display:grid;width:268px;padding:6px;border:1px solid var(--border);border-radius:13px;background:var(--menu);box-shadow:0 18px 60px rgba(0,0,0,.55)}
|
||||
|
||||
|
||||
.plus-menu button{display:flex;align-items:center;gap:10px;width:100%;height:38px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
||||
|
||||
|
||||
.plus-menu button:hover:not(:disabled){background:rgba(255,255,255,.07)}
|
||||
|
||||
|
||||
.plus-menu button:disabled{opacity:.4;cursor:not-allowed}
|
||||
|
||||
|
||||
.plus-menu svg{width:16px;height:16px;flex:0 0 16px}
|
||||
|
||||
|
||||
.plus-menu hr{width:100%;margin:5px 0;border:0;border-top:1px solid var(--border)}
|
||||
|
||||
|
||||
.plus-menu-label{padding:4px 10px 2px;color:var(--faint);font-size:11px}
|
||||
|
||||
|
||||
.plus-menu-skills{display:grid;min-height:0}
|
||||
|
||||
|
||||
.plus-menu-search{box-sizing:border-box;width:calc(100% - 8px);height:32px;margin:4px 4px 6px;padding:0 10px;border:1px solid var(--border);border-radius:8px;background:var(--elevated);color:var(--ink);font:inherit;font-size:13px;outline:0}
|
||||
|
||||
|
||||
.plus-menu-search:focus{border-color:var(--focus)}
|
||||
|
||||
|
||||
.plus-menu-skill-list{overflow-y:auto;max-height:min(228px,calc(100dvh - 280px));padding-bottom:2px}
|
||||
|
||||
|
||||
.plus-menu-skill-list::-webkit-scrollbar{width:8px}
|
||||
|
||||
|
||||
.plus-menu-skill-list::-webkit-scrollbar-thumb{border-radius:8px;background:#2f2f33}
|
||||
|
||||
|
||||
.plus-menu-empty{display:block;padding:10px 12px 12px;color:var(--muted);font-size:12px}
|
||||
|
||||
|
||||
.record-dot{display:inline-block;flex:0 0 12px;width:12px;height:12px;border-radius:50%;background:var(--danger);box-shadow:inset 0 0 0 2px var(--menu),0 0 0 1.5px var(--danger)}
|
||||
|
||||
|
||||
.record-dot.live{animation:pulse 1.2s infinite;margin-right:8px;vertical-align:-1px}
|
||||
|
||||
|
||||
.teach-dialog h2{display:flex;align-items:center;gap:10px}
|
||||
|
||||
|
||||
.teach-goal{min-height:78px;resize:vertical;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:10px 12px;outline:0;line-height:1.5}
|
||||
|
||||
|
||||
.teach-goal:focus{border-color:var(--focus)}
|
||||
|
||||
|
||||
.teach-tips{margin:0;padding-left:18px;color:var(--muted);font-size:12.5px;line-height:1.6}
|
||||
|
||||
|
||||
.teach-banner{display:flex;align-items:center;gap:12px;width:min(760px,100%);margin:4px auto 18px;padding:12px 14px;border:1px solid #3d2a2a;border-radius:12px;background:#1d1212;color:#f3c5c5;font-size:13px;line-height:1.5}
|
||||
|
||||
|
||||
.teach-banner.recording{border-color:#6a2a2a;background:#241313}
|
||||
|
||||
|
||||
.teach-banner>span{flex:1;display:grid;gap:2px}
|
||||
|
||||
|
||||
.teach-banner small{color:var(--muted)}
|
||||
|
||||
|
||||
.teach-banner .primary,.teach-banner .outline{flex:0 0 auto;white-space:nowrap}
|
||||
|
||||
|
||||
.skill-draft{display:grid;gap:12px;width:min(760px,100%);margin:4px auto 18px;padding:16px 18px;border:1px solid #2f4a42;border-radius:14px;background:#0f1815;font-size:13.5px;line-height:1.55}
|
||||
|
||||
|
||||
.skill-draft header{display:flex;align-items:center;gap:9px;color:var(--accent)}
|
||||
|
||||
|
||||
.skill-draft header svg{width:16px;height:16px}
|
||||
|
||||
|
||||
.skill-draft header small{margin-left:auto;color:var(--muted);font-weight:400}
|
||||
|
||||
|
||||
.skill-draft label{display:grid;gap:6px;color:var(--muted);font-size:12.5px}
|
||||
|
||||
|
||||
.skill-draft input,.skill-draft textarea{border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:9px 12px;outline:0;font:inherit;line-height:1.5}
|
||||
|
||||
|
||||
.skill-draft input:focus,.skill-draft textarea:focus{border-color:var(--focus)}
|
||||
|
||||
|
||||
.skill-draft textarea{resize:vertical}
|
||||
|
||||
|
||||
.skill-draft p{margin:0;color:var(--ink)}
|
||||
|
||||
|
||||
.skill-intent{color:#d8d8dc}
|
||||
|
||||
|
||||
.skill-inputs code{margin-right:6px;padding:1px 7px;border-radius:6px;background:rgba(62,197,168,.14);color:var(--accent);font-size:12px}
|
||||
|
||||
|
||||
.skill-steps ol{margin:0;padding-left:22px;color:var(--ink);cursor:text}
|
||||
|
||||
|
||||
.skill-steps li{margin:2px 0}
|
||||
|
||||
|
||||
.skill-steps li.more,.skill-check{color:var(--muted)}
|
||||
|
||||
|
||||
.skill-draft .link{width:fit-content;padding:0;border:0;background:none;color:var(--accent);font-size:12px;cursor:pointer}
|
||||
|
||||
|
||||
.skill-error{color:var(--error-fg);font-size:12.5px}
|
||||
|
||||
|
||||
.skill-actions{display:flex;align-items:center;gap:8px}
|
||||
|
||||
|
||||
.skill-actions .grow{flex:1}
|
||||
|
||||
|
||||
.skill-actions .link{display:inline-flex;align-items:center;gap:5px}
|
||||
|
||||
|
||||
.skill-actions .link svg{width:13px;height:13px}
|
||||
|
||||
|
||||
/* Saved-skill rows in the composer menu: run on the left, pencil on the right */
|
||||
.plus-menu-skill{display:flex;align-items:center;gap:2px}
|
||||
|
||||
|
||||
.plus-menu-skill>button:first-child{flex:1;min-width:0}
|
||||
|
||||
|
||||
.plus-menu-skill .skill-edit{flex:0 0 30px;width:30px;height:30px;padding:0;justify-content:center;color:var(--muted)}
|
||||
|
||||
|
||||
.plus-menu-skill .skill-edit:hover{color:var(--ink)}
|
||||
|
||||
|
||||
.plus-menu-skill .skill-edit svg{width:14px;height:14px;flex-basis:14px}
|
||||
|
||||
|
||||
.skill-import-input{position:absolute;left:0;bottom:0;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;opacity:0}
|
||||
|
||||
|
||||
/* Full skill editor */
|
||||
.dialog.skill-edit{width:min(600px,calc(100vw - 28px));max-height:calc(100dvh - 28px);overflow:auto}
|
||||
|
||||
|
||||
.dialog.skill-edit h2{display:flex;align-items:center;gap:9px}
|
||||
|
||||
|
||||
.dialog.skill-edit h2 svg{width:17px;height:17px;color:var(--accent)}
|
||||
|
||||
|
||||
.dialog.skill-edit label{display:grid;gap:6px;color:var(--muted);font-size:12.5px}
|
||||
|
||||
|
||||
.dialog.skill-edit input,.dialog.skill-edit textarea{width:100%;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:9px 12px;outline:0;font:inherit;font-size:13.5px;line-height:1.5;resize:vertical}
|
||||
|
||||
|
||||
.dialog.skill-edit input:focus,.dialog.skill-edit textarea:focus{border-color:var(--focus)}
|
||||
|
||||
|
||||
.dialog.skill-edit .dialog-actions{align-items:center;gap:8px;flex-wrap:wrap}
|
||||
|
||||
|
||||
.dialog.skill-edit .dialog-actions .grow{flex:1}
|
||||
|
||||
|
||||
.dialog.skill-edit .dialog-actions .outline svg{fill:none;stroke:currentColor;width:14px;height:14px}
|
||||
|
||||
|
||||
.vault-pane{flex:1;container-type:inline-size}
|
||||
|
||||
.vault-form{display:grid;gap:12px}
|
||||
|
||||
.vault-fields{display:grid;grid-template-columns:1fr 1fr;gap:10px 12px}
|
||||
|
||||
@container (max-width:310px){
|
||||
.vault-fields{grid-template-columns:1fr}
|
||||
}
|
||||
|
||||
.vault-form>.primary{justify-self:start}
|
||||
|
||||
.account-list{display:grid;gap:8px;margin-top:2px;padding-top:16px;border-top:1px solid var(--line)}
|
||||
|
||||
.vault-empty{margin:0;color:var(--faint);font-size:13px;line-height:1.5}
|
||||
|
||||
.account-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;padding:10px 6px 10px 12px;border:1px solid var(--border);border-radius:12px;background:var(--inset)}
|
||||
|
||||
.account-row-main{display:grid;gap:3px;min-width:0}
|
||||
|
||||
.account-row-main strong{overflow:hidden;font-size:14px;font-weight:400;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
.account-row-main small{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}
|
||||
|
||||
.account-row-main .account-notes{white-space:normal;overflow-wrap:anywhere}
|
||||
|
||||
.account-row .icon-button{color:var(--muted)}
|
||||
|
||||
.account-row .icon-button:hover:not(:disabled){color:var(--danger)}
|
||||
|
||||
|
||||
@keyframes monitor-breathe{50%{transform:translateY(-4px);box-shadow:0 0 28px #65dcb325}}
|
||||
|
||||
@keyframes monitor-orbit{to{transform:rotate(360deg)}}
|
||||
|
||||
@keyframes monitor-blink{0%,43%,49%,100%{transform:scaleY(1)}46%{transform:scaleY(.15)}}
|
||||
|
||||
.memory-row details{font-size:12px;color:var(--muted)}
|
||||
.memory-row summary{cursor:pointer;padding:4px 0}
|
||||
.memory-row dl{display:grid;grid-template-columns:auto minmax(0,1fr);gap:5px 10px;margin:8px 0}
|
||||
.memory-row dd{margin:0;overflow-wrap:anywhere}
|
||||
|
||||
.memory-target{display:flex;align-items:center;gap:10px;padding:12px 16px;font-size:13px;color:var(--muted)}
|
||||
.memory-target select{min-width:0;flex:1;background:var(--input);color:var(--ink);border:1px solid var(--border);border-radius:8px;padding:6px}
|
||||
.memory-destinations{display:flex;gap:10px;flex-wrap:wrap}
|
||||
|
||||
.run-memory-usage{display:block;margin-top:6px}
|
||||
.run-memory-usage button{font-size:11px;min-height:26px;padding:3px 8px}
|
||||
.run-memory-list{display:grid;gap:8px;margin-top:8px}
|
||||
.run-memory-item{display:block;border-left:2px solid var(--border);padding-left:8px}
|
||||
.run-memory-item>span{display:block;white-space:pre-wrap;overflow-wrap:anywhere}
|
||||
|
||||
.message-run-details{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:var(--muted);cursor:pointer}
|
||||
.message-run-details svg{width:13px;height:13px}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
@media(max-width:1050px){.app-shell{grid-template-columns:250px 1fr}.computer-panel{display:none}}
|
||||
@media(max-width:700px){.app-shell{display:block}.sidebar{position:fixed;z-index:40;inset:0 auto 0 0;width:min(300px,88vw);transform:translateX(-105%);transition:.2s;box-shadow:20px 0 60px #000}.sidebar.open{transform:none}.chat-panel{height:100%}.mobile-menu{display:inline-flex}.topbar{padding:0 12px}.messages{padding:24px 18px 16px}.composer{left:12px;right:12px}.computer-overlay>header{height:auto;min-height:64px;flex-wrap:wrap;padding:10px}.computer-overlay>header>div:last-child{flex-wrap:wrap;justify-content:flex-end}.overlay-screen{padding:8px}.mode-grid{grid-template-columns:1fr}}
|
||||
@media(max-width:1200px){.app-shell{grid-template-columns:230px minmax(0,1fr) 330px}}
|
||||
@media(max-width:1050px){.app-shell{grid-template-columns:250px minmax(0,1fr)}}
|
||||
@media(max-width:700px){.chat-panel{--chat-gutter:18px}.app-shell{display:block}.message{width:100%}.day-divider{width:100%}.message>span,.message>.message-body,.message-stack{max-width:90%}.composer-dock{padding:20px var(--chat-gutter) 16px}.composer{width:var(--chat-col);min-height:60px}.composer-files{padding:2px 4px 8px 8px}.composer-reply{margin:2px 0 4px;padding-left:12px}.file-card{max-width:100%;height:52px}.file-card-remove{flex-basis:32px;width:32px;height:32px}.message.with-files .msg-attachments{max-width:100%}.messages{padding-bottom:16px}.context-menu{width:min(220px,calc(100vw - 24px))}.context-menu button{height:44px}}
|
||||
@media(prefers-reduced-motion:reduce){.avatar.blobatar.thinking:before,.avatar.blobatar.thinking:after,.thinking-dots i{animation:none!important}}
|
||||
@media(max-width:1200px){.app-shell.right-open{grid-template-columns:230px minmax(0,1fr) 340px}.app-shell.right-collapsed{grid-template-columns:230px minmax(0,1fr) 52px}.app-shell.meeting-mode,.app-shell.meeting-mode.right-open,.app-shell.meeting-mode.right-collapsed{grid-template-columns:200px minmax(0,1fr) 320px}}
|
||||
@media(max-width:1050px){
|
||||
.panel-open-btn{display:inline-flex}
|
||||
.app-shell.right-collapsed{grid-template-columns:250px minmax(0,1fr)}
|
||||
.app-shell.right-collapsed .side-card{display:none}
|
||||
.app-shell.right-open{grid-template-columns:250px minmax(0,1fr)}
|
||||
.app-shell.right-open .side-card-backdrop,.app-shell.stage-open:not(.meeting-mode) .side-card-backdrop{display:block;position:fixed;inset:0;z-index:44;background:rgba(0,0,0,.5)}
|
||||
.app-shell.right-open .side-card{position:fixed;z-index:45;inset:0 0 0 auto;width:min(420px,100vw);box-shadow:-20px 0 60px #000}
|
||||
.app-shell:not(.meeting-mode).stage-open .meeting-stage:not(.is-hidden){position:fixed;z-index:45;inset:0 0 0 auto;width:min(420px,100vw);box-shadow:-20px 0 60px #000}
|
||||
.app-shell.meeting-mode,.app-shell.meeting-mode.right-open,.app-shell.meeting-mode.right-collapsed{
|
||||
display:grid;grid-template-columns:minmax(0,1fr) minmax(280px,340px);grid-template-areas:"stage chat";
|
||||
}
|
||||
.app-shell.meeting-mode .sidebar{position:fixed;z-index:40;inset:0 auto 0 0;width:min(300px,88vw);transform:translateX(-105%);transition:.2s;box-shadow:20px 0 60px #000}
|
||||
.app-shell.meeting-mode .sidebar.open{transform:none}
|
||||
.app-shell.meeting-mode .mobile-menu{display:inline-flex}
|
||||
}
|
||||
@media(max-width:700px){
|
||||
.meeting-toggle{display:none!important}
|
||||
.meeting-stage{display:none!important}
|
||||
.app-shell.meeting-mode,.app-shell.meeting-mode.right-open,.app-shell.meeting-mode.right-collapsed{display:block;grid-template-areas:none;grid-template-columns:none}
|
||||
}
|
||||
@media(prefers-reduced-motion:reduce){.working-label{animation:none;color:var(--muted);background:none}}
|
||||
@media(prefers-reduced-motion:reduce){.computer-hud .avatar.blobatar,.computer-hud-label{animation:none!important}.computer-hud-label{color:var(--muted);background:none}}
|
||||
@media(max-width:700px){
|
||||
.topbar{gap:8px}
|
||||
.top-tools{gap:2px;padding:3px}
|
||||
.top-tool-button{width:32px;height:32px}
|
||||
.topbar>strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.session-picker{margin-left:2px}
|
||||
.session-current{max-width:min(32vw,150px)}
|
||||
.message.assistant.spoken{grid-template-columns:24px minmax(0,1fr);column-gap:8px}
|
||||
.computer-part .preview{max-height:none}
|
||||
}
|
||||
@media(prefers-reduced-motion:reduce){.computer-signal,.computer-signal::before,.computer-signal-face i,.avatar.blobatar.thinking,.avatar-stack .stack-item .avatar.thinking{animation:none!important}}
|
||||
|
||||
/* Mobile conversation drawer and two-row chat header. */
|
||||
.mobile-nav-backdrop,.mobile-nav-close{display:none}
|
||||
.brand-icon{width:30px;height:30px;flex:none;border-radius:9px;margin-right:9px}
|
||||
@media(max-width:700px){
|
||||
.mobile-nav-backdrop{display:block;position:fixed;inset:0;z-index:39;border:0;border-radius:0;
|
||||
padding:0;background:#0008;backdrop-filter:blur(3px);touch-action:manipulation}
|
||||
.sidebar{visibility:hidden;padding-top:env(safe-area-inset-top);padding-bottom:env(safe-area-inset-bottom)}
|
||||
.sidebar.open{visibility:visible}
|
||||
.mobile-nav-close{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;
|
||||
margin-left:auto;font-size:26px;line-height:1}
|
||||
.brand .create-menu-wrap{margin-left:4px}
|
||||
.topbar{display:grid;grid-template-columns:44px 32px minmax(0,1fr) minmax(0,104px);
|
||||
grid-template-rows:44px auto;gap:8px;height:auto;flex:0 0 auto;
|
||||
padding:calc(8px + env(safe-area-inset-top)) 12px 8px;align-items:center}
|
||||
.topbar>.mobile-menu{grid-column:1;grid-row:1;width:44px;height:44px;justify-content:center}
|
||||
.topbar>.avatar,.topbar>.avatar-stack{grid-column:2;grid-row:1;margin:0}
|
||||
.topbar>strong{grid-column:3;grid-row:1;font-size:15px;line-height:1.3}
|
||||
.topbar>.session-picker{grid-column:4;grid-row:1;margin:0;min-width:0}
|
||||
.topbar .session-current{max-width:100%;min-height:44px;width:100%}
|
||||
.topbar>.grow{display:none}
|
||||
.topbar>.top-tools{grid-column:1 / -1;grid-row:2;display:flex;width:100%;min-width:0;
|
||||
margin:0;padding:0;border:0;border-radius:0;gap:4px;background:transparent;box-shadow:none}
|
||||
.topbar .top-tool-button,.topbar .call-entry{flex:1;min-width:0;width:auto}
|
||||
.topbar .top-tool-button{height:44px;min-height:44px;border-radius:10px;background:var(--surface)}
|
||||
.topbar .call-entry{display:flex}
|
||||
.topbar .call-entry>.top-tool-button{width:100%}
|
||||
.topbar .session-menu{right:0;left:auto;max-width:calc(100vw - 24px)}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
:root{font-family:"Huninn","jf open 粉圓",sans-serif;color:var(--ink);background:var(--page);font-synthesis:none;--page:#050506;--side:#0b0b0c;--main:#0d0d0e;--panel:#0a0a0b;--inset:#101012;--line:#202023;--border:#29292d;--surface:#151517;--ink:#f1f1f2;--muted:#85858a;--faint:#626267;--cream:#f1f1ef;--accent:#3ec5a8;--danger:#ef5555;--success:#4ecb71;--hairline:#171719;--elevated:#121214;--menu:#18181b;--input:#0c0c0d;--dialog:#111113;--on-cream:#1a1a1a;--on-send:#1b1b1c;--online:#22c55e;--unread:#3b82f6;--surface-hover:rgba(255,255,255,.06);--focus:#4b4b50;--error-bg:#2a1717;--error-fg:#fca5a5;--error-border:#5a2a2a;--danger-fill:#8f2828;--danger-line:#a43b3b;--danger-soft:#ff8585;--warn-bg:#221c0e;--warn-fg:#fcd68a;--warn-border:#5a4a1f}
|
||||
|
||||
*{box-sizing:border-box}
|
||||
|
||||
html,body,#root{height:100%;margin:0}
|
||||
|
||||
button,input,textarea{font:inherit}
|
||||
|
||||
button{color:inherit}
|
||||
|
||||
.app-shell{height:100%;display:grid;grid-template-columns:280px minmax(420px,1fr) minmax(340px,420px);background:var(--page);overflow:hidden}
|
||||
|
||||
.sidebar,.chat-panel,.computer-panel{min-width:0;min-height:0;border-right:1px solid var(--hairline)}
|
||||
|
||||
.sidebar{display:flex;flex-direction:column;background:var(--side);padding:14px}
|
||||
|
||||
.brand,.topbar,.panel-head{display:flex;align-items:center}
|
||||
|
||||
.brand{height:48px;padding:0 4px 0 12px;font-size:17px;font-weight:650}
|
||||
|
||||
.brand .icon-button{margin-left:auto}
|
||||
|
||||
.icon-button{width:36px;height:36px;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:10px;background:transparent;cursor:pointer}
|
||||
|
||||
.icon-button:hover{background:var(--surface)}
|
||||
|
||||
.icon-button:disabled{opacity:.3;cursor:not-allowed}
|
||||
|
||||
.icon-button svg{width:18px;height:18px}
|
||||
|
||||
.search{height:44px;display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:14px;background:var(--elevated);padding:0 13px;color:var(--muted);margin:10px 5px 16px}
|
||||
|
||||
.search svg{width:15px}
|
||||
|
||||
.search input{width:100%;border:0;outline:0;background:transparent;color:var(--ink)}
|
||||
|
||||
.bot-list{overflow:auto;display:flex;flex-direction:column;gap:5px}
|
||||
|
||||
.bot-row{display:flex;align-items:center;gap:12px;width:100%;padding:11px;border:0;border-radius:13px;background:transparent;text-align:left;cursor:pointer}
|
||||
|
||||
.bot-row:hover,.bot-row.selected{background:var(--hairline)}
|
||||
|
||||
.bot-row>span:nth-child(2){min-width:0;display:grid;gap:2px}
|
||||
|
||||
.bot-row strong{font-size:15px}
|
||||
|
||||
.bot-row small{color:var(--muted)}
|
||||
|
||||
.sidebar-bottom{margin-top:auto;border-top:1px solid var(--hairline);padding-top:12px}
|
||||
|
||||
.account{width:100%;display:flex;align-items:center;gap:10px;padding:8px;border:0;background:transparent;border-radius:12px}
|
||||
|
||||
.account span:nth-child(2){flex:1;text-align:left}
|
||||
|
||||
.account svg{width:15px;color:var(--muted)}
|
||||
|
||||
.chat-panel{position:relative;display:flex;flex-direction:column;background:var(--main)}
|
||||
|
||||
.topbar{height:72px;flex:0 0 72px;padding:0 24px;gap:12px;border-bottom:1px solid var(--hairline)}
|
||||
|
||||
.grow{flex:1}
|
||||
|
||||
.danger-ghost{min-height:36px;border-radius:10px;padding:0 15px;border:1px solid transparent;background:transparent;color:var(--danger);cursor:pointer}
|
||||
.danger-ghost:hover{border-color:var(--danger-line);background:var(--danger-fill);color:#fff}
|
||||
|
||||
.mobile-menu{display:none}
|
||||
|
||||
.welcome{margin:auto;text-align:center;color:var(--muted)}
|
||||
|
||||
.welcome .avatar{margin:auto;width:48px;height:48px}
|
||||
|
||||
.welcome h1{color:var(--ink);font-size:21px;margin:16px 0 7px}
|
||||
|
||||
.welcome p{margin:0}
|
||||
|
||||
.composer-plus,.send{width:42px;height:42px;border:0;border-radius:50%;display:grid;place-items:center;cursor:pointer}
|
||||
|
||||
.send{background:var(--cream);color:var(--on-send)}
|
||||
|
||||
.send:disabled{opacity:.28}
|
||||
|
||||
.error-banner{position:absolute;bottom:92px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:12px;max-width:80%;padding:10px 14px;border:1px solid var(--error-border);border-radius:10px;background:var(--error-bg);color:var(--error-fg);font-size:13px}
|
||||
|
||||
.error-banner button{border:0;background:none;color:inherit}
|
||||
|
||||
.error-banner svg{width:15px}
|
||||
|
||||
.panel-head{height:72px;gap:9px;color:var(--muted)}
|
||||
|
||||
.panel-head>span{color:var(--ink);font-weight:600;margin-right:auto}
|
||||
|
||||
.preview{aspect-ratio:16/10;width:100%;overflow:hidden;border:1px solid var(--border);border-radius:14px;background:var(--inset)}
|
||||
|
||||
.primary,.outline,.danger{min-height:36px;border-radius:10px;padding:0 15px;border:1px solid var(--border);cursor:pointer;display:inline-flex;align-items:center;gap:7px}
|
||||
|
||||
.primary{background:var(--cream);border-color:var(--cream);color:var(--on-cream);font-weight:600}
|
||||
|
||||
.outline{background:transparent}
|
||||
|
||||
.danger{background:var(--danger-fill);border-color:var(--danger-line);color:#fff}
|
||||
|
||||
.primary:disabled,.outline:disabled{opacity:.35;cursor:not-allowed}
|
||||
|
||||
.outline svg{width:13px;fill:currentColor}
|
||||
|
||||
.modal-backdrop{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:20px;background:rgba(0,0,0,.68);backdrop-filter:blur(8px)}
|
||||
|
||||
.dialog{width:min(520px,100%);display:grid;gap:20px;border:1px solid var(--border);border-radius:18px;background:var(--dialog);padding:22px;box-shadow:0 24px 80px #000}
|
||||
|
||||
.dialog.compact{width:min(430px,100%)}
|
||||
|
||||
.dialog h2{margin:0;color:var(--ink);font-size:19px}
|
||||
|
||||
.dialog p{margin:0;color:var(--muted);line-height:1.55}
|
||||
|
||||
.dialog-title{display:flex;align-items:center;justify-content:space-between}
|
||||
|
||||
.dialog-title button{border:0;background:none;color:var(--muted)}
|
||||
|
||||
.dialog-title svg{width:18px}
|
||||
|
||||
.dialog label{display:grid;gap:8px;color:var(--muted);font-size:13px}
|
||||
|
||||
.dialog input,.dialog select{height:44px;border:1px solid var(--border);border-radius:10px;background:var(--input);color:var(--ink);padding:0 12px;outline:0}
|
||||
|
||||
.dialog input:focus,.dialog select:focus{border-color:var(--focus)}
|
||||
|
||||
.mode-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||||
|
||||
.mode-grid button{display:grid;justify-items:start;gap:7px;padding:16px;border:1px solid var(--border);border-radius:13px;background:var(--input);text-align:left}
|
||||
|
||||
.mode-grid button.picked{border-color:var(--accent);background:rgba(62,197,168,.07)}
|
||||
|
||||
.mode-grid svg{width:20px}
|
||||
|
||||
.mode-grid small{color:var(--muted)}
|
||||
|
||||
.dialog-actions{display:flex;justify-content:flex-end;gap:9px}
|
||||
|
||||
.dialog-actions button{height:40px}
|
||||
|
||||
@keyframes pulse{50%{opacity:.3}}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { App } from "./App";
|
||||
import "./grok/shell.css";
|
||||
import "./grok/production.css";
|
||||
import "./grok/conversation.css";
|
||||
import "./grok/transcript-utility-parity.css";
|
||||
import "./grok/host.css";
|
||||
import { createRuntimeThemeInstaller, type ThemeDocument } from "./grok/runtime-theme-token-installer";
|
||||
|
||||
createRuntimeThemeInstaller(document as unknown as ThemeDocument, "dark");
|
||||
import "@fontsource/huninn";
|
||||
import "./lazyboy/styles.css";
|
||||
import "./lazyboy/refinements.css";
|
||||
import "./lazyboy/avatar.css";
|
||||
import "./lazyboy/chat.css";
|
||||
import "./lazyboy/computer.css";
|
||||
import "./lazyboy/responsive.css";
|
||||
import "./lazyboy/extra.css";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
|
|
|
|||
Loading…
Reference in New Issue