@zq-silk/yui 2.14.0 → 2.16.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/config/configCatalog.js +1 -1
- package/dist/controlPlaneCli.js +1 -1
- package/dist/job/projectSync.js +3 -3
- package/dist/storage/sqliteSchema.js +12 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/upgrade/projectSyncClockMigration.js +30 -0
- package/dist/web/assets/assetManifest.js +23 -1
- package/dist/web/assets/client/app/catalog.js +40 -14
- package/dist/web/assets/client/app/login.js +156 -0
- package/dist/web/assets/client/app/main.js +33 -9
- 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 +22 -5
- package/dist/web/assets/client/i18n/en.js +28 -4
- package/dist/web/assets/client/i18n/zh-CN.js +28 -4
- package/dist/web/assets/client/index.js +8 -0
- package/dist/web/assets/client/layout/manageShell.js +30 -8
- package/dist/web/assets/client/layout/shortcuts.js +7 -3
- 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 +120 -21
- package/dist/web/assets/client/lib/api.js +5 -0
- 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/session.js +21 -0
- package/dist/web/assets/client/lib/theme.js +11 -4
- package/dist/web/assets/client/ui/primitives.js +27 -0
- package/dist/web/assets/client/views/dock/terminal.js +4 -1
- package/dist/web/assets/client/views/overview.js +8 -5
- package/dist/web/assets/client/views/task/page.js +10 -10
- 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 +5 -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 -6
- package/dist/web/assets/shell/index.js +6 -2
- package/dist/web/assets/shell/login.js +57 -0
- 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 +4 -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 +1 -1
- package/dist/web/assets/styles/views/dock.js +22 -2
- package/dist/web/assets/styles/views/login.js +45 -0
- 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 +9 -15
- package/dist/web/assets/styles/views/terminal.js +1 -1
- package/dist/web/webAccess.js +129 -9
- package/dist/web/webServer.js +170 -14
- package/docs/release-workflow.md +67 -0
- package/docs/release-workflow.zh-CN.md +49 -0
- package/docs/storage-baseline.md +7 -1
- package/docs/storage-baseline.zh-CN.md +6 -1
- package/docs/testing/verification-levels.md +77 -11
- package/docs/testing/verification-levels.zh-CN.md +63 -9
- package/package.json +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// The motion system: every duration, easing, keyframe and stacking layer in
|
|
2
|
+
// the web UI comes from here. The stylesheets read the generated CSS custom
|
|
3
|
+
// properties (tokens.ts) and keyframes (styles/motion.ts); client scripts read
|
|
4
|
+
// the same values as JSON (client/lib/motion.ts). Changing a number below
|
|
5
|
+
// retimes the whole interface in one place.
|
|
6
|
+
//
|
|
7
|
+
// The curves follow well-known open-source component libraries: `sheet` is
|
|
8
|
+
// the iOS / Vaul drawer curve, `standard` a Material/Radix-style ease-out for
|
|
9
|
+
// small UI, and `exit` an accelerate curve so things leave quicker than they
|
|
10
|
+
// arrive.
|
|
11
|
+
export const DURATIONS = Object.freeze({
|
|
12
|
+
// Hover, press and colour feedback.
|
|
13
|
+
fast: 120,
|
|
14
|
+
// Popovers, menus, dialogs, content swaps.
|
|
15
|
+
base: 200,
|
|
16
|
+
// Exits are a little quicker than entries.
|
|
17
|
+
exit: 150,
|
|
18
|
+
// Column resizes (sidebar collapse, dock open) via View Transitions.
|
|
19
|
+
layout: 260,
|
|
20
|
+
// Drawers and sheets.
|
|
21
|
+
sheet: 420,
|
|
22
|
+
sheetOut: 300,
|
|
23
|
+
// Cross-document page transitions.
|
|
24
|
+
page: 220
|
|
25
|
+
});
|
|
26
|
+
export const EASINGS = Object.freeze({
|
|
27
|
+
standard: "cubic-bezier(.2,.8,.2,1)",
|
|
28
|
+
sheet: "cubic-bezier(.32,.72,0,1)",
|
|
29
|
+
exit: "cubic-bezier(.4,0,1,1)"
|
|
30
|
+
});
|
|
31
|
+
// Stacking layers, lowest to highest.
|
|
32
|
+
export const LAYERS = Object.freeze({
|
|
33
|
+
raised: 1,
|
|
34
|
+
sticky: 10,
|
|
35
|
+
header: 20,
|
|
36
|
+
popover: 30,
|
|
37
|
+
sheet: 60,
|
|
38
|
+
scrim: 70,
|
|
39
|
+
drawer: 71,
|
|
40
|
+
railOver: 72,
|
|
41
|
+
toast: 90,
|
|
42
|
+
skip: 100
|
|
43
|
+
});
|
|
44
|
+
// Keyframe presets. Each is emitted as a CSS @keyframes rule named
|
|
45
|
+
// `m-<name>` and handed to the Web Animations API by the client.
|
|
46
|
+
export const PRESETS = Object.freeze({
|
|
47
|
+
fade: { frames: [{ opacity: 0 }, { opacity: 1 }], duration: "base", easing: "standard" },
|
|
48
|
+
rise: { frames: [{ opacity: 0, transform: "translate3d(0,6px,0)" }, { opacity: 1, transform: "none" }], duration: "base", easing: "standard" },
|
|
49
|
+
pop: { frames: [{ opacity: 0, transform: "scale(.96)" }, { opacity: 1, transform: "none" }], duration: "base", easing: "standard" },
|
|
50
|
+
sheetUp: { frames: [{ transform: "translate3d(0,100%,0)" }, { transform: "translate3d(0,0,0)" }], duration: "sheet", easing: "sheet" },
|
|
51
|
+
sheetDown: { frames: [{ transform: "translate3d(0,0,0)" }, { transform: "translate3d(0,100%,0)" }], duration: "sheetOut", easing: "sheet" }
|
|
52
|
+
});
|
|
53
|
+
const kebab = (name) => name.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());
|
|
54
|
+
// CSS custom properties: --t-<duration>, --ease-<easing>, --z-<layer>.
|
|
55
|
+
// `--t-med` and `--ease` stay as aliases for the existing stylesheets.
|
|
56
|
+
export function motionTokenStyles() {
|
|
57
|
+
const t = Object.entries(DURATIONS).map(([k, v]) => `--t-${kebab(k)}:${v}ms`);
|
|
58
|
+
const e = Object.entries(EASINGS).map(([k, v]) => `--ease-${kebab(k)}:${v}`);
|
|
59
|
+
const z = Object.entries(LAYERS).map(([k, v]) => `--z-${kebab(k)}:${v}`);
|
|
60
|
+
return [...t, "--t-med:var(--t-base)", ...e, "--ease:var(--ease-standard)", ...z].join(";") + ";";
|
|
61
|
+
}
|
|
62
|
+
function frameCss(frame) {
|
|
63
|
+
return Object.entries(frame).map(([k, v]) => `${kebab(k)}:${v}`).join(";");
|
|
64
|
+
}
|
|
65
|
+
export function motionKeyframes() {
|
|
66
|
+
const presets = Object.entries(PRESETS).map(([name, p]) => {
|
|
67
|
+
const last = p.frames.length - 1;
|
|
68
|
+
const stops = p.frames.map((f, i) => `${last ? Math.round((i / last) * 100) : 0}%{${frameCss(f)}}`).join("");
|
|
69
|
+
return `@keyframes m-${kebab(name)}{${stops}}`;
|
|
70
|
+
});
|
|
71
|
+
return [...presets, "@keyframes m-spin{to{transform:rotate(360deg)}}", "@keyframes m-blink{50%{opacity:.35}}"].join("\n");
|
|
72
|
+
}
|
|
73
|
+
// The JSON the client motion helper reads.
|
|
74
|
+
export const MOTION = Object.freeze({ durations: DURATIONS, easings: EASINGS, presets: PRESETS });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { brandGlyphSvg } from "../shared/brand.js";
|
|
2
|
+
import { iconSvg } from "../shared/icons.js";
|
|
3
|
+
const item = (area, href, icon, key, label, current, id) => `<a${id ? ` id="${id}"` : ""} class="rail-item" href="${href}" data-area="${area}"${area === current ? ` aria-current="page"` : ""}${area === "workbench" && current === "workbench" ? "" : ` data-nav-area="${area}"`} title="${label}" data-i18n-title="${key}">${iconSvg(icon)}<span data-i18n="${key}">${label}</span></a>`;
|
|
4
|
+
export function appRail(current) {
|
|
5
|
+
const brandNav = current === "workbench" ? "" : ` data-nav-area="workbench"`;
|
|
6
|
+
return `<nav class="app-rail" aria-label="Main navigation" data-i18n-aria-label="manage.nav">
|
|
7
|
+
<a class="rail-brand" href="/"${brandNav} title="Yui"><span class="brand-mark" aria-hidden="true">${brandGlyphSvg()}</span><span class="sr-only">Yui</span></a>
|
|
8
|
+
<div class="rail-items">
|
|
9
|
+
${item("workbench", "/", "workbench", "manage.workbench", "Workbench", current, current === "workbench" ? "rail-workbench" : undefined)}
|
|
10
|
+
${item("models", "/models", "layers", "models.title", "Models", current)}
|
|
11
|
+
${item("settings", "/settings", "settings", "settings.title", "Settings", current, current === "workbench" ? "settings-open" : undefined)}
|
|
12
|
+
</div>
|
|
13
|
+
</nav>`;
|
|
14
|
+
}
|
|
@@ -27,6 +27,7 @@ export const DIALOGS_HTML = ` <dialog id="settings-dialog" class="dialog settin
|
|
|
27
27
|
<dt><kbd>R</kbd></dt><dd data-i18n="shortcut.refresh">Refresh</dd>
|
|
28
28
|
<dt><kbd>D</kbd></dt><dd data-i18n="shortcut.dock">Show or hide the session panel</dd>
|
|
29
29
|
<dt><kbd>O</kbd></dt><dd data-i18n="shortcut.operator">Operator session</dd>
|
|
30
|
+
<dt><kbd>[</kbd></dt><dd data-i18n="shortcut.sidebar">Show or hide the task list</dd>
|
|
30
31
|
<dt><kbd>1</kbd>–<kbd>${TASK_TABS.length}</kbd></dt><dd data-i18n="shortcut.tabs">Switch task sections</dd>
|
|
31
32
|
<dt><kbd>Esc</kbd></dt><dd data-i18n="shortcut.escape">Close panel / leave task</dd>
|
|
32
33
|
</dl>
|
|
@@ -7,17 +7,30 @@ import { iconSvg } from "../shared/icons.js";
|
|
|
7
7
|
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
8
|
<aside id="dock" class="dock" aria-label="Session" data-i18n-aria-label="dock.label" hidden>
|
|
9
9
|
<header class="dock-head">
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
<details id="dock-target" class="dock-target">
|
|
11
|
+
<summary class="dock-target-btn" aria-label="Conversation target" data-i18n-aria-label="dock.target">
|
|
12
|
+
<span id="dock-target-icon" class="dock-target-icon" aria-hidden="true">${iconSvg("broadcast")}</span>
|
|
13
|
+
<span id="dock-target-label" class="dock-target-label">Operator</span>${iconSvg("down")}
|
|
14
|
+
</summary>
|
|
15
|
+
<div class="dock-target-menu">
|
|
16
|
+
<div id="dock-target-options" class="dock-target-options" role="group" aria-label="Conversation target" data-i18n-aria-label="dock.target"></div>
|
|
17
|
+
<hr>
|
|
18
|
+
<button id="global-input-open" class="dock-target-item" type="button">${iconSvg("send")}<span data-i18n="global.openRole">Message another Role…</span></button>
|
|
19
|
+
</div>
|
|
20
|
+
</details>
|
|
16
21
|
<div class="dock-actions">
|
|
17
22
|
<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>
|
|
18
23
|
<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>
|
|
19
24
|
</div>
|
|
20
25
|
</header>
|
|
26
|
+
<div class="dock-tabs">
|
|
27
|
+
<div class="seg seg-sm" role="tablist" aria-label="Session view" data-i18n-aria-label="dock.label">
|
|
28
|
+
<button id="dock-tab-conversation" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-conversation"><span data-i18n="conversation.title">Conversation</span></button>
|
|
29
|
+
<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>
|
|
30
|
+
<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>
|
|
31
|
+
<button id="dock-tab-preview" class="seg-btn" type="button" role="tab" aria-selected="false" aria-controls="dock-preview" disabled><span data-i18n="preview.title">Preview</span></button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
21
34
|
<section id="dock-conversation" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-conversation" hidden></section>
|
|
22
35
|
<section id="dock-discussion" class="dock-pane dock-discussion" role="tabpanel" aria-labelledby="dock-tab-discussion"></section>
|
|
23
36
|
<section id="dock-preview" class="dock-pane dock-preview" role="tabpanel" aria-labelledby="dock-tab-preview" hidden></section>
|
|
@@ -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>`;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DEFAULT_DARK_THEME } from "../shared/themes.js";
|
|
2
|
+
import { BRAND_ICONS, brandGlyphSvg } from "../shared/brand.js";
|
|
3
|
+
import { iconSvg } from "../shared/icons.js";
|
|
4
|
+
// The only document served without a session. It carries no page token and
|
|
5
|
+
// no Task data; the client module posts the password as same-origin JSON and
|
|
6
|
+
// the server answers with a session cookie. CSP form-action 'none' means the
|
|
7
|
+
// form can never submit natively (so a password cannot end up in a URL).
|
|
8
|
+
const SHEETS = ["fonts", "tokens", "base", "motion", "ui/controls", "ui/forms", "views/login"];
|
|
9
|
+
export const LOGIN_HTML = `<!doctype html>
|
|
10
|
+
<html lang="en" data-theme="${DEFAULT_DARK_THEME.id}">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="utf-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
14
|
+
<meta name="color-scheme" content="dark light">
|
|
15
|
+
<link rel="icon" type="image/svg+xml" href="${BRAND_ICONS[DEFAULT_DARK_THEME.id]}">
|
|
16
|
+
<script src="/assets/theme.js"></script>
|
|
17
|
+
<title>Sign in · Yui</title>
|
|
18
|
+
<link rel="preload" href="/assets/fonts/inter-500.woff2" as="font" type="font/woff2" crossorigin>
|
|
19
|
+
<link rel="preload" href="/assets/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
|
|
20
|
+
${SHEETS.map(name => ` <link rel="stylesheet" href="/assets/css/${name}.css">`).join("\n")}
|
|
21
|
+
</head>
|
|
22
|
+
<body class="login-body">
|
|
23
|
+
<main class="login-shell">
|
|
24
|
+
<section class="login-card" aria-labelledby="login-title">
|
|
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>
|
|
31
|
+
</div>
|
|
32
|
+
<header class="login-heading">
|
|
33
|
+
<h1 id="login-title" data-i18n="login.heading">Sign in to Yui</h1>
|
|
34
|
+
<p id="login-subtitle" class="login-sub" data-i18n="login.subtitle">Enter the Web access password from this listener's private credential file.</p>
|
|
35
|
+
</header>
|
|
36
|
+
<p id="login-notice" class="login-notice" role="status" hidden></p>
|
|
37
|
+
<form id="login-form" class="login-form" method="post" novalidate aria-describedby="login-subtitle">
|
|
38
|
+
<input class="sr-only" type="text" name="username" value="yui" autocomplete="username" tabindex="-1" aria-hidden="true" readonly>
|
|
39
|
+
<div class="login-field">
|
|
40
|
+
<label class="login-label" for="login-password" data-i18n="login.password">Password</label>
|
|
41
|
+
<div class="login-password">
|
|
42
|
+
<input id="login-password" name="password" type="password" autocomplete="current-password" required maxlength="256" spellcheck="false" autocapitalize="off" autocorrect="off" aria-describedby="login-error" aria-invalid="false" autofocus>
|
|
43
|
+
<button id="login-reveal" class="icon-btn login-reveal" type="button" aria-controls="login-password" aria-pressed="false" aria-label="Show password" data-i18n-aria-label="login.show" title="Show password" data-i18n-title="login.show">${iconSvg("eye", "icon icon-show")}${iconSvg("eyeOff", "icon icon-hide")}</button>
|
|
44
|
+
</div>
|
|
45
|
+
<p id="login-error" class="login-error" role="alert" hidden></p>
|
|
46
|
+
</div>
|
|
47
|
+
<button id="login-submit" class="btn btn-primary login-submit" type="submit" disabled>
|
|
48
|
+
<span class="login-spinner" aria-hidden="true"></span><span id="login-submit-label" data-i18n="login.submit">Sign in</span>
|
|
49
|
+
</button>
|
|
50
|
+
</form>
|
|
51
|
+
<p id="login-transport" class="login-transport" hidden>${iconSvg("alert")}<span data-i18n="login.httpWarning">This connection uses plain HTTP and is not encrypted. Sign in only on a trusted network.</span></p>
|
|
52
|
+
<noscript><p class="login-error" data-i18n="login.noscript">JavaScript is required to sign in.</p></noscript>
|
|
53
|
+
</section>
|
|
54
|
+
</main>
|
|
55
|
+
<script type="module" src="/assets/js/app/login.js"></script>
|
|
56
|
+
</body>
|
|
57
|
+
</html>`;
|
|
@@ -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";
|
|
@@ -19,6 +20,7 @@ import { MANAGE_STYLES } from "./views/manage.js";
|
|
|
19
20
|
import { SETTINGS_STYLES } from "./views/settings.js";
|
|
20
21
|
import { MODELS_STYLES } from "./views/models.js";
|
|
21
22
|
import { JOBS_STYLES } from "./views/jobs.js";
|
|
23
|
+
import { LOGIN_STYLES } from "./views/login.js";
|
|
22
24
|
// The stylesheet registry, in cascade order: foundations (fonts, tokens,
|
|
23
25
|
// element defaults), the workspace frame, shared UI primitives, then one
|
|
24
26
|
// sheet per view, rendered prose, and breakpoints last so they override
|
|
@@ -28,6 +30,7 @@ export const STYLESHEETS = Object.freeze([
|
|
|
28
30
|
{ name: "fonts", body: FONT_FACE_STYLES },
|
|
29
31
|
{ name: "tokens", body: TOKEN_STYLES },
|
|
30
32
|
{ name: "base", body: BASE_STYLES },
|
|
33
|
+
{ name: "motion", body: MOTION_STYLES },
|
|
31
34
|
{ name: "layout/workspace", body: WORKSPACE_STYLES },
|
|
32
35
|
{ name: "ui/controls", body: CONTROL_STYLES },
|
|
33
36
|
{ name: "ui/surfaces", body: SURFACE_STYLES },
|
|
@@ -44,6 +47,7 @@ export const STYLESHEETS = Object.freeze([
|
|
|
44
47
|
{ name: "views/settings", body: SETTINGS_STYLES },
|
|
45
48
|
{ name: "views/models", body: MODELS_STYLES },
|
|
46
49
|
{ name: "views/jobs", body: JOBS_STYLES },
|
|
50
|
+
{ name: "views/login", body: LOGIN_STYLES },
|
|
47
51
|
{ name: "markdown", body: MARKDOWN_STYLES },
|
|
48
52
|
{ name: "layout/responsive", body: RESPONSIVE_STYLES }
|
|
49
53
|
]);
|
|
@@ -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
|
}
|