@chatcode/cco-better-sidebar 0.18.0-alpha.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/LICENSE +21 -0
- package/README.md +554 -0
- package/README_EN.md +563 -0
- package/assets/logo.svg +1 -0
- package/cordis.patch.yml +79 -0
- package/lib/client-editor.js +55744 -0
- package/lib/client-mermaid.js +208256 -0
- package/lib/client-registry.js +30378 -0
- package/lib/client-terminal.js +16894 -0
- package/lib/client.js +30378 -0
- package/lib/index.js +6897 -0
- package/lib/invariant.js +22 -0
- package/lib/types/agent-opens.d.ts +65 -0
- package/lib/types/agent-pty.d.ts +226 -0
- package/lib/types/browser-probe.d.ts +15 -0
- package/lib/types/bundle-route.d.ts +12 -0
- package/lib/types/chatcode-update.d.ts +47 -0
- package/lib/types/client/AccountMenu.d.ts +1 -0
- package/lib/types/client/AiRatioSection.d.ts +3 -0
- package/lib/types/client/BrowserView.d.ts +39 -0
- package/lib/types/client/ChatcodeGate.d.ts +6 -0
- package/lib/types/client/CodingPlanModelsSettings.d.ts +9 -0
- package/lib/types/client/DiffTab.d.ts +6 -0
- package/lib/types/client/DiffView.d.ts +51 -0
- package/lib/types/client/EditorHost.d.ts +13 -0
- package/lib/types/client/FenceErrorNotice.d.ts +5 -0
- package/lib/types/client/FileTree.d.ts +41 -0
- package/lib/types/client/FreeWindow.d.ts +29 -0
- package/lib/types/client/GitView.d.ts +12 -0
- package/lib/types/client/MarkdownHtml.d.ts +32 -0
- package/lib/types/client/OrphanedTab.d.ts +11 -0
- package/lib/types/client/PdfView.d.ts +6 -0
- package/lib/types/client/ReleaseNotesDialog.d.ts +11 -0
- package/lib/types/client/RenderBoundary.d.ts +32 -0
- package/lib/types/client/SandboxStatusBar.d.ts +10 -0
- package/lib/types/client/SideCardSection.d.ts +74 -0
- package/lib/types/client/Sidebar.d.ts +8 -0
- package/lib/types/client/TabBar.d.ts +54 -0
- package/lib/types/client/TerminalView.d.ts +24 -0
- package/lib/types/client/TextEditor.d.ts +10 -0
- package/lib/types/client/TreePanel.d.ts +27 -0
- package/lib/types/client/UpdateDialog.d.ts +10 -0
- package/lib/types/client/UpdateSettingsPanel.d.ts +1 -0
- package/lib/types/client/UpdateSettingsSection.d.ts +10 -0
- package/lib/types/client/UploadOverlay.d.ts +20 -0
- package/lib/types/client/UsageHeatmapSection.d.ts +4 -0
- package/lib/types/client/add-plugin-modal.d.ts +15 -0
- package/lib/types/client/api.d.ts +345 -0
- package/lib/types/client/binary-download.d.ts +12 -0
- package/lib/types/client/breakpoints.d.ts +17 -0
- package/lib/types/client/browser.d.ts +60 -0
- package/lib/types/client/builtins/index.d.ts +18 -0
- package/lib/types/client/builtins/tabs.d.ts +11 -0
- package/lib/types/client/builtins/viewers.d.ts +3 -0
- package/lib/types/client/chat-presentation.d.ts +13 -0
- package/lib/types/client/chunk-loader.d.ts +124 -0
- package/lib/types/client/chunks/editor.d.ts +9 -0
- package/lib/types/client/chunks/mermaid.d.ts +10 -0
- package/lib/types/client/chunks/terminal.d.ts +9 -0
- package/lib/types/client/cm-themes.d.ts +24 -0
- package/lib/types/client/conversation-draft.d.ts +14 -0
- package/lib/types/client/desktop-env.d.ts +35 -0
- package/lib/types/client/editor-load.d.ts +66 -0
- package/lib/types/client/frame-batcher.d.ts +24 -0
- package/lib/types/client/icons.d.ts +66 -0
- package/lib/types/client/image-types.d.ts +3 -0
- package/lib/types/client/ime-guard.d.ts +36 -0
- package/lib/types/client/index.d.ts +19 -0
- package/lib/types/client/intercept.d.ts +44 -0
- package/lib/types/client/lang.d.ts +14 -0
- package/lib/types/client/layout-push.d.ts +15 -0
- package/lib/types/client/lazy-chunk.d.ts +25 -0
- package/lib/types/client/link-intercept.d.ts +42 -0
- package/lib/types/client/locales-ar.d.ts +18 -0
- package/lib/types/client/locales-de.d.ts +4 -0
- package/lib/types/client/locales-fr.d.ts +10 -0
- package/lib/types/client/locales-hi.d.ts +17 -0
- package/lib/types/client/locales-id.d.ts +15 -0
- package/lib/types/client/locales-it.d.ts +8 -0
- package/lib/types/client/locales-ja.d.ts +17 -0
- package/lib/types/client/locales-ko.d.ts +9 -0
- package/lib/types/client/locales-nl.d.ts +16 -0
- package/lib/types/client/locales-pl.d.ts +19 -0
- package/lib/types/client/locales-pt.d.ts +1 -0
- package/lib/types/client/locales-ru.d.ts +13 -0
- package/lib/types/client/locales-sv.d.ts +1 -0
- package/lib/types/client/locales-th.d.ts +17 -0
- package/lib/types/client/locales-tr.d.ts +17 -0
- package/lib/types/client/locales-vi.d.ts +17 -0
- package/lib/types/client/locales-zh-HK.d.ts +32 -0
- package/lib/types/client/locales-zh-MO.d.ts +32 -0
- package/lib/types/client/locales-zh-TW.d.ts +32 -0
- package/lib/types/client/locales.d.ts +571 -0
- package/lib/types/client/markdown-html.d.ts +96 -0
- package/lib/types/client/markdown-images.d.ts +45 -0
- package/lib/types/client/markdown-labels.d.ts +21 -0
- package/lib/types/client/md-toc.d.ts +4 -0
- package/lib/types/client/mermaid-blocks.d.ts +47 -0
- package/lib/types/client/mermaid-sanitize.d.ts +2 -0
- package/lib/types/client/mermaid.d.ts +12 -0
- package/lib/types/client/open-when-sized.d.ts +20 -0
- package/lib/types/client/open-with-settings.d.ts +4 -0
- package/lib/types/client/open-with.d.ts +87 -0
- package/lib/types/client/openpath-intercept.d.ts +52 -0
- package/lib/types/client/paths.d.ts +43 -0
- package/lib/types/client/pdf-types.d.ts +2 -0
- package/lib/types/client/pinned.d.ts +86 -0
- package/lib/types/client/plugin-settings.d.ts +8 -0
- package/lib/types/client/plugins-shared.d.ts +27 -0
- package/lib/types/client/plugins-tabs.d.ts +3 -0
- package/lib/types/client/plugins-viewers.d.ts +3 -0
- package/lib/types/client/prefs.d.ts +41 -0
- package/lib/types/client/produced-files.d.ts +29 -0
- package/lib/types/client/selection-payload.d.ts +27 -0
- package/lib/types/client/service.d.ts +445 -0
- package/lib/types/client/session-pins.d.ts +14 -0
- package/lib/types/client/settings-nav-icon.d.ts +27 -0
- package/lib/types/client/shell-presets.d.ts +48 -0
- package/lib/types/client/split-pane.d.ts +38 -0
- package/lib/types/client/state.d.ts +480 -0
- package/lib/types/client/tab-content-memo.d.ts +40 -0
- package/lib/types/client/terminal-font.d.ts +78 -0
- package/lib/types/client/terminal-links.d.ts +119 -0
- package/lib/types/client/theme.d.ts +45 -0
- package/lib/types/client/titlebar-strip.d.ts +23 -0
- package/lib/types/client/ui-tweaks/appicons.d.ts +16 -0
- package/lib/types/client/ui-tweaks/archive.d.ts +42 -0
- package/lib/types/client/ui-tweaks/branding.d.ts +45 -0
- package/lib/types/client/ui-tweaks/cachehit.d.ts +41 -0
- package/lib/types/client/ui-tweaks/gitbar.d.ts +103 -0
- package/lib/types/client/ui-tweaks/index.d.ts +279 -0
- package/lib/types/client/ui-tweaks/mcp.d.ts +28 -0
- package/lib/types/client/ui-tweaks/notifier.d.ts +125 -0
- package/lib/types/client/ui-tweaks/whale.d.ts +55 -0
- package/lib/types/client/upload.d.ts +69 -0
- package/lib/types/client/useChatcodeAccount.d.ts +10 -0
- package/lib/types/client/wco.d.ts +47 -0
- package/lib/types/codingplan.d.ts +37 -0
- package/lib/types/config.d.ts +95 -0
- package/lib/types/context-types.d.ts +540 -0
- package/lib/types/fs-operations.d.ts +30 -0
- package/lib/types/fs-search.d.ts +24 -0
- package/lib/types/fs-tree.d.ts +51 -0
- package/lib/types/git.d.ts +124 -0
- package/lib/types/html-route.d.ts +59 -0
- package/lib/types/index.d.ts +47 -0
- package/lib/types/invariant.d.ts +15 -0
- package/lib/types/login-route.d.ts +2 -0
- package/lib/types/open-external.d.ts +24 -0
- package/lib/types/path-security.d.ts +26 -0
- package/lib/types/prefs-shared.d.ts +257 -0
- package/lib/types/pty-deps.d.ts +79 -0
- package/lib/types/pty-manager.d.ts +168 -0
- package/lib/types/tools.d.ts +34 -0
- package/lib/types/trust-fence.d.ts +24 -0
- package/lib/types/ui-tweaks/archive.d.ts +71 -0
- package/lib/types/ui-tweaks/config.d.ts +186 -0
- package/lib/types/ui-tweaks/git-web.d.ts +43 -0
- package/lib/types/ui-tweaks/git.d.ts +311 -0
- package/lib/types/ui-tweaks/index.d.ts +17 -0
- package/lib/types/ui-tweaks/mcp.d.ts +103 -0
- package/lib/types/ui-tweaks/turn-outcome.d.ts +57 -0
- package/lib/types/ui-tweaks/web.d.ts +55 -0
- package/lib/types/wire.d.ts +38 -0
- package/package.json +213 -0
- package/scripts/install.ps1 +310 -0
- package/scripts/install.sh +287 -0
- package/src/agent-opens.ts +265 -0
- package/src/agent-pty.ts +527 -0
- package/src/browser-probe.ts +26 -0
- package/src/bundle-route.ts +129 -0
- package/src/chatcode-update.ts +193 -0
- package/src/client/AccountMenu.tsx +177 -0
- package/src/client/AiRatioSection.module.css +329 -0
- package/src/client/AiRatioSection.tsx +292 -0
- package/src/client/BrowserView.tsx +289 -0
- package/src/client/ChatcodeGate.tsx +35 -0
- package/src/client/CodingPlanModelsSettings.module.css +118 -0
- package/src/client/CodingPlanModelsSettings.tsx +101 -0
- package/src/client/DiffTab.tsx +114 -0
- package/src/client/DiffView.tsx +305 -0
- package/src/client/EditorHost.tsx +563 -0
- package/src/client/FenceErrorNotice.tsx +40 -0
- package/src/client/FileTree.tsx +701 -0
- package/src/client/FreeWindow.tsx +337 -0
- package/src/client/GitView.tsx +722 -0
- package/src/client/MarkdownHtml.tsx +296 -0
- package/src/client/OrphanedTab.tsx +27 -0
- package/src/client/PdfView.tsx +110 -0
- package/src/client/ReleaseNotesDialog.tsx +91 -0
- package/src/client/RenderBoundary.tsx +49 -0
- package/src/client/SandboxStatusBar.tsx +60 -0
- package/src/client/SideCardSection.module.css +932 -0
- package/src/client/SideCardSection.tsx +1169 -0
- package/src/client/Sidebar.tsx +1857 -0
- package/src/client/TabBar.tsx +365 -0
- package/src/client/TerminalView.tsx +424 -0
- package/src/client/TextEditor.tsx +468 -0
- package/src/client/TreePanel.tsx +279 -0
- package/src/client/UpdateDialog.tsx +186 -0
- package/src/client/UpdateSettingsPanel.tsx +29 -0
- package/src/client/UpdateSettingsSection.tsx +22 -0
- package/src/client/UploadOverlay.tsx +62 -0
- package/src/client/UsageHeatmapSection.module.css +440 -0
- package/src/client/UsageHeatmapSection.tsx +301 -0
- package/src/client/add-plugin-modal.tsx +199 -0
- package/src/client/api.ts +433 -0
- package/src/client/binary-download.tsx +23 -0
- package/src/client/breakpoints.ts +61 -0
- package/src/client/browser.ts +167 -0
- package/src/client/builtins/index.ts +37 -0
- package/src/client/builtins/tabs.tsx +275 -0
- package/src/client/builtins/viewers.tsx +125 -0
- package/src/client/chat-presentation.ts +95 -0
- package/src/client/chunk-loader.ts +350 -0
- package/src/client/chunks/editor.tsx +9 -0
- package/src/client/chunks/mermaid.tsx +10 -0
- package/src/client/chunks/terminal.tsx +9 -0
- package/src/client/cm-themes.ts +129 -0
- package/src/client/conversation-draft.ts +29 -0
- package/src/client/css-modules.d.ts +5 -0
- package/src/client/desktop-env.ts +72 -0
- package/src/client/editor-load.ts +92 -0
- package/src/client/frame-batcher.ts +56 -0
- package/src/client/icons.tsx +170 -0
- package/src/client/image-types.ts +8 -0
- package/src/client/ime-guard.ts +47 -0
- package/src/client/index.tsx +501 -0
- package/src/client/intercept.tsx +172 -0
- package/src/client/lang.ts +225 -0
- package/src/client/layout-push.ts +37 -0
- package/src/client/layout.css +156 -0
- package/src/client/lazy-chunk.tsx +89 -0
- package/src/client/link-intercept.ts +73 -0
- package/src/client/locales-ar.ts +387 -0
- package/src/client/locales-de.ts +372 -0
- package/src/client/locales-fr.ts +379 -0
- package/src/client/locales-hi.ts +386 -0
- package/src/client/locales-id.ts +384 -0
- package/src/client/locales-it.ts +377 -0
- package/src/client/locales-ja.ts +486 -0
- package/src/client/locales-ko.ts +378 -0
- package/src/client/locales-nl.ts +384 -0
- package/src/client/locales-pl.ts +388 -0
- package/src/client/locales-pt.ts +369 -0
- package/src/client/locales-ru.ts +384 -0
- package/src/client/locales-sv.ts +369 -0
- package/src/client/locales-th.ts +386 -0
- package/src/client/locales-tr.ts +386 -0
- package/src/client/locales-vi.ts +386 -0
- package/src/client/locales-zh-HK.ts +401 -0
- package/src/client/locales-zh-MO.ts +401 -0
- package/src/client/locales-zh-TW.ts +401 -0
- package/src/client/locales.ts +1229 -0
- package/src/client/markdown-html.ts +331 -0
- package/src/client/markdown-images.ts +137 -0
- package/src/client/markdown-labels.tsx +31 -0
- package/src/client/md-toc.tsx +127 -0
- package/src/client/mermaid-blocks.ts +110 -0
- package/src/client/mermaid-sanitize.ts +93 -0
- package/src/client/mermaid.tsx +406 -0
- package/src/client/open-when-sized.ts +43 -0
- package/src/client/open-with-settings.tsx +135 -0
- package/src/client/open-with.ts +221 -0
- package/src/client/openpath-intercept.ts +79 -0
- package/src/client/paths.ts +63 -0
- package/src/client/pdf-types.ts +4 -0
- package/src/client/pinned.ts +184 -0
- package/src/client/plugin-settings.ts +43 -0
- package/src/client/plugins-shared.ts +29 -0
- package/src/client/plugins-tabs.ts +153 -0
- package/src/client/plugins-viewers.ts +44 -0
- package/src/client/prefs.ts +216 -0
- package/src/client/produced-files.ts +120 -0
- package/src/client/selection-payload.ts +86 -0
- package/src/client/service.ts +873 -0
- package/src/client/session-pins.tsx +334 -0
- package/src/client/settings-nav-icon.ts +55 -0
- package/src/client/shell-presets.ts +83 -0
- package/src/client/sidebar.module.css +3607 -0
- package/src/client/split-pane.tsx +333 -0
- package/src/client/state.ts +1646 -0
- package/src/client/tab-content-memo.ts +55 -0
- package/src/client/terminal-font.ts +224 -0
- package/src/client/terminal-links.ts +194 -0
- package/src/client/theme.ts +110 -0
- package/src/client/titlebar-strip.ts +37 -0
- package/src/client/ui-tweaks/appicons.ts +24 -0
- package/src/client/ui-tweaks/archive.tsx +281 -0
- package/src/client/ui-tweaks/branding.tsx +232 -0
- package/src/client/ui-tweaks/cachehit.tsx +122 -0
- package/src/client/ui-tweaks/gitbar.tsx +2559 -0
- package/src/client/ui-tweaks/index.tsx +1696 -0
- package/src/client/ui-tweaks/mcp.tsx +582 -0
- package/src/client/ui-tweaks/notifier.ts +514 -0
- package/src/client/ui-tweaks/whale.tsx +171 -0
- package/src/client/upload.ts +187 -0
- package/src/client/useChatcodeAccount.ts +103 -0
- package/src/client/wco.ts +120 -0
- package/src/codingplan.ts +146 -0
- package/src/config.ts +207 -0
- package/src/context-types.ts +559 -0
- package/src/fs-operations.ts +97 -0
- package/src/fs-search.ts +113 -0
- package/src/fs-tree.ts +158 -0
- package/src/git.ts +446 -0
- package/src/html-route.ts +106 -0
- package/src/index.ts +1524 -0
- package/src/invariant.ts +32 -0
- package/src/login-route.ts +26 -0
- package/src/open-external.ts +90 -0
- package/src/path-security.ts +81 -0
- package/src/prefs-shared.ts +303 -0
- package/src/pty-deps.ts +241 -0
- package/src/pty-manager.ts +376 -0
- package/src/tools.ts +482 -0
- package/src/trust-fence.ts +84 -0
- package/src/ui-tweaks/archive.ts +293 -0
- package/src/ui-tweaks/config.ts +257 -0
- package/src/ui-tweaks/git-web.ts +421 -0
- package/src/ui-tweaks/git.ts +1524 -0
- package/src/ui-tweaks/index.ts +61 -0
- package/src/ui-tweaks/mcp.ts +535 -0
- package/src/ui-tweaks/turn-outcome.ts +105 -0
- package/src/ui-tweaks/web.ts +225 -0
- package/src/wire.ts +110 -0
- package/vendor/addon-fit.js +2 -0
- package/vendor/xterm.css +218 -0
- package/vendor/xterm.js +5 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatCode self-update for the account menu's "check for updates / update now"
|
|
3
|
+
* action. Mirrors the semantics of the upstream `yuanjing-wanma-cli /update`
|
|
4
|
+
* flow (query the npm registry for the latest version, compare against the
|
|
5
|
+
* globally installed version, then `npm install -f -g <pkg>@<latest>`), applied
|
|
6
|
+
* to the CLI package this sidebar is responsible for.
|
|
7
|
+
*
|
|
8
|
+
* All subprocess work goes through `execFile` with a plain argv array (no
|
|
9
|
+
* shell), the same posture the rest of the host uses. The npm registry (and
|
|
10
|
+
* the global install target) is the machine running the DSH host, not the
|
|
11
|
+
* browser.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile } from 'node:child_process'
|
|
14
|
+
import { readFile } from 'node:fs/promises'
|
|
15
|
+
import { homedir } from 'node:os'
|
|
16
|
+
import { join } from 'node:path'
|
|
17
|
+
import { SidebarError } from './wire.ts'
|
|
18
|
+
|
|
19
|
+
/** CLI package the sidebar keeps up to date. */
|
|
20
|
+
export const CHATCODE_CLI_PKG = '@chatcode/chatcode-cli'
|
|
21
|
+
/** The single package a "one-click update" upgrades. */
|
|
22
|
+
export const CHATCODE_UPDATE_PACKAGES = [CHATCODE_CLI_PKG] as const
|
|
23
|
+
|
|
24
|
+
/** npm registry used for both the version lookup and the global install. */
|
|
25
|
+
export const NPM_REGISTRY = 'https://registry.npmjs.org'
|
|
26
|
+
|
|
27
|
+
/** One package's version-check result. */
|
|
28
|
+
export interface ChatCodeUpdateCheck {
|
|
29
|
+
packageName: string
|
|
30
|
+
/** Globally installed version (null when not globally present). */
|
|
31
|
+
current: string | null
|
|
32
|
+
/** Latest version on npm (null when the lookup failed). */
|
|
33
|
+
latest: string | null
|
|
34
|
+
needsUpdate: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** One package's perform-update result. */
|
|
38
|
+
export interface ChatCodeUpdateResult {
|
|
39
|
+
packageName: string
|
|
40
|
+
from: string | null
|
|
41
|
+
to: string | null
|
|
42
|
+
ok: boolean
|
|
43
|
+
/** Human message; on failure it carries the pasteable manual command. */
|
|
44
|
+
message: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface RunResult {
|
|
48
|
+
code: number
|
|
49
|
+
stdout: string
|
|
50
|
+
stderr: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Windows ships npm as a `npm.cmd` shim, which `execFile` cannot spawn
|
|
54
|
+
* directly (EINVAL) — it must run through the system shell. We keep args as an
|
|
55
|
+
* array so Node quotes each one (no string interpolation / injection), and
|
|
56
|
+
* stay shell-free on POSIX, matching the repo's git runner. */
|
|
57
|
+
function npmSpawnArgs(args: readonly string[]): { file: string; args: readonly string[] } {
|
|
58
|
+
if (process.platform === 'win32') {
|
|
59
|
+
// `cmd.exe /d /s /c` — the `/s` + array form lets Node do the argument
|
|
60
|
+
// escaping so `npm …` is invoked as a single anchored command.
|
|
61
|
+
return { file: process.env.comspec ?? 'cmd.exe', args: ['/d', '/s', '/c', 'npm', ...args] }
|
|
62
|
+
}
|
|
63
|
+
return { file: 'npm', args }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** npm command runner (bounded timeout). Errors are returned, not thrown, so
|
|
67
|
+
* callers can distinguish "npm exited non-zero" from npm missing. */
|
|
68
|
+
function runNpm(args: readonly string[], cwd: string, timeoutMs: number): Promise<RunResult> {
|
|
69
|
+
return new Promise((resolvePromise) => {
|
|
70
|
+
const spawnSpec = npmSpawnArgs(args)
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
child.kill('SIGTERM')
|
|
73
|
+
}, timeoutMs)
|
|
74
|
+
const child = execFile(
|
|
75
|
+
spawnSpec.file,
|
|
76
|
+
[...spawnSpec.args],
|
|
77
|
+
{ cwd, encoding: 'utf8', windowsHide: true, maxBuffer: 32 * 1024 * 1024 },
|
|
78
|
+
(error, stdout, stderr) => {
|
|
79
|
+
clearTimeout(timer)
|
|
80
|
+
if (error !== null) {
|
|
81
|
+
const raw = (error as NodeJS.ErrnoException).code
|
|
82
|
+
resolvePromise({ code: typeof raw === 'number' ? raw : 1, stdout, stderr })
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
resolvePromise({ code: 0, stdout, stderr })
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Read the globally installed version of one package. Resolves the npm global
|
|
93
|
+
* modules dir via `npm root -g` and reads `<dir>/<pkg>/package.json` directly —
|
|
94
|
+
* robust against `npm ls --json` packaging quirks. Returns the version string,
|
|
95
|
+
* or null when the package is not globally installed.
|
|
96
|
+
*/
|
|
97
|
+
export async function installedGlobalVersion(pkg: string): Promise<string | null> {
|
|
98
|
+
const { code, stdout } = await runNpm(['root', '-g'], homedir(), 30_000)
|
|
99
|
+
const root = stdout.trim()
|
|
100
|
+
if (code !== 0 || root === '') return null
|
|
101
|
+
const packageJsonPath = pkg.startsWith('@')
|
|
102
|
+
? join(root, pkg, 'package.json')
|
|
103
|
+
: join(root, join(...pkg.split('/')), 'package.json')
|
|
104
|
+
try {
|
|
105
|
+
const raw = await readFile(packageJsonPath, 'utf8')
|
|
106
|
+
const parsed: unknown = JSON.parse(raw)
|
|
107
|
+
const record = parsed !== null && typeof parsed === 'object' ? parsed as Record<string, unknown> : null
|
|
108
|
+
const version = record?.version
|
|
109
|
+
return typeof version === 'string' && version !== '' ? version : null
|
|
110
|
+
} catch {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Query npm for the latest published version of one package. Throws a
|
|
116
|
+
* SidebarError on any network / shape failure (mirrors chatcodeReleaseNote). */
|
|
117
|
+
export async function npmLatestVersion(pkg: string): Promise<string> {
|
|
118
|
+
// `encodeURI` (not `encodeURIComponent`) keeps scoped packages' leading `@`
|
|
119
|
+
// and `/` intact (`@scope/name`), which the npm registry requires verbatim.
|
|
120
|
+
const url = `https://registry.npmjs.org/${encodeURI(pkg)}/latest`
|
|
121
|
+
let response: Response
|
|
122
|
+
try {
|
|
123
|
+
response = await fetch(url, { signal: AbortSignal.timeout(10_000) })
|
|
124
|
+
} catch {
|
|
125
|
+
throw new SidebarError('chatcode-update-fetch', `the npm registry is unreachable for ${pkg}`, 502)
|
|
126
|
+
}
|
|
127
|
+
if (!response.ok) {
|
|
128
|
+
throw new SidebarError('chatcode-update-fetch', `the npm registry returned HTTP ${response.status} for ${pkg}`, 502)
|
|
129
|
+
}
|
|
130
|
+
const parsed: unknown = await response.json().catch(() => null)
|
|
131
|
+
const record = parsed !== null && typeof parsed === 'object' ? parsed as Record<string, unknown> : null
|
|
132
|
+
const version = record?.version
|
|
133
|
+
if (typeof version !== 'string' || version === '') {
|
|
134
|
+
throw new SidebarError('chatcode-update-fetch', `the npm registry returned an invalid response for ${pkg}`, 502)
|
|
135
|
+
}
|
|
136
|
+
return version
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** One package's version check (the pure comparison extracted for tests). */
|
|
140
|
+
export function chatcodeNeedsUpdate(current: string | null, latest: string | null): boolean {
|
|
141
|
+
// Unknown current version counts as needing an update; equality is up-to-date.
|
|
142
|
+
return current === null ? latest !== null : latest !== null && latest !== current
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Check one package against npm. A registry failure surfaces as a thrown
|
|
146
|
+
* SidebarError; a missing local install degrades to `current: null`. */
|
|
147
|
+
export async function checkOneUpdate(pkg: string): Promise<ChatCodeUpdateCheck> {
|
|
148
|
+
const [current, latest] = await Promise.all([installedGlobalVersion(pkg), npmLatestVersion(pkg)])
|
|
149
|
+
return { packageName: pkg, current, latest, needsUpdate: chatcodeNeedsUpdate(current, latest) }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The npm registry flag appended to every global install (mirrors upstream). */
|
|
153
|
+
function installArgs(pkg: string, version: string): string[] {
|
|
154
|
+
return ['install', '-f', '-g', `${pkg}@${version}`, '--registry', NPM_REGISTRY]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The pasteable manual fallback shown when a global install fails. */
|
|
158
|
+
export function manualInstallCommand(pkg: string, version: string): string {
|
|
159
|
+
return `npm i -f -g ${pkg}@${version} --registry=${NPM_REGISTRY}`
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Install one package at an exact latest version. Never throws on npm
|
|
163
|
+
* failure — the failure is encoded in the result with its manual command. */
|
|
164
|
+
export async function performOneUpdate(pkg: string, from: string | null, latest: string): Promise<ChatCodeUpdateResult> {
|
|
165
|
+
const { code, stderr, stdout } = await runNpm(installArgs(pkg, latest), homedir(), 120_000)
|
|
166
|
+
if (code !== 0) {
|
|
167
|
+
const detail = (stderr || stdout || 'unknown npm error').trim()
|
|
168
|
+
const message = `update failed: ${detail}\n${manualInstallCommand(pkg, latest)}`
|
|
169
|
+
return { packageName: pkg, from, to: latest, ok: false, message }
|
|
170
|
+
}
|
|
171
|
+
return { packageName: pkg, from, to: latest, ok: true, message: `${from ?? 'none'} -> ${latest}` }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Aggregate update-result shape returned by the `chatcode.update.perform`
|
|
175
|
+
* route (one entry per package, in update order). */
|
|
176
|
+
export async function performAllUpdates(checks: ChatCodeUpdateCheck[]): Promise<ChatCodeUpdateResult[]> {
|
|
177
|
+
const results: ChatCodeUpdateResult[] = []
|
|
178
|
+
for (const check of checks) {
|
|
179
|
+
const target = check.latest
|
|
180
|
+
if (target === null) {
|
|
181
|
+
results.push({
|
|
182
|
+
packageName: check.packageName,
|
|
183
|
+
from: check.current,
|
|
184
|
+
to: null,
|
|
185
|
+
ok: false,
|
|
186
|
+
message: 'no target version (the latest version lookup failed)',
|
|
187
|
+
})
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
results.push(await performOneUpdate(check.packageName, check.current, target))
|
|
191
|
+
}
|
|
192
|
+
return results
|
|
193
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bottom-left account control.
|
|
3
|
+
*
|
|
4
|
+
* DSH's settings launcher is currently a closed host-shell surface, so this
|
|
5
|
+
* component intentionally leaves its structure alone: it hides only the
|
|
6
|
+
* lower-left launcher while mounted and invokes that exact native button for
|
|
7
|
+
* the Settings menu item. This keeps the existing settings dialog and all
|
|
8
|
+
* host-owned navigation behavior intact.
|
|
9
|
+
*/
|
|
10
|
+
import { useEffect, useRef, useState } from 'react'
|
|
11
|
+
import { api } from './api.ts'
|
|
12
|
+
import { useChatcodeAccount } from './useChatcodeAccount.ts'
|
|
13
|
+
import { t } from './locales.ts'
|
|
14
|
+
import css from './sidebar.module.css'
|
|
15
|
+
|
|
16
|
+
const SETTINGS_LABELS = new Set([
|
|
17
|
+
'settings', '设置', '設定', 'paramètres', 'einstellungen', 'impostazioni',
|
|
18
|
+
'configurações', 'instellingen', 'inställningar', 'ayarlar', '설정', 'настройки',
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
function nativeSettingsButton(except: HTMLElement | null): HTMLButtonElement | undefined {
|
|
22
|
+
return [...document.querySelectorAll<HTMLButtonElement>('button')]
|
|
23
|
+
.filter(button => except === null || !except.contains(button))
|
|
24
|
+
.filter(button => {
|
|
25
|
+
const text = `${button.getAttribute('aria-label') ?? ''} ${button.title} ${button.textContent ?? ''}`.trim().toLowerCase()
|
|
26
|
+
if (![...SETTINGS_LABELS].some(label => text === label || text.includes(label))) return false
|
|
27
|
+
const rect = button.getBoundingClientRect()
|
|
28
|
+
// The host launcher lives in the lower-left rail. Avoid matching a
|
|
29
|
+
// Settings item inside an already-open dialog or an unrelated panel.
|
|
30
|
+
return rect.left < window.innerWidth * 0.4 && rect.bottom > window.innerHeight - 120
|
|
31
|
+
})
|
|
32
|
+
.sort((a, b) => b.getBoundingClientRect().bottom - a.getBoundingClientRect().bottom)[0]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Find the left navigation rail's usable width from the native launcher. */
|
|
36
|
+
function railWidthOf(button: HTMLButtonElement | undefined): number | undefined {
|
|
37
|
+
let element: HTMLElement | null = button?.parentElement ?? null
|
|
38
|
+
for (let depth = 0; element !== null && depth < 8; depth += 1, element = element.parentElement) {
|
|
39
|
+
const rect = element.getBoundingClientRect()
|
|
40
|
+
if (rect.left <= 8 && rect.width >= 160 && rect.width <= window.innerWidth * 0.5) return Math.round(rect.width)
|
|
41
|
+
}
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function LogoutIcon(): JSX.Element {
|
|
46
|
+
return (
|
|
47
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
48
|
+
<path d="M6.5 2.5h-2A1.5 1.5 0 0 0 3 4v8a1.5 1.5 0 0 0 1.5 1.5h2M8 5l3 3-3 3M5.5 8h5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
49
|
+
</svg>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Settings glyph kept local so every account action shares the same SVG treatment. */
|
|
54
|
+
function SettingsIcon(): JSX.Element {
|
|
55
|
+
return (
|
|
56
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
57
|
+
<path d="M6.9 2.2h2.2l.35 1.28c.33.14.64.32.92.54l1.26-.38 1.1 1.9-.93.94c.03.17.05.35.05.54s-.02.37-.05.54l.93.94-1.1 1.9-1.26-.38c-.28.22-.59.4-.92.54L9.1 13.8H6.9l-.35-1.28a4.1 4.1 0 0 1-.92-.54l-1.26.38-1.1-1.9.93-.94a3.2 3.2 0 0 1 0-1.08l-.93-.94 1.1-1.9 1.26.38c.28-.22.59-.4.92-.54L6.9 2.2Z" stroke="currentColor" strokeWidth="1.25" strokeLinejoin="round" />
|
|
58
|
+
<circle cx="8" cy="8" r="1.75" stroke="currentColor" strokeWidth="1.25" />
|
|
59
|
+
</svg>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Preserve the sidebar's existing browser-session exit when the optional ChatCode service is absent. */
|
|
64
|
+
function logout(): void {
|
|
65
|
+
for (const cookie of document.cookie.split(';')) {
|
|
66
|
+
const name = cookie.split('=')[0]?.trim()
|
|
67
|
+
if (name !== undefined && name !== '') document.cookie = `${name}=; Max-Age=0; Path=/; SameSite=Lax`
|
|
68
|
+
}
|
|
69
|
+
window.localStorage.clear()
|
|
70
|
+
window.sessionStorage.clear()
|
|
71
|
+
window.location.assign('/')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function AccountMenu(): JSX.Element {
|
|
75
|
+
const rootRef = useRef<HTMLDivElement>(null)
|
|
76
|
+
const [open, setOpen] = useState(false)
|
|
77
|
+
const [userName, setUserName] = useState('User')
|
|
78
|
+
const { chatcode, busy, error, loginUrl, startLogin, logout: logoutChatcode } = useChatcodeAccount()
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
let active = true
|
|
82
|
+
void api.accountGet()
|
|
83
|
+
.then(({ userName: next }) => { if (active && next.trim() !== '') setUserName(next) })
|
|
84
|
+
.catch(() => { /* The menu remains usable with its local fallback. */ })
|
|
85
|
+
return () => { active = false }
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const onPointerDown = (event: PointerEvent): void => {
|
|
90
|
+
if (!rootRef.current?.contains(event.target as Node)) setOpen(false)
|
|
91
|
+
}
|
|
92
|
+
document.addEventListener('pointerdown', onPointerDown)
|
|
93
|
+
return () => { document.removeEventListener('pointerdown', onPointerDown) }
|
|
94
|
+
}, [])
|
|
95
|
+
|
|
96
|
+
// The host has no slot for replacing this one closed-shell control. Keep
|
|
97
|
+
// the original fully recoverable: its inline style is restored on disposal
|
|
98
|
+
// and it remains the target invoked by our Settings menu item.
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
let current: HTMLButtonElement | undefined
|
|
101
|
+
let originalStyle: string | undefined
|
|
102
|
+
const sync = (): void => {
|
|
103
|
+
const next = nativeSettingsButton(rootRef.current)
|
|
104
|
+
const width = railWidthOf(next)
|
|
105
|
+
if (width !== undefined) rootRef.current?.style.setProperty('--dsh-account-menu-width', `${width}px`)
|
|
106
|
+
if (next === current) return
|
|
107
|
+
if (current !== undefined && originalStyle !== undefined) current.style.cssText = originalStyle
|
|
108
|
+
current = next
|
|
109
|
+
originalStyle = current?.style.cssText
|
|
110
|
+
if (current !== undefined) current.style.visibility = 'hidden'
|
|
111
|
+
}
|
|
112
|
+
sync()
|
|
113
|
+
const observer = new MutationObserver(sync)
|
|
114
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true })
|
|
115
|
+
return () => {
|
|
116
|
+
observer.disconnect()
|
|
117
|
+
if (current !== undefined && originalStyle !== undefined) current.style.cssText = originalStyle
|
|
118
|
+
}
|
|
119
|
+
}, [])
|
|
120
|
+
|
|
121
|
+
const openSettings = (): void => {
|
|
122
|
+
setOpen(false)
|
|
123
|
+
nativeSettingsButton(rootRef.current)?.click()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const chatcodeName = chatcode?.userName?.trim() || chatcode?.emailAddress?.trim() || userName
|
|
127
|
+
const signedIn = chatcode?.validation === 'valid' && chatcode.expired !== true
|
|
128
|
+
// A saved credential remains usable for the next check when the company
|
|
129
|
+
// network is unavailable. Do not turn that transient state into a login CTA.
|
|
130
|
+
const canStartChatcodeLogin = chatcode?.available === true && !signedIn
|
|
131
|
+
&& !(chatcode.configured === true && chatcode.validation === 'unavailable')
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div ref={rootRef} className={css.accountMenu}>
|
|
135
|
+
{open && (
|
|
136
|
+
<div className={css.accountDropdown} role="menu" aria-label={t('accountMenuLabel')}>
|
|
137
|
+
<div className={css.accountDropdownHeader}>
|
|
138
|
+
<span className={css.accountAvatar}>{chatcodeName.slice(0, 1).toUpperCase()}</span>
|
|
139
|
+
<span className={css.accountName}>{chatcodeName}</span>
|
|
140
|
+
<span className={css.accountPresence} style={{ opacity: signedIn ? 1 : 0.25 }} aria-hidden="true" />
|
|
141
|
+
</div>
|
|
142
|
+
<div className={css.accountMenuDivider} />
|
|
143
|
+
<button type="button" role="menuitem" className={css.accountItem} onClick={openSettings}>
|
|
144
|
+
<SettingsIcon />
|
|
145
|
+
{t('accountMenuSettings')}
|
|
146
|
+
</button>
|
|
147
|
+
{error && <div role="alert" className={css.accountItem}>{t(error === 'status' ? 'accountMenuChatcodeStatusError' : 'accountMenuChatcodeError')}</div>}
|
|
148
|
+
{(chatcode?.login?.state === 'failed' || chatcode?.login?.state === 'timed-out') && <div role="alert" className={css.accountItem}>{t('accountMenuChatcodeRetry')}</div>}
|
|
149
|
+
{loginUrl && <a data-dsh-external="true" className={css.accountItem} href={loginUrl} target="_blank" rel="noopener noreferrer" onClick={event => { event.stopPropagation() }}>{t('accountMenuChatcodeOpen')}</a>}
|
|
150
|
+
{chatcode?.available === true && <>
|
|
151
|
+
{canStartChatcodeLogin && <button type="button" role="menuitem" className={css.accountItem} disabled={busy} onClick={startLogin}>
|
|
152
|
+
<LogoutIcon />{busy ? t('accountMenuChatcodeStarting') : t('accountMenuChatcodeLogin')}
|
|
153
|
+
</button>}
|
|
154
|
+
{chatcode.configured && <button type="button" role="menuitem" className={css.accountItem} disabled={busy} onClick={logoutChatcode}>
|
|
155
|
+
<LogoutIcon />{t('accountMenuChatcodeLogout')}
|
|
156
|
+
</button>}
|
|
157
|
+
</>}
|
|
158
|
+
{chatcode?.available === false && <button type="button" role="menuitem" className={css.accountItem} onClick={logout}>
|
|
159
|
+
<LogoutIcon />{t('accountMenuLogout')}
|
|
160
|
+
</button>}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
className={css.accountTrigger}
|
|
166
|
+
aria-expanded={open}
|
|
167
|
+
aria-haspopup="menu"
|
|
168
|
+
aria-label={t('accountMenuLabel')}
|
|
169
|
+
onClick={() => { setOpen(value => !value) }}
|
|
170
|
+
>
|
|
171
|
+
<span className={css.accountAvatar}>{chatcodeName.slice(0, 1).toUpperCase()}</span>
|
|
172
|
+
<span className={css.accountName}>{chatcodeName}</span>
|
|
173
|
+
<span className={css.accountPresence} style={{ opacity: signedIn ? 1 : 0.25 }} aria-hidden="true" />
|
|
174
|
+
</button>
|
|
175
|
+
</div>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
.section {
|
|
2
|
+
container-type: inline-size;
|
|
3
|
+
width: 100%;
|
|
4
|
+
max-width: 920px;
|
|
5
|
+
min-width: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Query row: date range – 查询 – 导出. */
|
|
9
|
+
.queryRow {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
gap: 8px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dateInput {
|
|
17
|
+
flex: 1 1 140px;
|
|
18
|
+
min-width: 0;
|
|
19
|
+
max-width: 180px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.dash {
|
|
23
|
+
color: var(--dsw-alias-label-tertiary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* The 查询 button: the settings primary-button recipe (label-primary fill,
|
|
27
|
+
inverted surface text). */
|
|
28
|
+
.primaryButton {
|
|
29
|
+
appearance: none;
|
|
30
|
+
border: 1px solid transparent;
|
|
31
|
+
border-radius: 8px;
|
|
32
|
+
padding: 5px 14px;
|
|
33
|
+
font: inherit;
|
|
34
|
+
font-size: 13px;
|
|
35
|
+
line-height: 1.5;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
background: var(--dsw-alias-state-business-primary);
|
|
38
|
+
color: #fff;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.section.section .primaryButton:hover:not(:disabled),
|
|
42
|
+
.section.section .primaryButton:focus:not(:disabled),
|
|
43
|
+
.section.section .primaryButton:focus-visible:not(:disabled),
|
|
44
|
+
.section.section .primaryButton:active:not(:disabled) {
|
|
45
|
+
border-color: transparent !important;
|
|
46
|
+
background: var(--dsw-alias-state-business-primary) !important;
|
|
47
|
+
color: #fff !important;
|
|
48
|
+
outline: none !important;
|
|
49
|
+
box-shadow: none !important;
|
|
50
|
+
filter: none !important;
|
|
51
|
+
transform: none !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.primaryButton:disabled {
|
|
55
|
+
opacity: 0.45;
|
|
56
|
+
cursor: default;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* The 导出 / retry / pager buttons: the hairline-button recipe. */
|
|
60
|
+
.exportButton {
|
|
61
|
+
appearance: none;
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
padding: 5px 14px;
|
|
66
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
font: inherit;
|
|
69
|
+
font-size: 13px;
|
|
70
|
+
line-height: 1.5;
|
|
71
|
+
color: var(--dsw-alias-label-primary);
|
|
72
|
+
background: transparent;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.section.section .exportButton:hover:not(:disabled),
|
|
77
|
+
.section.section .exportButton:focus:not(:disabled),
|
|
78
|
+
.section.section .exportButton:focus-visible:not(:disabled),
|
|
79
|
+
.section.section .exportButton:active:not(:disabled) {
|
|
80
|
+
border-color: var(--dsw-alias-border-l2) !important;
|
|
81
|
+
background: transparent !important;
|
|
82
|
+
color: var(--dsw-alias-label-primary) !important;
|
|
83
|
+
outline: none !important;
|
|
84
|
+
box-shadow: none !important;
|
|
85
|
+
filter: none !important;
|
|
86
|
+
transform: none !important;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.exportButton:disabled {
|
|
90
|
+
opacity: 0.45;
|
|
91
|
+
cursor: default;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* The 4-card overview strip. */
|
|
95
|
+
.overview {
|
|
96
|
+
display: grid;
|
|
97
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
98
|
+
gap: 12px;
|
|
99
|
+
margin-top: 24px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.card {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
gap: 8px;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
padding: 14px 16px;
|
|
108
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
109
|
+
border-radius: 12px;
|
|
110
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.cardValue {
|
|
114
|
+
font-size: 22px;
|
|
115
|
+
line-height: 28px;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
font-variant-numeric: tabular-nums;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
text-overflow: ellipsis;
|
|
120
|
+
white-space: nowrap;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.addedValue {
|
|
124
|
+
color: var(--dsw-alias-state-business-primary);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.adoptedValue,
|
|
128
|
+
.ratioCardHigh .cardRatio {
|
|
129
|
+
color: var(--dsw-alias-state-success-primary);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.commitsValue {
|
|
133
|
+
color: var(--dsw-alias-state-warn-primary);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.ratioCardHigh {
|
|
137
|
+
border-color: color-mix(in srgb, var(--dsw-alias-state-success-primary) 35%, var(--dsw-alias-border-l2));
|
|
138
|
+
background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, var(--dsw-alias-bg-layer-3));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.ratioCardLow {
|
|
142
|
+
border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, var(--dsw-alias-border-l2));
|
|
143
|
+
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, var(--dsw-alias-bg-layer-3));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.ratioCardLow .cardRatio {
|
|
147
|
+
color: var(--dsw-alias-state-error-primary);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.cardLabel {
|
|
151
|
+
font-size: 12px;
|
|
152
|
+
line-height: 18px;
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
color: var(--dsw-alias-label-secondary);
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
text-overflow: ellipsis;
|
|
157
|
+
white-space: nowrap;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Commit table heading. */
|
|
161
|
+
.tableHeading {
|
|
162
|
+
margin-top: 30px;
|
|
163
|
+
margin-bottom: 12px;
|
|
164
|
+
font-size: 13px;
|
|
165
|
+
line-height: 20px;
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
color: var(--dsw-alias-label-primary);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.tableWrap {
|
|
171
|
+
max-width: 100%;
|
|
172
|
+
max-height: 44vh;
|
|
173
|
+
overflow: auto;
|
|
174
|
+
border: 1px solid var(--dsw-alias-border-l2);
|
|
175
|
+
border-radius: 12px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.table {
|
|
179
|
+
border-collapse: separate;
|
|
180
|
+
border-spacing: 0;
|
|
181
|
+
width: 100%;
|
|
182
|
+
min-width: 1180px;
|
|
183
|
+
font-size: 12px;
|
|
184
|
+
line-height: 18px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.table th {
|
|
188
|
+
position: sticky;
|
|
189
|
+
top: 0;
|
|
190
|
+
z-index: 1;
|
|
191
|
+
padding: 8px 10px;
|
|
192
|
+
text-align: left;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
white-space: nowrap;
|
|
195
|
+
color: var(--dsw-alias-label-secondary);
|
|
196
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
197
|
+
border-bottom: 1px solid var(--dsw-alias-border-l2);
|
|
198
|
+
border-right: 1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 75%, transparent);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.table td {
|
|
202
|
+
padding: 7px 10px;
|
|
203
|
+
max-width: 180px;
|
|
204
|
+
overflow: hidden;
|
|
205
|
+
text-overflow: ellipsis;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
color: var(--dsw-alias-label-primary);
|
|
208
|
+
border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 55%, transparent);
|
|
209
|
+
border-right: 1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 65%, transparent);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.table :is(th, td):last-child {
|
|
213
|
+
border-right: none;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.table tbody tr:last-child td {
|
|
217
|
+
border-bottom: none;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.table tbody tr:hover td {
|
|
221
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* Numeric columns: right-aligned tabular figures. */
|
|
225
|
+
.num {
|
|
226
|
+
text-align: right;
|
|
227
|
+
font-variant-numeric: tabular-nums;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* AI-adopted value ink when non-zero (the portal's green-if-positive). */
|
|
231
|
+
.aiValue {
|
|
232
|
+
color: var(--dsw-alias-state-success-primary);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* The per-row ratio: value + mini progress bar. Both carry the threshold
|
|
236
|
+
color (≥70 success / ≥50 business / else warn) inline from the component. */
|
|
237
|
+
.ratioValue {
|
|
238
|
+
display: inline-block;
|
|
239
|
+
min-width: 34px;
|
|
240
|
+
font-variant-numeric: tabular-nums;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.ratioBar {
|
|
244
|
+
display: inline-block;
|
|
245
|
+
vertical-align: middle;
|
|
246
|
+
width: 56px;
|
|
247
|
+
height: 5px;
|
|
248
|
+
margin-left: 6px;
|
|
249
|
+
border-radius: 999px;
|
|
250
|
+
background: color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.ratioBar > span {
|
|
255
|
+
display: block;
|
|
256
|
+
height: 100%;
|
|
257
|
+
border-radius: 999px;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.dateCell {
|
|
261
|
+
font-variant-numeric: tabular-nums;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Pager: ‹ info ›. */
|
|
265
|
+
.pager {
|
|
266
|
+
display: flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
justify-content: flex-end;
|
|
269
|
+
flex-wrap: wrap;
|
|
270
|
+
gap: 10px;
|
|
271
|
+
margin-top: 12px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.pager .exportButton {
|
|
275
|
+
padding: 3px 10px;
|
|
276
|
+
font-size: 14px;
|
|
277
|
+
line-height: 1.2;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.pageInfo {
|
|
281
|
+
font-size: 12px;
|
|
282
|
+
line-height: 18px;
|
|
283
|
+
color: var(--dsw-alias-label-secondary);
|
|
284
|
+
font-variant-numeric: tabular-nums;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Loading / sign-in / empty placeholder rows. */
|
|
288
|
+
.state {
|
|
289
|
+
min-height: 120px;
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
font-size: 13px;
|
|
293
|
+
line-height: 20px;
|
|
294
|
+
color: var(--dsw-alias-label-secondary);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* Error row with the inline retry. */
|
|
298
|
+
.stateError {
|
|
299
|
+
min-height: 120px;
|
|
300
|
+
display: flex;
|
|
301
|
+
align-items: center;
|
|
302
|
+
justify-content: space-between;
|
|
303
|
+
gap: 12px;
|
|
304
|
+
font-size: 13px;
|
|
305
|
+
line-height: 20px;
|
|
306
|
+
color: var(--dsw-alias-label-secondary);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.stateError > span {
|
|
310
|
+
min-width: 0;
|
|
311
|
+
overflow-wrap: anywhere;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
@container (max-width: 600px) {
|
|
315
|
+
.overview {
|
|
316
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@container (max-width: 320px) {
|
|
321
|
+
.overview {
|
|
322
|
+
grid-template-columns: minmax(0, 1fr);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.stateError {
|
|
326
|
+
align-items: flex-start;
|
|
327
|
+
flex-direction: column;
|
|
328
|
+
}
|
|
329
|
+
}
|