@zq-silk/yui 1.1.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/ARCHITECTURE.md +2 -1
  2. package/ARCHITECTURE.zh-CN.md +2 -1
  3. package/README.md +2 -0
  4. package/dist/cli/commandCatalog.js +344 -324
  5. package/dist/cli/commandDiscovery.js +69 -0
  6. package/dist/cli/completion.js +22 -227
  7. package/dist/cli/dynamicCompletion.js +12 -6
  8. package/dist/cli/invocationAuthority.js +2 -2
  9. package/dist/cli/invocationRouter.js +18 -7
  10. package/dist/cli/managedDiagnostics.js +2 -1
  11. package/dist/cli/updatePorts.js +0 -5
  12. package/dist/cli.js +184 -130
  13. package/dist/commands/capabilityCommands.js +5 -5
  14. package/dist/commands/controllerCommands.js +2 -1
  15. package/dist/commands/globalRoleCommands.js +86 -44
  16. package/dist/commands/grantCommands.js +48 -8
  17. package/dist/commands/taskCommands.js +157 -196
  18. package/dist/commands/taskContextCommand.js +29 -6
  19. package/dist/commands/taskFactCommands.js +10 -69
  20. package/dist/commands/taskInputCommands.js +26 -35
  21. package/dist/commands/taskPublicationCommands.js +2 -35
  22. package/dist/commands/taskPublicationVerifyCommand.js +1 -2
  23. package/dist/commands/taskUpstreamCommands.js +2 -1
  24. package/dist/context/runContextPack.js +8 -29
  25. package/dist/context/sessionBootstrapManifest.js +5 -101
  26. package/dist/context/taskContext.js +104 -41
  27. package/dist/controller/clientRuntime.js +16 -8
  28. package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
  29. package/dist/controller/runtime.js +6 -0
  30. package/dist/core/controllerClient.js +46 -21
  31. package/dist/core/protocol.js +12 -7
  32. package/dist/doctor/doctor.js +4 -3
  33. package/dist/errors/cliError.js +4 -4
  34. package/dist/errors/cliFailure.js +213 -0
  35. package/dist/executor/fileRoleLaunchPlanner.js +1 -4
  36. package/dist/grant/capabilityGrant.js +13 -0
  37. package/dist/grant/taskAuthorization.js +127 -0
  38. package/dist/kernel/accessAssessment.js +1 -0
  39. package/dist/kernel/builtinCapabilities.js +88 -9
  40. package/dist/kernel/capabilityRegistry.js +64 -19
  41. package/dist/kernel/kernelPorts.js +2 -2
  42. package/dist/nativeAgent/agent.js +173 -0
  43. package/dist/nativeAgent/contracts.js +1 -0
  44. package/dist/nativeAgent/demo.js +29 -0
  45. package/dist/nativeAgent/index.js +3 -0
  46. package/dist/nativeAgent/mockProvider.js +43 -0
  47. package/dist/nativeAgent/textTools.js +156 -0
  48. package/dist/nativeAgent/validation.js +95 -0
  49. package/dist/output/boundedRead.js +118 -0
  50. package/dist/plugins/pluginService.js +98 -8
  51. package/dist/release/releaseWorkflowEngine.js +8 -1
  52. package/dist/resources/projectResourceService.js +23 -1
  53. package/dist/runtime/managedCaller.js +2 -2
  54. package/dist/runtime/managedIdentity.js +6 -0
  55. package/dist/runtime/runtimeCoherence.js +10 -8
  56. package/dist/storage/sqliteSchema.js +10 -0
  57. package/dist/storage/storageVersions.js +1 -1
  58. package/dist/storage/taskStore.js +3 -0
  59. package/dist/surface/surfaceContributions.js +3 -0
  60. package/dist/task/leaderArchive.js +111 -0
  61. package/dist/task/leaderArchiveAuthority.js +24 -0
  62. package/dist/task/taskAuthority.js +5 -10
  63. package/dist/tmux/commandExecutor.js +7 -5
  64. package/dist/web/assets/assetManifest.js +38 -22
  65. package/dist/web/assets/client/api.js +115 -0
  66. package/dist/web/assets/client/app.js +555 -863
  67. package/dist/web/assets/client/components.js +323 -897
  68. package/dist/web/assets/client/detail.js +333 -0
  69. package/dist/web/assets/client/dock.js +225 -0
  70. package/dist/web/assets/client/dom.js +55 -3
  71. package/dist/web/assets/client/evidence.js +172 -0
  72. package/dist/web/assets/client/format.js +25 -14
  73. package/dist/web/assets/client/forms.js +230 -0
  74. package/dist/web/assets/client/i18n.js +1020 -791
  75. package/dist/web/assets/client/overview.js +115 -0
  76. package/dist/web/assets/client/records.js +63 -0
  77. package/dist/web/assets/client/sections.js +313 -0
  78. package/dist/web/assets/client/sidebar.js +117 -0
  79. package/dist/web/assets/client/theme.js +61 -22
  80. package/dist/web/assets/icons.js +39 -0
  81. package/dist/web/assets/shell.js +124 -118
  82. package/dist/web/assets/styles/base.js +41 -0
  83. package/dist/web/assets/styles/components.js +167 -0
  84. package/dist/web/assets/styles/layout.js +49 -77
  85. package/dist/web/assets/styles/markdown.js +16 -24
  86. package/dist/web/assets/styles/responsive.js +33 -44
  87. package/dist/web/assets/styles/tokens.js +65 -89
  88. package/dist/web/assets/styles/views.js +289 -0
  89. package/dist/web/webServer.js +2 -1
  90. package/docs/architecture/README.md +1 -0
  91. package/docs/architecture/README.zh-CN.md +1 -0
  92. package/docs/cli-information-contract.md +107 -0
  93. package/docs/cli-information-contract.zh-CN.md +81 -0
  94. package/docs/managed-turn-and-session-runtime.md +9 -0
  95. package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
  96. package/docs/plugin-sdk.md +7 -3
  97. package/docs/plugin-sdk.zh-CN.md +5 -2
  98. package/docs/release-workflow.md +42 -9
  99. package/docs/release-workflow.zh-CN.md +30 -7
  100. package/docs/storage-baseline.md +5 -0
  101. package/docs/testing/verification-levels.md +2 -2
  102. package/i18n/README.zh-CN.md +2 -0
  103. package/package.json +1 -1
  104. package/skills/yui-leader/SKILL.md +8 -1
  105. package/skills/yui-leader/references/authorization.md +64 -0
  106. package/skills/yui-leader/references/execution.md +18 -4
  107. package/skills/yui-leader/references/task-plugins.md +4 -2
  108. package/skills/yui-operator/SKILL.md +9 -2
  109. package/skills/yui-runtime/SKILL.md +77 -9
  110. package/skills/yui-runtime/references/publication.md +3 -1
  111. package/dist/web/assets/client/taskSummary.js +0 -350
  112. package/dist/web/assets/client/taskSurface.js +0 -616
  113. package/dist/web/assets/client/view.js +0 -608
  114. package/dist/web/assets/styles/cards.js +0 -247
  115. package/dist/web/assets/styles/widgets.js +0 -168
