@zq-silk/yui 2.15.0 → 2.17.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.
- package/dist/cli/commandCatalog.js +17 -11
- package/dist/cli/interactionCandidates.js +20 -5
- package/dist/cli/interactionPolicy.js +5 -4
- package/dist/cli/roleWizard.js +7 -0
- package/dist/commands/globalRoleCommands.js +13 -2
- package/dist/commands/operatorCommands.js +2 -0
- package/dist/commands/profileCommands.js +19 -2
- package/dist/commands/roleConfiguration.js +3 -0
- package/dist/commands/taskCommands.js +16 -0
- package/dist/commands/taskRoleRuntimeStatus.js +3 -0
- package/dist/controller/agentJobControl.js +9 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +35 -4
- package/dist/controller/globalInputDelivery.js +14 -8
- package/dist/controller/providerRetryAdmission.js +9 -0
- package/dist/controller/providerRetryDelivery.js +16 -0
- package/dist/controller/runtime.js +14 -0
- package/dist/controller/runtimeLaunchCoordinator.js +3 -1
- package/dist/executor/fileRoleLaunchPlanner.js +3 -0
- package/dist/message/messageContinuation.js +4 -0
- package/dist/output/rolePresentation.js +3 -0
- package/dist/profile/agentProfile.js +8 -0
- package/dist/profile/agentProfileRuntime.js +16 -1
- package/dist/review/reviewDecision.js +6 -0
- package/dist/role/availability.js +28 -0
- package/dist/role/role.js +6 -0
- package/dist/runtime/codexNativeAccess.js +21 -4
- package/dist/runtime/tmuxAdapters.js +5 -0
- package/dist/scheduler/activeRoleRunDelivery.js +9 -0
- package/dist/scheduler/leaderWakeupProcessor.js +8 -1
- package/dist/scheduler/roleRunLiveness.js +3 -0
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/web/assets/client/app/catalog.js +40 -14
- package/dist/web/assets/client/app/main.js +43 -26
- package/dist/web/assets/client/app/manage.js +18 -0
- package/dist/web/assets/client/app/selection.js +4 -1
- package/dist/web/assets/client/app/taskView.js +29 -16
- package/dist/web/assets/client/i18n/en.js +72 -21
- package/dist/web/assets/client/i18n/zh-CN.js +72 -21
- package/dist/web/assets/client/index.js +6 -0
- package/dist/web/assets/client/layout/manageShell.js +30 -8
- package/dist/web/assets/client/layout/shortcuts.js +9 -5
- package/dist/web/assets/client/layout/sidebarNav.js +147 -0
- package/dist/web/assets/client/layout/sizing.js +5 -2
- package/dist/web/assets/client/layout/workspace.js +193 -107
- package/dist/web/assets/client/lib/motion.js +59 -0
- package/dist/web/assets/client/lib/prefs.js +29 -2
- package/dist/web/assets/client/lib/theme.js +11 -4
- package/dist/web/assets/client/ui/primitives.js +40 -0
- package/dist/web/assets/client/views/dock/conversation.js +421 -123
- package/dist/web/assets/client/views/dock/session.js +229 -0
- package/dist/web/assets/client/views/dock/terminal.js +48 -39
- package/dist/web/assets/client/views/overview.js +8 -5
- package/dist/web/assets/client/views/task/page.js +7 -17
- package/dist/web/assets/shared/brand.js +22 -0
- package/dist/web/assets/shared/geometry.js +7 -1
- package/dist/web/assets/shared/icons.js +4 -0
- package/dist/web/assets/shared/motion.js +74 -0
- package/dist/web/assets/shell/appRail.js +14 -0
- package/dist/web/assets/shell/dialogs.js +1 -0
- package/dist/web/assets/shell/dock.js +19 -24
- package/dist/web/assets/shell/index.js +6 -2
- package/dist/web/assets/shell/login.js +9 -8
- package/dist/web/assets/shell/manage.js +32 -20
- package/dist/web/assets/shell/settingsContent.js +1 -1
- package/dist/web/assets/shell/sidebar.js +16 -23
- package/dist/web/assets/styles/base.js +1 -1
- package/dist/web/assets/styles/index.js +2 -0
- package/dist/web/assets/styles/layout/responsive.js +63 -19
- package/dist/web/assets/styles/layout/workspace.js +53 -16
- package/dist/web/assets/styles/motion.js +51 -0
- package/dist/web/assets/styles/tokens.js +5 -3
- package/dist/web/assets/styles/ui/controls.js +1 -1
- package/dist/web/assets/styles/ui/forms.js +1 -1
- package/dist/web/assets/styles/ui/surfaces.js +1 -2
- package/dist/web/assets/styles/views/conversation.js +97 -18
- package/dist/web/assets/styles/views/dock.js +34 -2
- package/dist/web/assets/styles/views/login.js +16 -13
- package/dist/web/assets/styles/views/manage.js +25 -42
- package/dist/web/assets/styles/views/page.js +2 -3
- package/dist/web/assets/styles/views/records.js +1 -1
- package/dist/web/assets/styles/views/settings.js +1 -1
- package/dist/web/assets/styles/views/sidebar.js +25 -20
- package/dist/web/assets/styles/views/task.js +8 -15
- package/dist/web/assets/styles/views/terminal.js +13 -9
- package/dist/web/webConversation.js +1 -0
- package/dist/web/webSettings.js +10 -2
- package/docs/managed-turn-and-session-runtime.md +10 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/release-workflow.md +46 -0
- package/docs/release-workflow.zh-CN.md +34 -0
- package/docs/roles-and-configuration.md +45 -1
- package/docs/roles-and-configuration.zh-CN.md +35 -0
- package/docs/storage-baseline.md +6 -1
- package/docs/storage-baseline.zh-CN.md +5 -1
- package/docs/testing/review-evaluation.md +35 -1
- package/docs/testing/review-evaluation.zh-CN.md +25 -1
- package/docs/testing/verification-levels.md +2 -2
- package/docs/testing/verification-levels.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/VERSION +1 -1
- package/skills/yui-leader/references/execution.md +19 -8
- package/skills/yui-reviewer/SKILL.md +13 -2
- package/skills/yui-reviewer/VERSION +1 -1
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { DOCK } from "../shared/geometry.js";
|
|
2
2
|
import { iconSvg } from "../shared/icons.js";
|
|
3
|
-
// The dock divider and the dock region:
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The dock divider and the dock region: one header row with the target menu
|
|
4
|
+
// (the only Role selector), the view tabs (icon-only until the dock is wide)
|
|
5
|
+
// and the dock actions; then the session page mount (chat or terminal, built
|
|
6
|
+
// by views/dock/session), the discussion mount and the preview.
|
|
6
7
|
// The string keeps its indentation inside .app and has no trailing newline.
|
|
7
8
|
export const DOCK_HTML = ` <div id="dock-divider" class="dock-divider" role="separator" tabindex="0" aria-orientation="vertical" aria-valuemin="${DOCK.min}" aria-valuemax="${DOCK.max}" aria-valuenow="${DOCK.initial}" aria-label="Resize session panel" data-i18n-aria-label="dock.resize" hidden><span aria-hidden="true"></span></div>
|
|
8
9
|
<aside id="dock" class="dock" aria-label="Session" data-i18n-aria-label="dock.label" hidden>
|
|
9
10
|
<header class="dock-head">
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
<details id="dock-target" class="dock-target">
|
|
12
|
+
<summary class="dock-target-btn" aria-label="Conversation target" data-i18n-aria-label="dock.target">
|
|
13
|
+
<span id="dock-target-icon" class="dock-target-icon" aria-hidden="true">${iconSvg("broadcast")}</span>
|
|
14
|
+
<span id="dock-target-label" class="dock-target-label">Operator</span>${iconSvg("down")}
|
|
15
|
+
</summary>
|
|
16
|
+
<div class="dock-target-menu">
|
|
17
|
+
<div id="dock-target-options" class="dock-target-options" role="group" aria-label="Conversation target" data-i18n-aria-label="dock.target"></div>
|
|
18
|
+
<hr>
|
|
19
|
+
<button id="global-input-open" class="dock-target-item" type="button">${iconSvg("send")}<span data-i18n="global.openRole">Message another Role…</span></button>
|
|
20
|
+
</div>
|
|
21
|
+
</details>
|
|
22
|
+
<div class="seg seg-sm dock-tabs" role="tablist" aria-label="Session view" data-i18n-aria-label="dock.label">
|
|
23
|
+
<button id="dock-tab-conversation" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-conversation" aria-label="Conversation" data-i18n-aria-label="conversation.title" title="Conversation" data-i18n-title="conversation.title">${iconSvg("sparkle")}<span data-i18n="conversation.title">Conversation</span></button>
|
|
24
|
+
<button id="dock-tab-discussion" class="seg-btn" type="button" role="tab" aria-selected="true" aria-controls="dock-discussion" aria-label="Discussion" data-i18n-aria-label="dock.discussion" title="Discussion" data-i18n-title="dock.discussion">${iconSvg("chat")}<span data-i18n="dock.discussion">Discussion</span></button>
|
|
25
|
+
<button id="dock-tab-preview" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-preview" aria-label="Preview" data-i18n-aria-label="preview.title" title="Preview" data-i18n-title="preview.title" disabled>${iconSvg("eye")}<span data-i18n="preview.title">Preview</span></button>
|
|
15
26
|
</div>
|
|
16
27
|
<div class="dock-actions">
|
|
17
28
|
<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>
|
|
@@ -21,20 +32,4 @@ export const DOCK_HTML = ` <div id="dock-divider" class="dock-divider" role="
|
|
|
21
32
|
<section id="dock-conversation" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-conversation" hidden></section>
|
|
22
33
|
<section id="dock-discussion" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-discussion"></section>
|
|
23
34
|
<section id="dock-preview" class="dock-pane dock-preview" role="tabpanel" aria-labelledby="dock-tab-preview" hidden></section>
|
|
24
|
-
<section id="dock-session" class="dock-pane dock-session" role="tabpanel" aria-labelledby="dock-tab-session" hidden>
|
|
25
|
-
<div class="session-bar">
|
|
26
|
-
<div id="session-targets" class="session-targets" role="group" aria-label="Session target" data-i18n-aria-label="dock.target"></div>
|
|
27
|
-
<span id="terminal-state" class="conn" data-state="idle"><i aria-hidden="true"></i><span data-i18n="terminal.idle">Not connected</span></span>
|
|
28
|
-
</div>
|
|
29
|
-
<div id="terminal-host" class="terminal-host"></div>
|
|
30
|
-
<div id="terminal-empty" class="terminal-empty">
|
|
31
|
-
${iconSvg("terminal", "icon icon-xl")}
|
|
32
|
-
<p class="terminal-empty-title" data-i18n="terminal.emptyTitle">Attach to a native Session</p>
|
|
33
|
-
<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>
|
|
34
|
-
</div>
|
|
35
|
-
<footer class="session-foot">
|
|
36
|
-
<span data-i18n="terminal.cliHint">Full terminal</span>
|
|
37
|
-
<code id="terminal-cli">yui operator enter</code>
|
|
38
|
-
</footer>
|
|
39
|
-
</section>
|
|
40
35
|
</aside>`;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { BRAND_ICONS } from "../shared/brand.js";
|
|
1
2
|
import { DEFAULT_DARK_THEME } from "../shared/themes.js";
|
|
2
3
|
import { STYLESHEETS } from "../styles/index.js";
|
|
3
4
|
import { DIALOGS_HTML } from "./dialogs.js";
|
|
4
5
|
import { DOCK_HTML } from "./dock.js";
|
|
5
6
|
import { SIDEBAR_HTML } from "./sidebar.js";
|
|
7
|
+
import { appRail } from "./appRail.js";
|
|
6
8
|
// Static application frame. Everything inside #detail, #task-list and the dock
|
|
7
9
|
// panes is rendered by the client modules; the shell only fixes the regions
|
|
8
10
|
// and the controls whose identity must survive re-renders. Stylesheets are
|
|
@@ -17,8 +19,8 @@ export const DASHBOARD_HTML = `<!doctype html>
|
|
|
17
19
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
18
20
|
<meta name="color-scheme" content="dark light">
|
|
19
21
|
<meta name="yui-web-token" content="__YUI_WEB_TOKEN__">
|
|
22
|
+
<link rel="icon" type="image/svg+xml" href="${BRAND_ICONS[DEFAULT_DARK_THEME.id]}">
|
|
20
23
|
<script src="/assets/theme.js"></script>
|
|
21
|
-
<link rel="icon" href="data:,">
|
|
22
24
|
<title>Yui</title>
|
|
23
25
|
<link rel="preload" href="/assets/fonts/inter-500.woff2" as="font" type="font/woff2" crossorigin>
|
|
24
26
|
<link rel="preload" href="/assets/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
|
|
@@ -28,7 +30,9 @@ ${STYLESHEET_LINKS}
|
|
|
28
30
|
<body>
|
|
29
31
|
<a class="skip-link" href="#center" data-i18n="a11y.skip">Skip to task details</a>
|
|
30
32
|
<div class="app">
|
|
33
|
+
${appRail("workbench")}
|
|
31
34
|
${SIDEBAR_HTML}
|
|
35
|
+
<div id="sidebar-scrim" class="sidebar-scrim"></div>
|
|
32
36
|
<main id="center" class="center" tabindex="-1">
|
|
33
37
|
<div id="detail" class="detail"></div>
|
|
34
38
|
</main>
|
|
@@ -36,6 +40,6 @@ ${DOCK_HTML}
|
|
|
36
40
|
</div>
|
|
37
41
|
${DIALOGS_HTML}
|
|
38
42
|
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
|
39
|
-
<script type="module" src="/assets/app.js"></script>
|
|
43
|
+
<script type="module" src="/assets/app.js" blocking="render"></script>
|
|
40
44
|
</body>
|
|
41
45
|
</html>`;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { DEFAULT_DARK_THEME } from "../shared/themes.js";
|
|
2
|
+
import { BRAND_ICONS, brandGlyphSvg } from "../shared/brand.js";
|
|
2
3
|
import { iconSvg } from "../shared/icons.js";
|
|
3
4
|
// The only document served without a session. It carries no page token and
|
|
4
5
|
// no Task data; the client module posts the password as same-origin JSON and
|
|
5
6
|
// the server answers with a session cookie. CSP form-action 'none' means the
|
|
6
7
|
// form can never submit natively (so a password cannot end up in a URL).
|
|
7
|
-
const SHEETS = ["fonts", "tokens", "base", "ui/controls", "ui/forms", "views/login"];
|
|
8
|
+
const SHEETS = ["fonts", "tokens", "base", "motion", "ui/controls", "ui/forms", "views/login"];
|
|
8
9
|
export const LOGIN_HTML = `<!doctype html>
|
|
9
10
|
<html lang="en" data-theme="${DEFAULT_DARK_THEME.id}">
|
|
10
11
|
<head>
|
|
11
12
|
<meta charset="utf-8">
|
|
12
13
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
13
14
|
<meta name="color-scheme" content="dark light">
|
|
15
|
+
<link rel="icon" type="image/svg+xml" href="${BRAND_ICONS[DEFAULT_DARK_THEME.id]}">
|
|
14
16
|
<script src="/assets/theme.js"></script>
|
|
15
|
-
<link rel="icon" href="data:,">
|
|
16
17
|
<title>Sign in · Yui</title>
|
|
17
18
|
<link rel="preload" href="/assets/fonts/inter-500.woff2" as="font" type="font/woff2" crossorigin>
|
|
18
19
|
<link rel="preload" href="/assets/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
|
|
@@ -20,13 +21,13 @@ ${SHEETS.map(name => ` <link rel="stylesheet" href="/assets/css/${name}.css">`)
|
|
|
20
21
|
</head>
|
|
21
22
|
<body class="login-body">
|
|
22
23
|
<main class="login-shell">
|
|
23
|
-
<div class="login-top">
|
|
24
|
-
<label class="login-locale"><span class="sr-only" data-i18n="settings.language">Language</span><select id="locale-select"><option value="en">English</option><option value="zh-CN">简体中文</option></select></label>
|
|
25
|
-
</div>
|
|
26
24
|
<section class="login-card" aria-labelledby="login-title">
|
|
27
|
-
<div class="login-
|
|
28
|
-
<
|
|
29
|
-
|
|
25
|
+
<div class="login-top">
|
|
26
|
+
<div class="login-brand">
|
|
27
|
+
<span class="brand-mark" aria-hidden="true">${brandGlyphSvg()}</span>
|
|
28
|
+
<span class="brand-name">Yui</span>
|
|
29
|
+
</div>
|
|
30
|
+
<label class="login-locale"><span class="sr-only" data-i18n="settings.language">Language</span><select id="locale-select"><option value="en">English</option><option value="zh-CN">简体中文</option></select></label>
|
|
30
31
|
</div>
|
|
31
32
|
<header class="login-heading">
|
|
32
33
|
<h1 id="login-title" data-i18n="login.heading">Sign in to Yui</h1>
|
|
@@ -1,51 +1,63 @@
|
|
|
1
1
|
import { STYLESHEETS } from "../styles/index.js";
|
|
2
2
|
import { DEFAULT_DARK_THEME } from "../shared/themes.js";
|
|
3
|
+
import { BRAND_ICONS } from "../shared/brand.js";
|
|
3
4
|
import { iconSvg } from "../shared/icons.js";
|
|
5
|
+
import { appRail } from "./appRail.js";
|
|
4
6
|
import { MANAGE_SECTIONS } from "../shared/manageNav.js";
|
|
5
|
-
// One frame and entry point for /models/* and /settings/*. The
|
|
6
|
-
//
|
|
7
|
-
// the
|
|
7
|
+
// One frame and entry point for /models/* and /settings/*. The frame is the
|
|
8
|
+
// workbench's own: the global rail, a collapsible list (here the current
|
|
9
|
+
// area's sections), and the center column under the shared top bar. Narrow
|
|
10
|
+
// screens open the rail and the list together as a drawer, exactly as on the
|
|
11
|
+
// workbench. The shared router changes sections without unloading their
|
|
12
|
+
// controllers or drafts. Leaving the management document still uses the
|
|
13
|
+
// browser's unsaved-draft prompt.
|
|
8
14
|
const escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """);
|
|
9
15
|
const sectionLink = (item) => `<a class="manage-link" href="/${item.section === "connection" ? "settings" : item.page}/${item.section}" data-page="${item.page}" data-section="${item.section}"><span data-i18n="${item.key}">${escapeHtml(item.label)}</span><i class="manage-dirty" aria-hidden="true"></i></a>`;
|
|
10
|
-
const area = (name,
|
|
16
|
+
const area = (name, key, label) => `
|
|
11
17
|
<div class="manage-area" data-area-group="${name}">
|
|
12
|
-
<a class="manage-primary" href="/${name}" data-area="${name}">${iconSvg(icon)}<span data-i18n="${key}">${label}</span></a>
|
|
13
18
|
<div class="manage-sections" role="group" aria-label="${label}" data-i18n-aria-label="${key}">
|
|
14
19
|
${MANAGE_SECTIONS[name].map(sectionLink).join("\n ")}
|
|
15
20
|
</div>
|
|
16
21
|
</div>`;
|
|
17
|
-
const RAIL = `<aside class="manage-rail">
|
|
18
|
-
<header class="
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
</a>
|
|
22
|
-
<label class="manage-locale"><span class="sr-only" data-i18n="settings.language">Language</span><select id="locale-select"><option value="en">English</option><option value="zh-CN">简体中文</option></select></label>
|
|
22
|
+
const RAIL = `<aside class="manage-rail sidebar" aria-label="Sections" data-i18n-aria-label="manage.sections">
|
|
23
|
+
<header class="side-head">
|
|
24
|
+
<h2 id="manage-area-title" class="side-title"></h2>
|
|
25
|
+
<button id="sidebar-collapse" class="icon-btn side-collapse" type="button" aria-label="Collapse sections" data-i18n-aria-label="manage.collapseSections" title="Collapse sections · [" data-i18n-title="manage.collapseSections">${iconSvg("sidebar")}</button>
|
|
23
26
|
</header>
|
|
24
|
-
<nav class="manage-nav" aria-label="
|
|
25
|
-
<div class="manage-area" data-area-group="workbench">
|
|
26
|
-
<a class="manage-primary" href="/" data-area="workbench">${iconSvg("inbox")}<span data-i18n="manage.workbench">Workbench</span></a>
|
|
27
|
-
</div>${area("models", "layers", "models.title", "Models")}${area("settings", "settings", "settings.title", "Settings")}
|
|
27
|
+
<nav class="manage-nav" aria-label="Sections" data-i18n-aria-label="manage.sections">${area("models", "models.title", "Models")}${area("settings", "settings.title", "Settings")}
|
|
28
28
|
</nav>
|
|
29
29
|
</aside>`;
|
|
30
|
+
const TOP_BAR = `<header class="top-bar">
|
|
31
|
+
<button class="icon-btn side-expand" type="button" data-sidebar-toggle="true" aria-expanded="false" aria-label="Show sections" data-i18n-aria-label="manage.showSections" title="Show sections · [" data-i18n-title="manage.showSections"><span class="i-sidebar">${iconSvg("sidebar")}</span><span class="i-menu">${iconSvg("menu")}</span></button>
|
|
32
|
+
<nav class="crumbs" aria-label="Location" data-i18n-aria-label="crumbs.label"><span id="manage-crumb-area" class="crumb crumb-static"></span><span class="crumb-sep" aria-hidden="true">/</span><span id="manage-crumb-section" class="crumb-current"></span></nav>
|
|
33
|
+
<span class="spacer"></span>
|
|
34
|
+
<label class="top-locale"><span class="sr-only" data-i18n="settings.language">Language</span><select id="locale-select"><option value="en">English</option><option value="zh-CN">简体中文</option></select></label>
|
|
35
|
+
</header>`;
|
|
30
36
|
// One management document. `main` is the page's reading column markup.
|
|
31
37
|
export function manageDocument(options) {
|
|
32
38
|
return `<!doctype html>
|
|
33
39
|
<html lang="en" data-theme="${DEFAULT_DARK_THEME.id}">
|
|
34
40
|
<head>
|
|
35
|
-
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
41
|
+
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
36
42
|
<meta name="yui-web-token" content="__YUI_WEB_TOKEN__">
|
|
43
|
+
<link rel="icon" type="image/svg+xml" href="${BRAND_ICONS[DEFAULT_DARK_THEME.id]}">
|
|
37
44
|
<script src="/assets/theme.js"></script>
|
|
38
|
-
<
|
|
45
|
+
<title>Yui · ${options.title}</title>
|
|
39
46
|
${STYLESHEETS.map(s => `<link rel="stylesheet" href="/assets/css/${s.name}.css">`).join("\n ")}
|
|
40
47
|
</head>
|
|
41
48
|
<body class="manage-body ${options.page}-body">
|
|
42
|
-
<div class="manage-app" data-page="${options.page}" hidden>
|
|
49
|
+
<div class="app manage-app" data-page="${options.page}" hidden>
|
|
50
|
+
${appRail(options.page === "models" ? "models" : "settings")}
|
|
43
51
|
${RAIL}
|
|
44
|
-
<
|
|
52
|
+
<div id="sidebar-scrim" class="sidebar-scrim"></div>
|
|
53
|
+
<main id="manage-scroll" class="center manage-center" tabindex="-1">
|
|
54
|
+
${TOP_BAR}
|
|
55
|
+
<div class="manage-main" id="manage-main">
|
|
45
56
|
${options.main}
|
|
57
|
+
</div>
|
|
46
58
|
</main>
|
|
47
59
|
</div>
|
|
48
|
-
<script type="module" src="/assets/js/app/manage.js"></script>
|
|
60
|
+
<script type="module" src="/assets/js/app/manage.js" blocking="render"></script>
|
|
49
61
|
</body></html>`;
|
|
50
62
|
}
|
|
51
63
|
// The common section heading: title and one explanatory line.
|
|
@@ -10,7 +10,7 @@ export const SETTINGS_MAIN = `
|
|
|
10
10
|
<fieldset class="field"><legend data-i18n="settings.theme">Theme</legend><div id="theme-options" class="theme-options"></div></fieldset>
|
|
11
11
|
<div class="settings-stack">
|
|
12
12
|
<label class="field"><span data-i18n="settings.language">Language</span><select id="settings-locale"><option value="en">English</option><option value="zh-CN">简体中文</option></select></label>
|
|
13
|
-
<label class="field"><span data-i18n="settings.accessMode">Default session access</span><select id="access-mode"><option value="
|
|
13
|
+
<label class="field"><span data-i18n="settings.accessMode">Default session access</span><select id="access-mode"><option value="structured" data-i18n="settings.structured">Structured view</option><option value="native" data-i18n="settings.native">Native terminal</option></select></label>
|
|
14
14
|
<p data-i18n="settings.accessHelp">Preference for subsequent session selection where supported. It does not switch a live Session.</p>
|
|
15
15
|
<button id="reset-layout" type="button" class="btn" data-i18n="settings.resetLayout">Reset saved workspace layout</button>
|
|
16
16
|
<p id="browser-receipt" role="status"></p>
|
|
@@ -6,24 +6,27 @@ import { iconSvg } from "../shared/icons.js";
|
|
|
6
6
|
// The string keeps its indentation inside .app and has no trailing newline.
|
|
7
7
|
export const SIDEBAR_HTML = ` <aside class="sidebar" aria-label="Tasks" data-i18n-aria-label="sidebar.label">
|
|
8
8
|
<header class="side-head">
|
|
9
|
-
<
|
|
10
|
-
<span class="brand-mark" aria-hidden="true">結</span>
|
|
11
|
-
<span class="brand-name">Yui</span>
|
|
12
|
-
</div>
|
|
9
|
+
<h2 class="side-title" data-i18n="sidebar.label">Tasks</h2>
|
|
13
10
|
<span class="sync" id="sync-state" title="Last sync" data-i18n-title="sync.label">
|
|
14
11
|
<i aria-hidden="true"></i><time id="last-sync">—</time>
|
|
15
12
|
</span>
|
|
16
13
|
<button id="refresh" class="icon-btn" type="button" aria-label="Refresh" data-i18n-aria-label="actions.refresh" title="Refresh · R">${iconSvg("refresh")}</button>
|
|
14
|
+
<button id="sidebar-collapse" class="icon-btn side-collapse" type="button" aria-label="Collapse task list" data-i18n-aria-label="sidebar.collapse" title="Collapse task list · [" data-i18n-title="sidebar.collapse">${iconSvg("sidebar")}</button>
|
|
17
15
|
</header>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
<div class="side-tools">
|
|
17
|
+
<button id="create-task" class="btn btn-primary side-create" type="button">${iconSvg("plus")}<span data-i18n="create.title">New task</span></button>
|
|
18
|
+
<div class="side-search">
|
|
19
|
+
<label class="search-field">
|
|
20
|
+
${iconSvg("search")}
|
|
21
|
+
<span class="sr-only" data-i18n="search.label">Search tasks</span>
|
|
22
|
+
<input id="search" type="search" autocomplete="off" spellcheck="false" placeholder="Search tasks" data-i18n-placeholder="search.placeholder">
|
|
23
|
+
<kbd>/</kbd>
|
|
24
|
+
</label>
|
|
25
|
+
<button id="body-search-open" class="icon-btn side-search-body" type="button" aria-label="Search task contents" data-i18n-aria-label="bodySearch.title" title="Search task contents" data-i18n-title="bodySearch.title">${iconSvg("textSearch")}</button>
|
|
26
|
+
</div>
|
|
27
|
+
<div id="catalog-source" class="seg seg-sm source-seg" role="radiogroup" aria-label="Task category" data-i18n-aria-label="jobs.taskCategory"></div>
|
|
28
|
+
<div id="attention-bar" class="attention-bar" aria-live="polite"></div>
|
|
29
|
+
</div>
|
|
27
30
|
<div id="status-filters" class="status-tabs" role="group" aria-label="Filter by status" data-i18n-aria-label="filters.label"></div>
|
|
28
31
|
<div id="task-list" class="task-list" aria-label="Tasks" data-i18n-aria-label="sidebar.label" aria-live="polite"></div>
|
|
29
32
|
<div id="catalog-controls" class="pager" aria-live="polite">
|
|
@@ -32,15 +35,5 @@ export const SIDEBAR_HTML = ` <aside class="sidebar" aria-label="Tasks" data-
|
|
|
32
35
|
<button id="catalog-next" class="pager-btn" type="button" data-i18n="catalog.next" disabled>Next page</button>
|
|
33
36
|
<button id="catalog-attention-reset" class="pager-clear" type="button" data-i18n="catalog.clearAttention" hidden>Clear attention filter</button>
|
|
34
37
|
</div>
|
|
35
|
-
<footer class="side-foot">
|
|
36
|
-
<button id="operator-terminal" class="foot-btn" type="button" title="Operator session · O">
|
|
37
|
-
${iconSvg("terminal")}<span data-i18n="actions.operator">Operator</span><kbd>O</kbd>
|
|
38
|
-
</button>
|
|
39
|
-
<button id="global-input-open" class="foot-btn" type="button" title="Global input" data-i18n-title="global.open">
|
|
40
|
-
${iconSvg("broadcast")}<span data-i18n="global.open">Global input</span>
|
|
41
|
-
</button>
|
|
42
|
-
<a id="models-open" class="icon-btn" href="/models" aria-label="Models" data-i18n-aria-label="models.title" title="Models" data-i18n-title="models.title">${iconSvg("layers")}</a>
|
|
43
|
-
<button id="settings-open" class="icon-btn" type="button" aria-label="Settings" data-i18n-aria-label="settings.title" title="Settings">${iconSvg("settings")}</button>
|
|
44
|
-
</footer>
|
|
45
38
|
</aside>
|
|
46
39
|
<div id="sidebar-divider" class="sidebar-divider" role="separator" tabindex="0" aria-orientation="vertical" aria-valuemin="${SIDEBAR.min}" aria-valuemax="${SIDEBAR.max}" aria-valuenow="${SIDEBAR.compact}" aria-label="Resize task list" data-i18n-aria-label="sidebar.resize" title="Drag to resize · double-click to reset" data-i18n-title="sidebar.resizeHint"><span aria-hidden="true"></span></div>`;
|
|
@@ -28,7 +28,7 @@ input:disabled,select:disabled,textarea:disabled{opacity:.55}
|
|
|
28
28
|
::-webkit-scrollbar-track{background:transparent}
|
|
29
29
|
[hidden]{display:none!important}
|
|
30
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:
|
|
31
|
+
.skip-link{position:fixed;left:12px;top:-60px;z-index:var(--z-skip);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
32
|
.skip-link:focus{top:12px}
|
|
33
33
|
.icon{width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
|
|
34
34
|
.icon-sm{width:14px;height:14px}
|
|
@@ -3,6 +3,7 @@ import { BASE_STYLES } from "./base.js";
|
|
|
3
3
|
import { RESPONSIVE_STYLES } from "./layout/responsive.js";
|
|
4
4
|
import { WORKSPACE_STYLES } from "./layout/workspace.js";
|
|
5
5
|
import { MARKDOWN_STYLES } from "./markdown.js";
|
|
6
|
+
import { MOTION_STYLES } from "./motion.js";
|
|
6
7
|
import { TOKEN_STYLES } from "./tokens.js";
|
|
7
8
|
import { CONTROL_STYLES } from "./ui/controls.js";
|
|
8
9
|
import { FORM_STYLES } from "./ui/forms.js";
|
|
@@ -29,6 +30,7 @@ export const STYLESHEETS = Object.freeze([
|
|
|
29
30
|
{ name: "fonts", body: FONT_FACE_STYLES },
|
|
30
31
|
{ name: "tokens", body: TOKEN_STYLES },
|
|
31
32
|
{ name: "base", body: BASE_STYLES },
|
|
33
|
+
{ name: "motion", body: MOTION_STYLES },
|
|
32
34
|
{ name: "layout/workspace", body: WORKSPACE_STYLES },
|
|
33
35
|
{ name: "ui/controls", body: CONTROL_STYLES },
|
|
34
36
|
{ name: "ui/surfaces", body: SURFACE_STYLES },
|
|
@@ -1,38 +1,83 @@
|
|
|
1
|
-
import { BREAKPOINTS, SIDEBAR } from "../../shared/geometry.js";
|
|
1
|
+
import { BREAKPOINTS, DOCK_FOCUS_MAX, SIDEBAR } from "../../shared/geometry.js";
|
|
2
2
|
/*
|
|
3
3
|
* RESPONSIVE — breakpoints only, from shared/geometry.ts.
|
|
4
4
|
* ≤ wide compact sidebar default and a three-column metric strip
|
|
5
|
-
* ≤
|
|
6
|
-
*
|
|
5
|
+
* ≤ focus an open dock moves the task list out of the grid so the task
|
|
6
|
+
* column keeps its minimum width; it opens as a drawer instead
|
|
7
|
+
* ≤ narrow the workspace takes the screen; the rail and the list open
|
|
8
|
+
* together as one drawer, and the dock becomes a full-screen sheet
|
|
7
9
|
* ≤ small phone layout for page content
|
|
8
|
-
*
|
|
10
|
+
*
|
|
11
|
+
* Motion follows the native sheet pattern (Vaul, Radix Sheet, iOS): drawers
|
|
12
|
+
* and sheets stay rendered off screen and move only by transform, so opening
|
|
13
|
+
* and closing both animate on the compositor; the scrim only fades. A drag
|
|
14
|
+
* (layout/sidebarNav) moves the panels directly and the release continues
|
|
15
|
+
* from where the finger left them.
|
|
9
16
|
*/
|
|
10
17
|
export const RESPONSIVE_STYLES = `
|
|
11
|
-
@container sidebar (max-width:299px){
|
|
12
|
-
.foot-btn kbd{display:none}
|
|
13
|
-
}
|
|
14
|
-
@container sidebar (max-width:259px){
|
|
15
|
-
.foot-btn{gap:0;padding:0 9px}
|
|
16
|
-
.foot-btn span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
17
|
-
}
|
|
18
18
|
@media(max-width:${BREAKPOINTS.wide}px){
|
|
19
19
|
:root{--sidebar-w:${SIDEBAR.compact}px}
|
|
20
20
|
.metric-strip{grid-template-columns:repeat(3,minmax(0,1fr))}
|
|
21
21
|
}
|
|
22
|
+
@media(min-width:${BREAKPOINTS.narrow + 1}px) and (max-width:${DOCK_FOCUS_MAX}px){
|
|
23
|
+
body.dock-open .app{grid-template-areas:"rail center divider dock";grid-template-columns:var(--rail-w) minmax(0,1fr) var(--divider-w) var(--dock-w)}
|
|
24
|
+
body.dock-open.dock-center .app{grid-template-areas:"rail dock divider center";grid-template-columns:var(--rail-w) var(--dock-w) var(--divider-w) minmax(0,1fr)}
|
|
25
|
+
body.dock-open .sidebar-divider{display:none}
|
|
26
|
+
body.dock-open .side-expand{display:inline-grid}
|
|
27
|
+
/* The list slides out from under the rail. */
|
|
28
|
+
body.dock-open .app-rail{position:relative;z-index:var(--z-rail-over)}
|
|
29
|
+
body.dock-open.sidebar-collapsed .app .sidebar{display:flex}
|
|
30
|
+
body.dock-open .app .sidebar{--drawer-w:min(340px,calc(100vw - var(--rail-w) - 24px));display:flex;position:fixed;top:0;bottom:0;left:var(--rail-w);z-index:var(--z-drawer);width:var(--drawer-w);border-right:1px solid var(--line-2);box-shadow:var(--shadow-2);transform:translate3d(calc(-100% - 32px),0,0);visibility:hidden;transition:transform var(--t-sheet-out) var(--ease-sheet),visibility 0s linear var(--t-sheet-out)}
|
|
31
|
+
}
|
|
22
32
|
@media(max-width:${BREAKPOINTS.narrow}px){
|
|
33
|
+
:root{--drawer-w:min(320px,calc(100vw - var(--rail-w) - 48px));--drawer-off:calc(-1 * (var(--rail-w) + var(--drawer-w)) - 32px)}
|
|
23
34
|
.app,body.dock-open .app,body.dock-open.dock-center .app{grid-template-areas:"main";grid-template-columns:minmax(0,1fr)}
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
|
|
27
|
-
body.
|
|
28
|
-
.
|
|
29
|
-
.
|
|
35
|
+
.center{grid-area:main}
|
|
36
|
+
/* One drawer: the rail and the list, side by side, moving together. */
|
|
37
|
+
.app .app-rail,.app .sidebar{display:flex;position:fixed;top:0;bottom:0;z-index:var(--z-drawer);padding-top:env(safe-area-inset-top,0px);transform:translate3d(var(--drawer-off),0,0);visibility:hidden;transition:transform var(--t-sheet-out) var(--ease-sheet),visibility 0s linear var(--t-sheet-out);touch-action:pan-y}
|
|
38
|
+
body.sidebar-collapsed .app .sidebar{display:flex}
|
|
39
|
+
.app .app-rail{left:0;width:var(--rail-w);padding-top:calc(10px + env(safe-area-inset-top,0px))}
|
|
40
|
+
.app .sidebar{left:var(--rail-w);width:var(--drawer-w);border-right:1px solid var(--line-2);border-radius:0 var(--r-lg) var(--r-lg) 0;box-shadow:var(--shadow-2)}
|
|
41
|
+
.icon-btn.side-expand{display:inline-grid}
|
|
42
|
+
.side-expand .i-sidebar{display:none}
|
|
43
|
+
.side-expand .i-menu{display:contents}
|
|
44
|
+
/* Top bar: the current place is the title. */
|
|
45
|
+
.top-bar{padding:0 12px 0 8px}
|
|
46
|
+
.crumbs>span.crumb,.crumbs>span.crumb+.crumb-sep{display:none}
|
|
47
|
+
.crumbs span.crumb-current{font-size:var(--fs-lg)}
|
|
30
48
|
.dock-divider,.sidebar-divider{display:none}
|
|
31
|
-
.dock{position:fixed;inset:0;z-index:
|
|
49
|
+
.dock{position:fixed;inset:0;z-index:var(--z-sheet);padding-top:env(safe-area-inset-top,0px);padding-bottom:env(safe-area-inset-bottom,0px);animation:m-sheet-up var(--t-sheet) var(--ease-sheet)}
|
|
32
50
|
#dock-swap{display:none}
|
|
33
51
|
.dock-toggle span{display:none}
|
|
34
52
|
.dock-toggle{width:32px;padding:0}
|
|
35
53
|
}
|
|
54
|
+
/* Open drawer (sidebarNav sets body.sidebar-drawer only where the list
|
|
55
|
+
cannot sit inline). Opening eases in a little slower than closing. */
|
|
56
|
+
body.sidebar-drawer .app .sidebar,body.sidebar-drawer .app .app-rail{transform:translate3d(0,0,0);visibility:visible;transition:transform var(--t-sheet) var(--ease-sheet),visibility 0s}
|
|
57
|
+
body.sidebar-drawer .app .sidebar-divider{display:none}
|
|
58
|
+
.sidebar-scrim{position:fixed;inset:0;z-index:var(--z-scrim);background:rgb(12 10 8 / .4);opacity:0;visibility:hidden;pointer-events:none;transition:opacity var(--t-sheet-out) var(--ease-sheet),visibility 0s linear var(--t-sheet-out)}
|
|
59
|
+
body.sidebar-drawer .sidebar-scrim{opacity:1;visibility:visible;pointer-events:auto;transition:opacity var(--t-sheet) var(--ease-sheet),visibility 0s}
|
|
60
|
+
/* While a finger drags the drawer, it follows directly. */
|
|
61
|
+
body.drawer-dragging .app .sidebar,body.drawer-dragging .app .app-rail,body.drawer-dragging .sidebar-scrim{transition:none}
|
|
62
|
+
@media(prefers-reduced-motion:reduce){
|
|
63
|
+
.app .sidebar,.app .app-rail,.sidebar-scrim,body.sidebar-drawer .app .sidebar,body.sidebar-drawer .app .app-rail,body.sidebar-drawer .sidebar-scrim{transition:none}
|
|
64
|
+
.dock{animation:none}
|
|
65
|
+
}
|
|
66
|
+
/* Touch screens: controls reach a comfortable tap size without changing the
|
|
67
|
+
desktop density. */
|
|
68
|
+
@media(pointer:coarse){
|
|
69
|
+
.btn,.icon-btn,.dock-toggle{min-height:38px}
|
|
70
|
+
.icon-btn{min-width:38px}
|
|
71
|
+
.dock-toggle{min-width:38px}
|
|
72
|
+
.seg-btn,.seg-sm .seg-btn{height:36px}
|
|
73
|
+
.cv-pill{height:36px}
|
|
74
|
+
.cv-link{min-height:32px}
|
|
75
|
+
.chip-tab{height:34px}
|
|
76
|
+
.btn-link{min-height:32px;padding-top:6px;padding-bottom:6px}
|
|
77
|
+
.cv-activity>summary{min-height:32px}
|
|
78
|
+
.cv-sub>summary{padding:8px 0}
|
|
79
|
+
.task-row{min-height:48px}
|
|
80
|
+
}
|
|
36
81
|
@media(max-width:${BREAKPOINTS.small}px){
|
|
37
82
|
.metric-strip{grid-template-columns:repeat(2,minmax(0,1fr))}
|
|
38
83
|
.page-head{padding-top:24px;flex-wrap:wrap}
|
|
@@ -46,7 +91,6 @@ export const RESPONSIVE_STYLES = `
|
|
|
46
91
|
.now-card>.card-body{padding:14px 14px 14px 17px}
|
|
47
92
|
.kv{grid-template-columns:minmax(0,1fr)}
|
|
48
93
|
.kv dt{margin-top:4px}
|
|
49
|
-
.foot-btn kbd{display:none}
|
|
50
94
|
.lazy-meta{display:none}
|
|
51
95
|
.lazy-body{padding-left:16px}
|
|
52
96
|
}
|
|
@@ -1,18 +1,58 @@
|
|
|
1
|
+
import { RAIL_WIDTH } from "../../shared/geometry.js";
|
|
1
2
|
/*
|
|
2
|
-
* WORKSPACE — the
|
|
3
|
-
* sidebar | center (task details) | divider | dock (session)
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* WORKSPACE — the application frame.
|
|
4
|
+
* rail | sidebar | center (task details) | divider | dock (session)
|
|
5
|
+
* The rail is the global Workbench / Models / Settings navigation, shared with
|
|
6
|
+
* the management pages. body.dock-open shows the dock; body.dock-center swaps
|
|
7
|
+
* the dock into the middle so the order becomes rail | sidebar | dock |
|
|
8
|
+
* divider | center. body.sidebar-collapsed folds the task list away (the page
|
|
9
|
+
* header then offers .side-expand to bring it back). The sidebar's own
|
|
10
|
+
* divider overlays its right edge, so it adds no grid column; --sidebar-w is
|
|
11
|
+
* the default width and a stored preference overrides it inline. Region
|
|
8
12
|
* contents are styled by the view stylesheets.
|
|
9
13
|
*/
|
|
10
14
|
export const WORKSPACE_STYLES = `
|
|
15
|
+
:root{--rail-w:${RAIL_WIDTH}px}
|
|
11
16
|
.app{display:grid;height:100vh;height:100dvh;grid-template-rows:minmax(0,1fr);
|
|
12
|
-
grid-template-areas:"sidebar center divider dock";
|
|
13
|
-
grid-template-columns:var(--sidebar-w) minmax(0,1fr) 0 0}
|
|
14
|
-
body.dock-open .app{grid-template-columns:var(--sidebar-w) minmax(0,1fr) var(--divider-w) var(--dock-w)}
|
|
15
|
-
body.dock-open.dock-center .app{grid-template-areas:"sidebar dock divider center";grid-template-columns:var(--sidebar-w) var(--dock-w) var(--divider-w) minmax(0,1fr)}
|
|
17
|
+
grid-template-areas:"rail sidebar center divider dock";
|
|
18
|
+
grid-template-columns:var(--rail-w) var(--sidebar-w) minmax(0,1fr) 0 0}
|
|
19
|
+
body.dock-open .app{grid-template-columns:var(--rail-w) var(--sidebar-w) minmax(0,1fr) var(--divider-w) var(--dock-w)}
|
|
20
|
+
body.dock-open.dock-center .app{grid-template-areas:"rail sidebar dock divider center";grid-template-columns:var(--rail-w) var(--sidebar-w) var(--dock-w) var(--divider-w) minmax(0,1fr)}
|
|
21
|
+
body.sidebar-collapsed{--sidebar-w:0px}
|
|
22
|
+
body.sidebar-collapsed:not(.sidebar-drawer) .sidebar,body.sidebar-collapsed .sidebar-divider{display:none}
|
|
23
|
+
|
|
24
|
+
/* Global rail: brand, then the three areas as icon-over-label tiles. */
|
|
25
|
+
.app-rail{grid-area:rail;display:flex;flex-direction:column;align-items:center;gap:6px;min-height:0;padding:10px 0 12px;background:var(--surface);border-right:1px solid var(--line);overflow-y:auto}
|
|
26
|
+
.rail-brand{display:grid;place-items:center;width:40px;height:40px;margin-bottom:6px;border-radius:var(--r-md);text-decoration:none}
|
|
27
|
+
.rail-brand .brand-mark{display:grid;place-items:center}
|
|
28
|
+
.rail-brand:hover .brand-mark{box-shadow:0 0 0 3px var(--accent-soft)}
|
|
29
|
+
.rail-items{display:flex;flex-direction:column;align-items:center;gap:4px}
|
|
30
|
+
.rail-item{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;width:48px;min-height:50px;padding:6px 2px;border-radius:var(--r-md);color:var(--ink-3);font-size:10.5px;font-weight:550;line-height:1.15;text-align:center;text-decoration:none;transition:background var(--t-fast),color var(--t-fast),transform var(--t-fast) var(--ease)}
|
|
31
|
+
.rail-item .icon{width:18px;height:18px;flex:none}
|
|
32
|
+
.rail-item span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
33
|
+
.rail-item:hover{background:var(--surface-3);color:var(--ink)}
|
|
34
|
+
.rail-item[aria-current="page"]{background:var(--accent-soft);color:var(--accent)}
|
|
35
|
+
|
|
36
|
+
/* Task-list switch in the page header: only while the list is out of view. */
|
|
37
|
+
.icon-btn.side-expand{display:none}
|
|
38
|
+
.side-expand .i-menu{display:none}
|
|
39
|
+
.side-expand>span{display:contents}
|
|
40
|
+
body.sidebar-collapsed .side-expand{display:inline-grid}
|
|
41
|
+
.side-title{flex:1;min-width:0;font-size:var(--fs-md);font-weight:650;letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
42
|
+
.sidebar-scrim[hidden]{display:none}
|
|
43
|
+
|
|
44
|
+
/* Top bar: the first row of the center column on every page. Its height and
|
|
45
|
+
rule line up with the task-list head and the dock head, so the frame reads
|
|
46
|
+
as one header row: list switch, location, then the page's actions. */
|
|
47
|
+
.top-bar{position:sticky;top:0;z-index:var(--z-sticky);display:flex;align-items:center;gap:6px;height:var(--header-h);padding:0 16px 0 12px;border-bottom:1px solid var(--line);background:var(--canvas)}
|
|
48
|
+
.top-title{min-width:0;overflow:hidden;color:var(--ink);font-size:var(--fs-md);font-weight:600;text-overflow:ellipsis;white-space:nowrap}
|
|
49
|
+
.crumbs{display:flex;align-items:center;gap:4px;min-width:0;font-size:var(--fs-sm);white-space:nowrap}
|
|
50
|
+
.crumb,.crumb-sep{flex:none}
|
|
51
|
+
.crumb{padding:3px 6px;border:0;border-radius:var(--r-xs);background:transparent;color:var(--ink-3);font-size:var(--fs-sm)}
|
|
52
|
+
button.crumb:hover{color:var(--ink);background:var(--surface-3)}
|
|
53
|
+
.crumb-sep{color:var(--ink-4)}
|
|
54
|
+
.crumb-current{min-width:0;overflow:hidden;padding:3px 6px;color:var(--ink);font-weight:600;text-overflow:ellipsis}
|
|
55
|
+
code.crumb-current{font-weight:500;font-size:var(--fs-xs);color:var(--ink-2)}
|
|
16
56
|
|
|
17
57
|
/* Resize dividers: a wide invisible hit area around a 1px rule that lights up
|
|
18
58
|
on hover, focus and while dragging. */
|
|
@@ -25,16 +65,13 @@ body.sidebar-resizing,body.dock-resizing{cursor:col-resize;user-select:none}
|
|
|
25
65
|
|
|
26
66
|
/* Sidebar */
|
|
27
67
|
.sidebar{grid-area:sidebar;display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--surface);border-right:1px solid var(--line);container:sidebar/inline-size}
|
|
28
|
-
.sidebar-divider{grid-area:sidebar;justify-self:end;z-index:
|
|
68
|
+
.sidebar-divider{grid-area:sidebar;justify-self:end;z-index:var(--z-header);width:var(--divider-w);margin-right:-5px}
|
|
29
69
|
.sidebar-divider span{left:3px;background:transparent}
|
|
30
70
|
.side-head{flex:none;display:flex;align-items:center;gap:10px;height:var(--header-h);padding:0 12px 0 16px;border-bottom:1px solid var(--line)}
|
|
31
|
-
.
|
|
32
|
-
.attention-bar{flex:none;margin:10px 12px 0}
|
|
33
|
-
.status-tabs{flex:none;margin:10px 0 0;padding:0 12px}
|
|
71
|
+
.status-tabs{flex:none;margin:4px 0 0;padding:0 12px}
|
|
34
72
|
.task-list{flex:1;min-height:0;overflow-y:auto;padding:4px 8px 12px;scrollbar-gutter:stable}
|
|
35
73
|
.pager{flex:none;display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:6px;padding:8px 12px;border-top:1px solid var(--line)}
|
|
36
74
|
.pager .pager-clear{grid-column:1/-1}
|
|
37
|
-
.side-foot{flex:none;display:flex;align-items:center;gap:4px;padding:10px;border-top:1px solid var(--line)}
|
|
38
75
|
|
|
39
76
|
/* Center */
|
|
40
77
|
.center{grid-area:center;min-width:0;min-height:0;overflow-y:auto;scrollbar-gutter:stable;background:var(--canvas)}
|
|
@@ -42,7 +79,7 @@ body.sidebar-resizing,body.dock-resizing{cursor:col-resize;user-select:none}
|
|
|
42
79
|
.detail{min-height:100%}
|
|
43
80
|
|
|
44
81
|
/* Dock */
|
|
45
|
-
.dock-divider{grid-area:divider;background:var(--canvas);z-index:
|
|
82
|
+
.dock-divider{grid-area:divider;background:var(--canvas);z-index:var(--z-raised)}
|
|
46
83
|
.dock-divider span{left:4px;background:var(--line-2)}
|
|
47
84
|
.dock{grid-area:dock;display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--surface)}
|
|
48
85
|
body.dock-open:not(.dock-center) .dock{border-left:0}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BREAKPOINTS } from "../shared/geometry.js";
|
|
2
|
+
import { motionKeyframes } from "../shared/motion.js";
|
|
3
|
+
/*
|
|
4
|
+
* MOTION — the shared animation library. Timings, curves and layers are the
|
|
5
|
+
* tokens generated from shared/motion.ts; this sheet holds the keyframes and
|
|
6
|
+
* the component motion that is the same everywhere:
|
|
7
|
+
* dialogs scale + fade in, quicker fade out; the backdrop fades
|
|
8
|
+
* (@starting-style + allow-discrete, as Radix Dialog does)
|
|
9
|
+
* popovers <details> menus pop in from their anchor
|
|
10
|
+
* press buttons settle slightly under the pointer
|
|
11
|
+
* pages cross-document View Transitions between Workbench, Models
|
|
12
|
+
* and Settings; the rail stays put
|
|
13
|
+
* layout same-document View Transitions for column changes (the
|
|
14
|
+
* client wraps sidebar collapse and dock open/close)
|
|
15
|
+
* Everything honours prefers-reduced-motion (base.ts turns it all off).
|
|
16
|
+
*/
|
|
17
|
+
export const MOTION_STYLES = `
|
|
18
|
+
${motionKeyframes()}
|
|
19
|
+
.dialog{opacity:0;transform:scale(.96);transition:opacity var(--t-exit) var(--ease-exit),transform var(--t-exit) var(--ease-exit),overlay var(--t-exit) allow-discrete,display var(--t-exit) allow-discrete}
|
|
20
|
+
.dialog[open]{opacity:1;transform:none;transition-duration:var(--t-base);transition-timing-function:var(--ease-standard)}
|
|
21
|
+
.dialog::backdrop{opacity:0;transition:opacity var(--t-exit) var(--ease-exit),overlay var(--t-exit) allow-discrete,display var(--t-exit) allow-discrete}
|
|
22
|
+
.dialog[open]::backdrop{opacity:1;transition-duration:var(--t-base)}
|
|
23
|
+
@starting-style{.dialog[open]{opacity:0;transform:scale(.96)}.dialog[open]::backdrop{opacity:0}}
|
|
24
|
+
details[open]>.cv-popbody,details[open]>.dock-target-menu{animation:m-pop var(--t-base) var(--ease-standard)}
|
|
25
|
+
.cv-attach>.cv-popbody,.cv-model>.cv-popbody{transform-origin:bottom left}
|
|
26
|
+
.cv-model>.cv-popbody{transform-origin:bottom right}
|
|
27
|
+
.cv-menu>.cv-popbody{transform-origin:top right}
|
|
28
|
+
.dock-target-menu{transform-origin:top left}
|
|
29
|
+
.icon-btn:active:not(:disabled),.rail-item:active{transform:scale(.94)}
|
|
30
|
+
/* Workbench ⇄ Models ⇄ Settings are separate documents: the browser
|
|
31
|
+
cross-fades them (no blank frame) and the rail stays put between them. */
|
|
32
|
+
@view-transition{navigation:auto}
|
|
33
|
+
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--t-page);animation-timing-function:var(--ease-sheet)}
|
|
34
|
+
@media(prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}
|
|
35
|
+
/* Page content entering after an explicit navigation (client/lib/motion). */
|
|
36
|
+
.m-enter{animation:m-rise var(--t-base) var(--ease-standard)}
|
|
37
|
+
@media(min-width:${BREAKPOINTS.narrow + 1}px){
|
|
38
|
+
.app-rail{view-transition-name:app-rail}
|
|
39
|
+
/* Column changes: client/lib/motion sets .vt-layout for the transition
|
|
40
|
+
only. Each column glides between its old and new box showing its new
|
|
41
|
+
content (no double image); a column that appears or leaves fades. */
|
|
42
|
+
.vt-layout .sidebar{view-transition-name:sidebar}
|
|
43
|
+
.vt-layout .center{view-transition-name:center}
|
|
44
|
+
.vt-layout .dock{view-transition-name:dock}
|
|
45
|
+
::view-transition-group(sidebar),::view-transition-group(center),::view-transition-group(dock){animation-duration:var(--t-layout);animation-timing-function:var(--ease-sheet);overflow:clip}
|
|
46
|
+
::view-transition-old(sidebar),::view-transition-new(sidebar),::view-transition-old(center),::view-transition-new(center),::view-transition-old(dock),::view-transition-new(dock){width:100%;height:100%;object-fit:none;object-position:0 0;animation:none}
|
|
47
|
+
::view-transition-old(sidebar),::view-transition-old(center),::view-transition-old(dock){opacity:0}
|
|
48
|
+
::view-transition-old(sidebar):only-child,::view-transition-old(dock):only-child{opacity:1;animation:m-fade var(--t-exit) var(--ease-exit) reverse both}
|
|
49
|
+
::view-transition-new(sidebar):only-child,::view-transition-new(dock):only-child{animation:m-fade var(--t-layout) var(--ease-standard) both}
|
|
50
|
+
}
|
|
51
|
+
`;
|