@@ -1,32 +1,71 @@
1
- export const THEME_SCRIPT = `
2
- // Theme registry. To add a theme: append its name here, add a matching
3
- // [data-theme="name"]{...} token block in styles.ts (TOKEN_STYLES), and add an
4
- // <option> in shell.ts. The first entry is the default when none is stored.
5
- export const THEMES = Object.freeze(["control-room", "paper", "atlas"]);
1
+ export const THEME_SCRIPT = String.raw `
2
+ // Theme preference. "system" follows prefers-color-scheme and resolves to
3
+ // sumi (dark) or washi (light); the resolved name is stamped on <html> so the
4
+ // token blocks in styles/tokens.ts apply. To add a theme, append it here and
5
+ // add its token block.
6
+ import { h } from "/assets/js/dom.js";
6
7
 
7
- function preferredTheme() {
8
- const saved = localStorage.getItem("yui.theme");
9
- return THEMES.includes(saved) ? saved : "control-room";
8
+ export const THEMES = Object.freeze([
9
+ { id: "system", swatch: ["#141518", "#fbfaf7", "#ff7a52"] },
10
+ { id: "sumi", swatch: ["#0e0f11", "#191a1e", "#ff7a52"] },
11
+ { id: "washi", swatch: ["#f4f2ed", "#ffffff", "#d24a26"] },
12
+ { id: "ai", swatch: ["#0a0e1a", "#141a2e", "#8fa8ff"] }
13
+ ]);
14
+ const IDS = THEMES.map(function (theme) { return theme.id; });
15
+
16
+ function storedPreference() {
17
+ try {
18
+ const saved = localStorage.getItem("yui.theme");
19
+ return IDS.includes(saved) ? saved : "system";
20
+ } catch { return "system"; }
10
21
  }
11
22
 
12
- export function createThemeController(select) {
13
- let theme = preferredTheme();
23
+ export function createThemeController(container, t) {
24
+ const media = window.matchMedia("(prefers-color-scheme: light)");
25
+ let preference = storedPreference();
26
+ const listeners = new Set();
14
27
 
15
- function apply(nextTheme, persist) {
16
- if (!THEMES.includes(nextTheme)) return;
17
- theme = nextTheme;
18
- document.documentElement.dataset.theme = theme;
19
- if (select) select.value = theme;
20
- if (persist) localStorage.setItem("yui.theme", theme);
28
+ function resolved() {
29
+ return preference === "system" ? (media.matches ? "washi" : "sumi") : preference;
21
30
  }
22
-
23
- if (select) {
24
- select.addEventListener("change", function () { apply(select.value, true); });
31
+ function apply() {
32
+ document.documentElement.dataset.theme = resolved();
33
+ if (container) container.querySelectorAll("[data-theme-option]").forEach(function (button) {
34
+ button.setAttribute("aria-pressed", String(button.dataset.themeOption === preference));
35
+ });
36
+ listeners.forEach(function (listener) { listener(resolved()); });
37
+ }
38
+ function render() {
39
+ if (!container) return;
40
+ container.replaceChildren.apply(container, THEMES.map(function (theme) {
41
+ const swatch = h("span.theme-swatch", { "aria-hidden": "true" },
42
+ theme.swatch.map(function (color, index) {
43
+ const part = h("i");
44
+ part.style.background = color;
45
+ part.dataset.part = String(index);
46
+ return part;
47
+ }));
48
+ return h("button.theme-option", {
49
+ type: "button",
50
+ dataset: { themeOption: theme.id },
51
+ onclick: function () { set(theme.id); }
52
+ }, swatch, h("span", null, t("theme." + theme.id)));
53
+ }));
54
+ apply();
55
+ }
56
+ function set(next) {
57
+ if (!IDS.includes(next)) return;
58
+ preference = next;
59
+ try { localStorage.setItem("yui.theme", next); } catch {}
60
+ apply();
25
61
  }
26
- apply(theme, false);
62
+ media.addEventListener("change", function () { if (preference === "system") apply(); });
63
+ render();
27
64
  return {
28
- getTheme: function () { return theme; },
29
- setTheme: function (nextTheme) { apply(nextTheme, true); }
65
+ resolved: resolved,
66
+ render: render,
67
+ set: set,
68
+ subscribe: function (listener) { listeners.add(listener); }
30
69
  };
31
70
  }
32
71
  `;
@@ -0,0 +1,39 @@
1
+ // A small stroke icon set (24×24 grid, round caps). Shared by the static shell
2
+ // markup and the client DOM builder so both draw from one geometry source.
3
+ // Each entry is a list of SVG path "d" strings.
4
+ export const ICON_PATHS = Object.freeze({
5
+ refresh: ["M20 11a8 8 0 0 0-14.3-4.9L4 8", "M4 4v4h4", "M4 13a8 8 0 0 0 14.3 4.9L20 16", "M20 20v-4h-4"],
6
+ search: ["M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14z", "M20 20l-4-4"],
7
+ terminal: ["M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z", "M7 10l3 2-3 2", "M12 15h5"],
8
+ send: ["M4 12l16-8-6 16-2.5-6.5z", "M11.5 13.5L20 4"],
9
+ swap: ["M7 7h13", "M16 3l4 4-4 4", "M17 17H4", "M8 13l-4 4 4 4"],
10
+ close: ["M6 6l12 12", "M18 6L6 18"],
11
+ back: ["M19 12H5", "M11 6l-6 6 6 6"],
12
+ dock: ["M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z", "M14 5v14", "M17 9h1", "M17 12h1"],
13
+ settings: ["M4 7h10", "M18 7h2", "M16 5v4", "M4 17h4", "M12 17h8", "M10 15v4"],
14
+ chat: ["M5 5h14a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-8l-4 3v-3H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z"],
15
+ broadcast: ["M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2z", "M8.5 8.5a5 5 0 0 0 0 7", "M15.5 8.5a5 5 0 0 1 0 7", "M5.6 5.6a9 9 0 0 0 0 12.8", "M18.4 5.6a9 9 0 0 1 0 12.8"],
16
+ chevron: ["M9 6l6 6-6 6"],
17
+ down: ["M6 9l6 6 6-6"],
18
+ check: ["M5 12.5l4.5 4.5L19 7.5"],
19
+ alert: ["M12 4l9 16H3z", "M12 10v4", "M12 17v.5"],
20
+ clock: ["M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z", "M12 7v5l3 2"],
21
+ file: ["M7 3h7l5 5v12a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z", "M14 3v5h5"],
22
+ copy: ["M9 9h10a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1z", "M5 15H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1"],
23
+ layers: ["M12 3l9 5-9 5-9-5z", "M3 13l9 5 9-5"],
24
+ pulse: ["M3 12h4l3-7 4 14 3-7h4"],
25
+ history: ["M3 12a9 9 0 1 0 3-6.7", "M3 4v4h4", "M12 8v4l3 2"],
26
+ dots: ["M5 12h.01", "M12 12h.01", "M19 12h.01"],
27
+ inbox: ["M4 13l2.5-8h11L20 13", "M4 13v6h16v-6", "M4 13h5l1 2h4l1-2h5"],
28
+ user: ["M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8z", "M4 20a8 8 0 0 1 16 0"],
29
+ eye: ["M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z", "M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"],
30
+ plus: ["M12 5v14", "M5 12h14"],
31
+ target: ["M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18z", "M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z", "M12 12h.01"],
32
+ flag: ["M5 21V4", "M5 4h11l-2 4 2 4H5"],
33
+ sparkle: ["M12 3v4", "M12 17v4", "M3 12h4", "M17 12h4", "M6 6l2.5 2.5", "M15.5 15.5L18 18", "M18 6l-2.5 2.5", "M8.5 15.5L6 18"]
34
+ });
35
+ export function iconSvg(name, className = "icon") {
36
+ const paths = ICON_PATHS[name] ?? [];
37
+ return `<svg class="${className}" viewBox="0 0 24 24" aria-hidden="true" focusable="false">`
38
+ + paths.map((d) => `<path d="${d}"/>`).join("") + "</svg>";
39
+ }
@@ -1,153 +1,159 @@
1
+ import { iconSvg } from "./icons.js";
2
+ // Static application frame. Everything inside #detail, #task-list and the dock
3
+ // panes is rendered by the client modules; this file only fixes the regions
4
+ // and the controls whose identity must survive re-renders.
1
5
  export const DASHBOARD_HTML = `<!doctype html>
2
- <html lang="en" data-theme="control-room">
6
+ <html lang="en" data-theme="sumi">
3
7
  <head>
4
8
  <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
10
  <meta name="color-scheme" content="dark light">
7
11
  <meta name="yui-web-token" content="__YUI_WEB_TOKEN__">
8
12
  <link rel="icon" href="data:,">
9
- <title>Yui Control Room</title>
13
+ <title>Yui</title>
10
14
  <link rel="preload" href="/assets/fonts/inter-500.woff2" as="font" type="font/woff2" crossorigin>
11
15
  <link rel="preload" href="/assets/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
12
16
  <link rel="stylesheet" href="/assets/css/fonts.css">
13
17
  <link rel="stylesheet" href="/assets/css/tokens.css">
18
+ <link rel="stylesheet" href="/assets/css/base.css">
14
19
  <link rel="stylesheet" href="/assets/css/layout.css">
15
- <link rel="stylesheet" href="/assets/css/widgets.css">
16
- <link rel="stylesheet" href="/assets/css/cards.css">
20
+ <link rel="stylesheet" href="/assets/css/components.css">
21
+ <link rel="stylesheet" href="/assets/css/views.css">
17
22
  <link rel="stylesheet" href="/assets/css/markdown.css">
18
23
  <link rel="stylesheet" href="/assets/css/responsive.css">
19
24
  <link rel="stylesheet" href="/assets/vendor/xterm.css">
20
25
  </head>
21
26
  <body>
22
- <a class="skip-link" href="#main" data-i18n="a11y.skip">Skip to the main panel</a>
23
- <div class="app-shell">
24
- <aside class="sidebar" aria-label="Work index">
25
- <div class="sidebar-brand">
26
- <span class="brand-mark" aria-hidden="true">結</span>
27
- <div class="brand-text">
28
- <strong>Yui</strong>
29
- <span data-i18n="brand.subtitle">local control plane</span>
27
+ <a class="skip-link" href="#center" data-i18n="a11y.skip">Skip to task details</a>
28
+ <div class="app">
29
+ <aside class="sidebar" aria-label="Tasks" data-i18n-aria-label="sidebar.label">
30
+ <header class="side-head">
31
+ <div class="brand">
32
+ <span class="brand-mark" aria-hidden="true">結</span>
33
+ <span class="brand-name">Yui</span>
30
34
  </div>
31
- <span class="live" title="Local loopback"><i aria-hidden="true"></i><span class="sr-only" data-i18n="brand.connection">Local loopback</span></span>
32
- </div>
33
- <button id="global-input-open" class="operator-open" type="button">Global inputs · 全局消息</button>
34
- <label class="search">
35
+ <span class="sync" id="sync-state" title="Last sync" data-i18n-title="sync.label">
36
+ <i aria-hidden="true"></i><time id="last-sync">—</time>
37
+ </span>
38
+ <button id="refresh" class="icon-btn" type="button" aria-label="Refresh" data-i18n-aria-label="actions.refresh" title="Refresh · R">${iconSvg("refresh")}</button>
39
+ </header>
40
+ <label class="search-field">
41
+ ${iconSvg("search")}
35
42
  <span class="sr-only" data-i18n="search.label">Search tasks</span>
36
- <input id="search" type="search" placeholder="Filter by title, ID, tag, or project…" data-i18n-placeholder="search.placeholder">
43
+ <input id="search" type="search" autocomplete="off" spellcheck="false" placeholder="Search tasks" data-i18n-placeholder="search.placeholder">
37
44
  <kbd>/</kbd>
38
45
  </label>
39
- <div id="status-filters" class="filters" role="group" aria-label="Filter by status" data-i18n-aria-label="filters.label"></div>
40
- <div id="task-list" class="task-list" aria-label="Tasks" data-i18n-aria-label="board.title" aria-live="polite"><div class="loading" data-i18n="loading.dashboard">Reading local state…</div></div>
41
- <div id="catalog-controls" class="catalog-pager" aria-live="polite">
42
- <button id="catalog-reset" type="button" data-i18n="catalog.first">First page</button>
43
- <span id="catalog-count"></span>
44
- <button id="catalog-next" type="button" data-i18n="catalog.next" disabled>Next page</button>
45
- <button id="catalog-attention-reset" type="button" data-i18n="catalog.clearAttention" hidden>Clear attention filter</button>
46
- </div>
47
- <div class="sidebar-foot">
48
- <div class="sidebar-controls">
49
- <label class="select-control">
50
- <span class="sr-only" data-i18n="controls.language">Language</span>
51
- <select id="locale-select" aria-label="Language" data-i18n-aria-label="controls.language">
52
- <option value="en">English</option>
53
- <option value="zh-CN">简体中文</option>
54
- </select>
55
- </label>
56
- <label class="select-control">
57
- <span class="sr-only" data-i18n="controls.theme">Theme</span>
58
- <select id="theme-select" aria-label="Theme" data-i18n-aria-label="controls.theme">
59
- <option value="control-room" data-i18n="theme.controlRoom">Control room</option>
60
- <option value="paper" data-i18n="theme.paper">Paper ledger</option>
61
- <option value="atlas" data-i18n="theme.atlas">Atlas</option>
62
- </select>
63
- </label>
64
- </div>
46
+ <div id="attention-bar" class="attention-bar" aria-live="polite"></div>
47
+ <div id="status-filters" class="status-tabs" role="group" aria-label="Filter by status" data-i18n-aria-label="filters.label"></div>
48
+ <div id="task-list" class="task-list" aria-label="Tasks" data-i18n-aria-label="sidebar.label" aria-live="polite"></div>
49
+ <div id="catalog-controls" class="pager" aria-live="polite">
50
+ <button id="catalog-reset" class="pager-btn" type="button" data-i18n="catalog.first">First page</button>
51
+ <span id="catalog-count" class="pager-count"></span>
52
+ <button id="catalog-next" class="pager-btn" type="button" data-i18n="catalog.next" disabled>Next page</button>
53
+ <button id="catalog-attention-reset" class="pager-clear" type="button" data-i18n="catalog.clearAttention" hidden>Clear attention filter</button>
65
54
  </div>
55
+ <footer class="side-foot">
56
+ <button id="operator-terminal" class="foot-btn" type="button" title="Operator session · O">
57
+ ${iconSvg("terminal")}<span data-i18n="actions.operator">Operator</span><kbd>O</kbd>
58
+ </button>
59
+ <button id="global-input-open" class="foot-btn" type="button">
60
+ ${iconSvg("broadcast")}<span data-i18n="global.open">Global input</span>
61
+ </button>
62
+ <button id="settings-open" class="icon-btn" type="button" aria-label="Settings" data-i18n-aria-label="settings.title" title="Settings">${iconSvg("settings")}</button>
63
+ </footer>
66
64
  </aside>
67
- <div class="main-col">
68
- <header class="topbar">
69
- <div class="topbar-leading">
70
- <button id="detail-back" class="detail-back" type="button" aria-label="Back to task list" data-i18n-aria-label="actions.back" hidden>←</button>
71
- <div class="breadcrumb">
72
- <span class="crumb" data-i18n="breadcrumb.taskList">All work</span>
73
- <span class="crumb-sep" aria-hidden="true">/</span>
74
- <h1 id="page-title" class="crumb-current" data-i18n="page.title">Overview</h1>
75
- </div>
65
+ <main id="center" class="center" tabindex="-1">
66
+ <div id="detail" class="detail"></div>
67
+ </main>
68
+ <div id="dock-divider" class="dock-divider" role="separator" tabindex="0" aria-orientation="vertical" aria-valuemin="320" aria-valuemax="720" aria-valuenow="440" aria-label="Resize session panel" data-i18n-aria-label="dock.resize" hidden><span aria-hidden="true"></span></div>
69
+ <aside id="dock" class="dock" aria-label="Session" data-i18n-aria-label="dock.label" hidden>
70
+ <header class="dock-head">
71
+ <div class="seg" role="tablist" aria-label="Session view" data-i18n-aria-label="dock.label">
72
+ <button id="dock-tab-discussion" class="seg-btn" type="button" role="tab" aria-selected="true" aria-controls="dock-discussion">${iconSvg("chat")}<span data-i18n="dock.discussion">Discussion</span></button>
73
+ <button id="dock-tab-session" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-session">${iconSvg("terminal")}<span data-i18n="dock.session">Live session</span></button>
76
74
  </div>
77
- <div class="topbar-actions">
78
- <button id="conversation-toggle" class="operator-open" type="button" aria-controls="conversation-panel" aria-expanded="false" data-i18n="conversation.show" hidden>Task discussion</button>
79
- <div class="clock">
80
- <span data-i18n="sync.label">Last sync</span>
81
- <time id="last-sync">—</time>
82
- </div>
83
- <button id="operator-terminal" class="operator-open" type="button" aria-label="Operator session" data-i18n-aria-label="actions.operator">
84
- <span class="operator-title" data-i18n="actions.operator">Operator session</span>
85
- <span class="operator-shortcuts" aria-hidden="true"><kbd>O</kbd></span>
86
- </button>
87
- <button id="refresh" class="refresh" type="button" title="Refresh tasks" aria-label="Refresh" data-i18n-aria-label="actions.refresh"><span data-i18n="actions.refresh">Refresh</span> <kbd>R</kbd></button>
75
+ <div class="dock-actions">
76
+ <button id="dock-swap" class="icon-btn" type="button" aria-label="Swap with task details" data-i18n-aria-label="dock.swap" title="Swap with task details">${iconSvg("swap")}</button>
77
+ <button id="dock-close" class="icon-btn" type="button" aria-label="Hide session panel" data-i18n-aria-label="dock.hide" title="Hide · D">${iconSvg("close")}</button>
88
78
  </div>
89
79
  </header>
90
- <nav id="detail-tabs" class="detail-tabs" aria-label="Task sections" data-i18n-aria-label="tabs.label" hidden>
91
- <button class="tab" type="button" data-target="detail-top" data-i18n="tabs.summary">Overview</button>
92
- <button class="tab" type="button" data-target="detail-work" data-i18n="tabs.workResults">Work &amp; results</button>
93
- <button class="tab" type="button" data-target="detail-exec" data-i18n="tabs.executionReviews">Execution &amp; reviews</button>
94
- <button class="tab" type="button" data-target="detail-history" data-i18n="tabs.historyMessages">History &amp; messages</button>
95
- </nav>
96
- <main id="detail" class="detail" aria-labelledby="page-title" tabindex="-1"></main>
97
- </div>
98
- <div id="pane-divider" class="pane-divider" role="separator" tabindex="0" aria-label="Resize task discussion" aria-orientation="vertical" aria-valuemin="280" aria-valuemax="640" aria-valuenow="400" data-i18n-aria-label="conversation.resize" hidden><span aria-hidden="true"></span></div>
99
- <aside id="conversation-panel" class="conversation-panel" aria-labelledby="conversation-title" hidden>
100
- <header class="conversation-head">
101
- <div>
102
- <span class="conversation-eyebrow" data-i18n="conversation.source">Recorded Task messages · not a Session transcript</span>
103
- <h2 id="conversation-title" data-i18n="conversation.title">Task discussion</h2>
80
+ <section id="dock-discussion" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-discussion"></section>
81
+ <section id="dock-session" class="dock-pane dock-session" role="tabpanel" aria-labelledby="dock-tab-session" hidden>
82
+ <div class="session-bar">
83
+ <div id="session-targets" class="session-targets" role="group" aria-label="Session target" data-i18n-aria-label="dock.target"></div>
84
+ <span id="terminal-state" class="conn" data-state="idle"><i aria-hidden="true"></i><span data-i18n="terminal.idle">Not connected</span></span>
104
85
  </div>
105
- <div class="conversation-head-actions">
106
- <button id="conversation-swap" class="pane-action" type="button" title="Move discussion to the other side" data-i18n-aria-label="conversation.swap" aria-label="Move discussion to the other side">⇄</button>
107
- <button id="conversation-close" class="pane-action" type="button" title="Hide discussion" data-i18n-aria-label="conversation.hide" aria-label="Hide discussion">×</button>
86
+ <div id="terminal-host" class="terminal-host"></div>
87
+ <div id="terminal-empty" class="terminal-empty">
88
+ ${iconSvg("terminal", "icon icon-xl")}
89
+ <p class="terminal-empty-title" data-i18n="terminal.emptyTitle">Attach to a native Session</p>
90
+ <p class="terminal-empty-text" data-i18n="terminal.emptyText">Pick a Role above. The panel shows the Session's current screen; attaching never starts or restarts an Agent.</p>
108
91
  </div>
109
- </header>
110
- <div id="conversation-content" class="conversation-content task-surface"></div>
92
+ <footer class="session-foot">
93
+ <span data-i18n="terminal.cliHint">Full terminal</span>
94
+ <code id="terminal-cli">yui operator enter</code>
95
+ </footer>
96
+ </section>
111
97
  </aside>
112
- <dialog id="global-input-dialog" class="global-input-dialog" aria-labelledby="global-input-title">
113
- <h2 id="global-input-title">Global Role input · 全局 Role 消息</h2>
114
- <p class="muted">Queue is the default. A cancel request does not prove the Turn stopped.<br>默认排队;取消请求不代表执行已停止。</p>
115
- <form id="global-input-form" class="record-block">
116
- <label>Role<input id="global-input-role" value="operator" required pattern="[A-Za-z0-9_-]+"></label>
117
- <label>Action · 动作<select id="global-input-action">
118
- <option value="queue">Queue · 排队</option>
119
- <option value="steer">Steer · 当前轮插话</option>
120
- <option value="interrupt">Interrupt · 请求取消</option>
121
- </select></label>
122
- <label id="global-input-body-label">Message · 消息<textarea id="global-input-body" maxlength="8000" required></textarea></label>
123
- <label id="global-input-target-label" hidden>Expected Turn · 精确目标<input id="global-input-target"></label>
124
- <label id="global-input-then-label" hidden>Then-message ID · 后续消息引用<input id="global-input-then"></label>
125
- <div class="record-actions">
126
- <button id="global-input-inspect" type="button" class="record-open">Read state · 读取状态</button>
127
- <button id="global-input-submit" type="submit" class="record-open">Submit · 提交</button>
128
- <button id="global-input-close" type="button" class="record-open">Close · 关闭</button>
129
- </div>
130
- <p id="global-input-receipt" role="status" class="muted">Not submitted · 未提交</p>
131
- <pre id="global-input-state" class="global-input-state"></pre>
132
- </form>
133
- </dialog>
134
- <aside id="terminal-panel" class="terminal-panel" aria-labelledby="terminal-title" aria-hidden="true" hidden>
135
- <header class="terminal-head">
98
+ </div>
99
+ <dialog id="settings-dialog" class="dialog settings-dialog" aria-labelledby="settings-title">
100
+ <form method="dialog" class="dialog-body">
101
+ <header class="dialog-head">
102
+ <h2 id="settings-title" data-i18n="settings.title">Settings</h2>
103
+ <button class="icon-btn" value="close" aria-label="Close" data-i18n-aria-label="actions.close">${iconSvg("close")}</button>
104
+ </header>
105
+ <fieldset class="field">
106
+ <legend data-i18n="settings.theme">Theme</legend>
107
+ <div id="theme-options" class="theme-options"></div>
108
+ </fieldset>
109
+ <label class="field">
110
+ <span data-i18n="settings.language">Language</span>
111
+ <select id="locale-select">
112
+ <option value="en">English</option>
113
+ <option value="zh-CN">简体中文</option>
114
+ </select>
115
+ </label>
116
+ <div class="field">
117
+ <span data-i18n="settings.shortcuts">Keyboard</span>
118
+ <dl class="shortcut-list">
119
+ <dt><kbd>/</kbd></dt><dd data-i18n="shortcut.search">Search tasks</dd>
120
+ <dt><kbd>R</kbd></dt><dd data-i18n="shortcut.refresh">Refresh</dd>
121
+ <dt><kbd>D</kbd></dt><dd data-i18n="shortcut.dock">Show or hide the session panel</dd>
122
+ <dt><kbd>O</kbd></dt><dd data-i18n="shortcut.operator">Operator session</dd>
123
+ <dt><kbd>1</kbd>–<kbd>5</kbd></dt><dd data-i18n="shortcut.tabs">Switch task sections</dd>
124
+ <dt><kbd>Esc</kbd></dt><dd data-i18n="shortcut.escape">Close panel / leave task</dd>
125
+ </dl>
126
+ </div>
127
+ </form>
128
+ </dialog>
129
+ <dialog id="global-input-dialog" class="dialog" aria-labelledby="global-input-title">
130
+ <form id="global-input-form" class="dialog-body">
131
+ <header class="dialog-head">
136
132
  <div>
137
- <span id="terminal-state" class="live"><i aria-hidden="true"></i><span data-i18n="terminal.connecting">Connecting</span></span>
138
- <h2 id="terminal-title">Operator</h2>
133
+ <h2 id="global-input-title" data-i18n="global.title">Global Role input</h2>
134
+ <p class="dialog-sub" data-i18n="global.help">Queue is the default. A cancel request does not prove the Turn stopped.</p>
139
135
  </div>
140
- <button id="terminal-close" class="detail-back terminal-close" type="button" aria-label="Close terminal" data-i18n-aria-label="terminal.close">×</button>
136
+ <button id="global-input-close" class="icon-btn" type="button" aria-label="Close" data-i18n-aria-label="actions.close">${iconSvg("close")}</button>
141
137
  </header>
142
- <div id="terminal-host" class="terminal-host">
143
- <div id="terminal-hint" class="terminal-hint" hidden>
144
- <p data-i18n="terminal.attachTitle">Open the full Operator in a terminal</p>
145
- <p class="terminal-hint-cli"><a id="terminal-cli" href="#" title="Copy command">yui operator enter</a></p>
146
- <p class="terminal-hint-note" data-i18n="terminal.attachNote">This panel stays attached until you close it.</p>
147
- </div>
138
+ <div class="field-row">
139
+ <label class="field"><span data-i18n="global.role">Role</span><input id="global-input-role" value="operator" required pattern="[A-Za-z0-9_-]+"></label>
140
+ <label class="field"><span data-i18n="control.action">Action</span><select id="global-input-action">
141
+ <option value="queue" data-i18n="control.queue">Queue</option>
142
+ <option value="steer" data-i18n="control.steer">Steer</option>
143
+ <option value="interrupt" data-i18n="control.interrupt">Interrupt</option>
144
+ </select></label>
148
145
  </div>
149
- </aside>
150
- </div>
146
+ <label id="global-input-body-label" class="field"><span data-i18n="control.message">Message</span><textarea id="global-input-body" maxlength="8000" required rows="4"></textarea></label>
147
+ <label id="global-input-target-label" class="field" hidden><span data-i18n="control.expectedTurn">Expected current Turn</span><input id="global-input-target" class="mono"></label>
148
+ <label id="global-input-then-label" class="field" hidden><span data-i18n="control.then">Then-message reference</span><input id="global-input-then" class="mono"></label>
149
+ <div class="dialog-actions">
150
+ <button id="global-input-inspect" type="button" class="btn" data-i18n="global.inspect">Read state</button>
151
+ <button id="global-input-submit" type="submit" class="btn btn-primary" data-i18n="actions.submit">Submit</button>
152
+ </div>
153
+ <p id="global-input-receipt" role="status" class="receipt" data-i18n="receipt.notSubmitted">Not submitted</p>
154
+ <pre id="global-input-state" class="code-block" hidden></pre>
155
+ </form>
156
+ </dialog>
151
157
  <div id="toast" class="toast" role="status" aria-live="polite"></div>
152
158
  <script type="module" src="/assets/app.js"></script>
153
159
  </body>
@@ -0,0 +1,41 @@
1
+ /*
2
+ * BASE — element defaults, form controls, focus, scrollbars and utilities.
3
+ */
4
+ export const BASE_STYLES = `
5
+ *,*::before,*::after{box-sizing:border-box}
6
+ html{background:var(--canvas);color:var(--ink);font-family:var(--font-sans);font-size:var(--fs-md);line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility}
7
+ body{margin:0;min-width:320px;height:100vh;height:100dvh;overflow:hidden;background:var(--canvas);color:var(--ink);font-feature-settings:"cv11","ss01"}
8
+ h1,h2,h3,h4,h5,h6,p{margin:0}
9
+ h1,h2,h3,h4{font-weight:600;letter-spacing:-.011em;line-height:1.3}
10
+ a{color:var(--accent)}
11
+ code,kbd,pre,.mono{font-family:var(--font-mono);font-size:.92em}
12
+ kbd{display:inline-grid;place-items:center;min-width:18px;height:18px;padding:0 5px;border:1px solid var(--line-2);border-bottom-width:2px;border-radius:var(--r-xs);background:var(--surface-2);color:var(--ink-3);font-family:var(--font-sans);font-size:10px;font-weight:600;line-height:1}
13
+ button{font:inherit;color:inherit;cursor:pointer}
14
+ button:disabled{cursor:not-allowed}
15
+ input,select,textarea{font:inherit;color:var(--ink)}
16
+ input,select,textarea{width:100%;min-width:0;padding:8px 10px;background:var(--sunken);border:1px solid var(--line-2);border-radius:var(--r-sm);transition:border-color var(--t-fast),box-shadow var(--t-fast),background var(--t-fast)}
17
+ textarea{resize:vertical;line-height:1.5}
18
+ select{appearance:none;padding-right:30px;background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);background-position:calc(100% - 15px) 50%,calc(100% - 11px) 50%;background-size:4px 4px;background-repeat:no-repeat}
19
+ input::placeholder,textarea::placeholder{color:var(--ink-4)}
20
+ input:hover,select:hover,textarea:hover{border-color:var(--line-3)}
21
+ input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent-line);box-shadow:0 0 0 3px var(--accent-soft);background:var(--surface-2)}
22
+ input:disabled,select:disabled,textarea:disabled{opacity:.55}
23
+ :focus-visible{outline:none;box-shadow:var(--focus)}
24
+ ::selection{background:var(--accent-soft);color:var(--ink)}
25
+ *{scrollbar-width:thin;scrollbar-color:var(--line-3) transparent}
26
+ ::-webkit-scrollbar{width:10px;height:10px}
27
+ ::-webkit-scrollbar-thumb{background:var(--line-3);border-radius:var(--r-pill);border:3px solid transparent;background-clip:content-box}
28
+ ::-webkit-scrollbar-track{background:transparent}
29
+ [hidden]{display:none!important}
30
+ .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
31
+ .skip-link{position:fixed;left:12px;top:-60px;z-index:100;padding:9px 14px;border-radius:var(--r-sm);background:var(--accent);color:var(--accent-ink);text-decoration:none;font-weight:600;transition:top var(--t-fast)}
32
+ .skip-link:focus{top:12px}
33
+ .icon{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
34
+ .icon-sm{width:14px;height:14px}
35
+ .icon-xl{width:30px;height:30px;stroke-width:1.4}
36
+ .muted{color:var(--ink-3)}
37
+ .faint{color:var(--ink-4)}
38
+ .nowrap{white-space:nowrap}
39
+ .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
40
+ @media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important;scroll-behavior:auto!important}}
41
+ `;