reviewflow 3.15.0 → 3.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/CHANGELOG.md +20 -0
- package/dist/dashboard/index.html +132 -0
- package/dist/dashboard/modules/i18n.d.ts.map +1 -1
- package/dist/dashboard/modules/i18n.js +30 -0
- package/dist/dashboard/modules/i18n.js.map +1 -1
- package/dist/dashboard/modules/worktreePanel.d.ts +111 -0
- package/dist/dashboard/modules/worktreePanel.d.ts.map +1 -0
- package/dist/dashboard/modules/worktreePanel.js +373 -0
- package/dist/dashboard/modules/worktreePanel.js.map +1 -0
- package/dist/dashboard/styles.css +312 -0
- package/dist/dashboard/vendor/anime.esm.min.js +7 -0
- package/dist/frameworks/claude/timers/claudeInvocationTimers.d.ts.map +1 -1
- package/dist/frameworks/claude/timers/claudeInvocationTimers.js +0 -1
- package/dist/frameworks/claude/timers/claudeInvocationTimers.js.map +1 -1
- package/dist/frameworks/scheduler/worktreeSweepScheduler.d.ts +20 -0
- package/dist/frameworks/scheduler/worktreeSweepScheduler.d.ts.map +1 -0
- package/dist/frameworks/scheduler/worktreeSweepScheduler.js +70 -0
- package/dist/frameworks/scheduler/worktreeSweepScheduler.js.map +1 -0
- package/dist/main/dependencies.d.ts +10 -0
- package/dist/main/dependencies.d.ts.map +1 -1
- package/dist/main/dependencies.js +15 -0
- package/dist/main/dependencies.js.map +1 -1
- package/dist/main/routes.d.ts.map +1 -1
- package/dist/main/routes.js +10 -7
- package/dist/main/routes.js.map +1 -1
- package/dist/main/server.d.ts.map +1 -1
- package/dist/main/server.js +16 -2
- package/dist/main/server.js.map +1 -1
- package/dist/modules/claude-invocation/interface-adapters/gateways/claudeSession.cli.gateway.d.ts.map +1 -1
- package/dist/modules/claude-invocation/interface-adapters/gateways/claudeSession.cli.gateway.js +17 -2
- package/dist/modules/claude-invocation/interface-adapters/gateways/claudeSession.cli.gateway.js.map +1 -1
- package/dist/modules/claude-invocation/usecases/auditBilling.usecase.d.ts +0 -2
- package/dist/modules/claude-invocation/usecases/auditBilling.usecase.d.ts.map +1 -1
- package/dist/modules/claude-invocation/usecases/auditBilling.usecase.js +6 -6
- package/dist/modules/claude-invocation/usecases/auditBilling.usecase.js.map +1 -1
- package/dist/modules/platform-integration/entities/github/githubPullRequestEvent.guard.d.ts +14 -0
- package/dist/modules/platform-integration/entities/github/githubPullRequestEvent.guard.d.ts.map +1 -1
- package/dist/modules/platform-integration/entities/github/githubPullRequestEvent.guard.js +13 -2
- package/dist/modules/platform-integration/entities/github/githubPullRequestEvent.guard.js.map +1 -1
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/github.controller.d.ts.map +1 -1
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/github.controller.js +14 -2
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/github.controller.js.map +1 -1
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/gitlab.controller.d.ts.map +1 -1
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/gitlab.controller.js +2 -3
- package/dist/modules/platform-integration/interface-adapters/controllers/webhook/gitlab.controller.js.map +1 -1
- package/dist/modules/review-execution/entities/progress/agentDefinition.type.d.ts.map +1 -1
- package/dist/modules/review-execution/entities/progress/agentDefinition.type.js +1 -0
- package/dist/modules/review-execution/entities/progress/agentDefinition.type.js.map +1 -1
- package/dist/modules/worktree-management/entities/sweep/lastSweepSummary.schema.d.ts +9 -0
- package/dist/modules/worktree-management/entities/sweep/lastSweepSummary.schema.d.ts.map +1 -0
- package/dist/modules/worktree-management/entities/sweep/lastSweepSummary.schema.js +8 -0
- package/dist/modules/worktree-management/entities/sweep/lastSweepSummary.schema.js.map +1 -0
- package/dist/modules/worktree-management/entities/sweep/runSweepResult.d.ts +12 -0
- package/dist/modules/worktree-management/entities/sweep/runSweepResult.d.ts.map +1 -0
- package/dist/modules/worktree-management/entities/sweep/runSweepResult.js +2 -0
- package/dist/modules/worktree-management/entities/sweep/runSweepResult.js.map +1 -0
- package/dist/modules/worktree-management/entities/worktree/worktreeSizeProbe.gateway.d.ts +4 -0
- package/dist/modules/worktree-management/entities/worktree/worktreeSizeProbe.gateway.d.ts.map +1 -0
- package/dist/modules/worktree-management/entities/worktree/worktreeSizeProbe.gateway.js +2 -0
- package/dist/modules/worktree-management/entities/worktree/worktreeSizeProbe.gateway.js.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.d.ts +19 -0
- package/dist/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.d.ts.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.js +39 -0
- package/dist/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.js.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.d.ts +17 -0
- package/dist/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.d.ts.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.js +46 -0
- package/dist/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.js.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.d.ts +53 -0
- package/dist/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.d.ts.map +1 -0
- package/dist/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.js +102 -0
- package/dist/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.js.map +1 -0
- package/dist/modules/worktree-management/usecases/sweepStaleWorktrees.usecase.d.ts +1 -1
- package/dist/modules/worktree-management/usecases/sweepStaleWorktrees.usecase.d.ts.map +1 -1
- package/dist/modules/worktree-management/usecases/sweepStaleWorktrees.usecase.js +1 -1
- package/dist/modules/worktree-management/usecases/sweepStaleWorktrees.usecase.js.map +1 -1
- package/dist/tests/acceptance/169-migrate-claude-invocation-to-bg-mode.acceptance.test.js +5 -5
- package/dist/tests/acceptance/169-migrate-claude-invocation-to-bg-mode.acceptance.test.js.map +1 -1
- package/dist/tests/acceptance/170-prebuilt-worktree-lifecycle.acceptance.test.d.ts +5 -4
- package/dist/tests/acceptance/170-prebuilt-worktree-lifecycle.acceptance.test.d.ts.map +1 -1
- package/dist/tests/acceptance/170-prebuilt-worktree-lifecycle.acceptance.test.js +246 -11
- package/dist/tests/acceptance/170-prebuilt-worktree-lifecycle.acceptance.test.js.map +1 -1
- package/dist/tests/acceptance/173-dashboard-worktree-panel.acceptance.test.d.ts +10 -0
- package/dist/tests/acceptance/173-dashboard-worktree-panel.acceptance.test.d.ts.map +1 -0
- package/dist/tests/acceptance/173-dashboard-worktree-panel.acceptance.test.js +192 -0
- package/dist/tests/acceptance/173-dashboard-worktree-panel.acceptance.test.js.map +1 -0
- package/dist/tests/factories/lastSweepSummary.factory.d.ts +5 -0
- package/dist/tests/factories/lastSweepSummary.factory.d.ts.map +1 -0
- package/dist/tests/factories/lastSweepSummary.factory.js +12 -0
- package/dist/tests/factories/lastSweepSummary.factory.js.map +1 -0
- package/dist/tests/integration/claudeInvocation.integration.test.js +5 -1
- package/dist/tests/integration/claudeInvocation.integration.test.js.map +1 -1
- package/dist/tests/stubs/worktreeSizeProbe.stub.d.ts +11 -0
- package/dist/tests/stubs/worktreeSizeProbe.stub.d.ts.map +1 -0
- package/dist/tests/stubs/worktreeSizeProbe.stub.js +22 -0
- package/dist/tests/stubs/worktreeSizeProbe.stub.js.map +1 -0
- package/dist/tests/units/dashboard/modules/worktreePanel.test.d.ts +2 -0
- package/dist/tests/units/dashboard/modules/worktreePanel.test.d.ts.map +1 -0
- package/dist/tests/units/dashboard/modules/worktreePanel.test.js +274 -0
- package/dist/tests/units/dashboard/modules/worktreePanel.test.js.map +1 -0
- package/dist/tests/units/entities/github/githubPullRequestEvent.guard.test.js +26 -0
- package/dist/tests/units/entities/github/githubPullRequestEvent.guard.test.js.map +1 -1
- package/dist/tests/units/frameworks/claude/timers/claudeInvocationTimers.test.js +3 -3
- package/dist/tests/units/frameworks/claude/timers/claudeInvocationTimers.test.js.map +1 -1
- package/dist/tests/units/frameworks/scheduler/worktreeSweepScheduler.test.d.ts +2 -0
- package/dist/tests/units/frameworks/scheduler/worktreeSweepScheduler.test.d.ts.map +1 -0
- package/dist/tests/units/frameworks/scheduler/worktreeSweepScheduler.test.js +236 -0
- package/dist/tests/units/frameworks/scheduler/worktreeSweepScheduler.test.js.map +1 -0
- package/dist/tests/units/interface-adapters/controllers/webhook/github.controller.test.js +74 -2
- package/dist/tests/units/interface-adapters/controllers/webhook/github.controller.test.js.map +1 -1
- package/dist/tests/units/modules/claude-invocation/interface-adapters/gateways/claudeSession.cli.gateway.test.js +17 -5
- package/dist/tests/units/modules/claude-invocation/interface-adapters/gateways/claudeSession.cli.gateway.test.js.map +1 -1
- package/dist/tests/units/modules/claude-invocation/usecases/auditBilling.usecase.test.js +2 -18
- package/dist/tests/units/modules/claude-invocation/usecases/auditBilling.usecase.test.js.map +1 -1
- package/dist/tests/units/modules/worktree-management/entities/sweep/lastSweepSummary.schema.test.d.ts +2 -0
- package/dist/tests/units/modules/worktree-management/entities/sweep/lastSweepSummary.schema.test.d.ts.map +1 -0
- package/dist/tests/units/modules/worktree-management/entities/sweep/lastSweepSummary.schema.test.js +44 -0
- package/dist/tests/units/modules/worktree-management/entities/sweep/lastSweepSummary.schema.test.js.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.test.d.ts +2 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.test.d.ts.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.test.js +160 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/controllers/http/worktreeOverview.routes.test.js.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.test.d.ts +2 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.test.d.ts.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.test.js +54 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/gateways/worktreeSizeProbe.cli.gateway.test.js.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.test.d.ts +2 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.test.d.ts.map +1 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.test.js +259 -0
- package/dist/tests/units/modules/worktree-management/interface-adapters/presenters/worktreePanel.presenter.test.js.map +1 -0
- package/package.json +2 -1
- package/scripts/copyAssets.mjs +8 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard module — worktree pool panel (SPEC-173).
|
|
3
|
+
* Humble object: pure functions, no global state, no direct DOM access here.
|
|
4
|
+
* Animation choreography lives in the consumer (index.html) and styles.css.
|
|
5
|
+
*
|
|
6
|
+
* Visual DNA: "Agentic OS" — see project_agentic_os_design_dna.md.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {'active' | 'idle' | 'stale'} WorktreeRowStatus
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} WorktreeRowViewModel
|
|
15
|
+
* @property {number} mrNumber
|
|
16
|
+
* @property {string} path
|
|
17
|
+
* @property {string} mtime
|
|
18
|
+
* @property {number} ageSeconds
|
|
19
|
+
* @property {number | null} sizeBytes
|
|
20
|
+
* @property {WorktreeRowStatus} status
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} WorktreeGroupViewModel
|
|
25
|
+
* @property {'gitlab' | 'github'} platform
|
|
26
|
+
* @property {string} projectPath
|
|
27
|
+
* @property {WorktreeRowViewModel[]} worktrees
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {Object} LastSweepViewModel
|
|
32
|
+
* @property {string} ranAt
|
|
33
|
+
* @property {number} removed
|
|
34
|
+
* @property {number} failures
|
|
35
|
+
* @property {number} scanned
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {Object} WorktreePanelViewModel
|
|
40
|
+
* @property {number} totalCount
|
|
41
|
+
* @property {number} totalSizeBytes
|
|
42
|
+
* @property {number} activeCount
|
|
43
|
+
* @property {number} idleCount
|
|
44
|
+
* @property {number} staleCount
|
|
45
|
+
* @property {string} nextSweepAt
|
|
46
|
+
* @property {LastSweepViewModel | null} lastSweep
|
|
47
|
+
* @property {WorktreeGroupViewModel[]} groups
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {Object} WorktreeTotals
|
|
52
|
+
* @property {number} total
|
|
53
|
+
* @property {number} active
|
|
54
|
+
* @property {number} idle
|
|
55
|
+
* @property {number} stale
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {Object} NullableWorktreeTotals
|
|
60
|
+
* @property {number | null} total
|
|
61
|
+
* @property {number | null} active
|
|
62
|
+
* @property {number | null} idle
|
|
63
|
+
* @property {number | null} stale
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {{ status: 'ok'; payload: { ranAt: string; removed: number; failures: number; scanned: number } }
|
|
68
|
+
* | { status: 'conflict'; startedAt: string }
|
|
69
|
+
* | { status: 'error'; reason?: string }} ManualSweepResult
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {string | number | null | undefined} text
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
function escapeHtml(text) {
|
|
77
|
+
if (text === null || text === undefined) return '';
|
|
78
|
+
return String(text)
|
|
79
|
+
.replace(/&/g, '&')
|
|
80
|
+
.replace(/</g, '<')
|
|
81
|
+
.replace(/>/g, '>')
|
|
82
|
+
.replace(/"/g, '"')
|
|
83
|
+
.replace(/'/g, ''');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {number | null} bytes
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
export function formatBytes(bytes) {
|
|
91
|
+
if (bytes === null || bytes === undefined) return '—';
|
|
92
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
93
|
+
const kb = bytes / 1024;
|
|
94
|
+
if (kb < 1024) return `${kb.toFixed(1)} KB`;
|
|
95
|
+
const mb = kb / 1024;
|
|
96
|
+
if (mb < 1024) return `${mb.toFixed(1)} MB`;
|
|
97
|
+
const gb = mb / 1024;
|
|
98
|
+
return `${gb.toFixed(2)} GB`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {number} ageSeconds
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
export function formatRelativeAge(ageSeconds) {
|
|
106
|
+
if (ageSeconds < 60) return `${ageSeconds}s`;
|
|
107
|
+
const minutes = Math.floor(ageSeconds / 60);
|
|
108
|
+
if (minutes < 60) return `${minutes}m`;
|
|
109
|
+
const hours = Math.floor(minutes / 60);
|
|
110
|
+
if (hours < 24) return `${hours}h`;
|
|
111
|
+
const days = Math.floor(hours / 24);
|
|
112
|
+
return `${days}d`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {string} mtime
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
function formatMtime(mtime) {
|
|
120
|
+
const date = new Date(mtime);
|
|
121
|
+
if (Number.isNaN(date.getTime())) return mtime;
|
|
122
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
123
|
+
return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())} ${pad(
|
|
124
|
+
date.getUTCHours(),
|
|
125
|
+
)}:${pad(date.getUTCMinutes())}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {string} path
|
|
130
|
+
* @returns {string}
|
|
131
|
+
*/
|
|
132
|
+
function truncatePathMiddle(path) {
|
|
133
|
+
if (path.length <= 48) return path;
|
|
134
|
+
return `${path.slice(0, 24)}…${path.slice(-20)}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {WorktreeRowStatus} status
|
|
139
|
+
* @returns {string}
|
|
140
|
+
*/
|
|
141
|
+
export function renderWorktreeStatusBadge(status) {
|
|
142
|
+
if (status === 'active') {
|
|
143
|
+
return '<span class="worktree-status worktree-status-active" data-status="active"><span class="worktree-status-glyph">●</span><span class="worktree-status-label">ACTIVE</span></span>';
|
|
144
|
+
}
|
|
145
|
+
if (status === 'idle') {
|
|
146
|
+
return '<span class="worktree-status worktree-status-idle" data-status="idle"><span class="worktree-status-glyph">○</span><span class="worktree-status-label">IDLE</span></span>';
|
|
147
|
+
}
|
|
148
|
+
return '<span class="worktree-status worktree-status-stale" data-status="stale"><span class="worktree-status-glyph">◆</span><span class="worktree-status-label">STALE</span></span>';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
export function renderWorktreeEmptyState() {
|
|
155
|
+
return `
|
|
156
|
+
<div class="worktree-empty">
|
|
157
|
+
<svg class="worktree-empty-illustration" viewBox="0 0 120 120" width="96" height="96" aria-hidden="true">
|
|
158
|
+
<g fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round">
|
|
159
|
+
<path d="M60 102 V62" />
|
|
160
|
+
<path d="M60 62 L36 42" />
|
|
161
|
+
<path d="M60 62 L84 42" />
|
|
162
|
+
<path d="M36 42 L26 26" />
|
|
163
|
+
<path d="M36 42 L48 26" />
|
|
164
|
+
<path d="M84 42 L72 26" />
|
|
165
|
+
<path d="M84 42 L94 26" />
|
|
166
|
+
<circle cx="26" cy="26" r="3" />
|
|
167
|
+
<circle cx="48" cy="26" r="3" />
|
|
168
|
+
<circle cx="72" cy="26" r="3" />
|
|
169
|
+
<circle cx="94" cy="26" r="3" class="worktree-empty-leaf" />
|
|
170
|
+
<rect x="50" y="100" width="20" height="6" rx="0" />
|
|
171
|
+
</g>
|
|
172
|
+
</svg>
|
|
173
|
+
<div class="worktree-empty-title">// POOL EMPTY</div>
|
|
174
|
+
<div class="worktree-empty-subtitle">No worktree on disk. The next scheduled review will materialize one.</div>
|
|
175
|
+
</div>
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {WorktreeRowViewModel} row
|
|
181
|
+
* @param {string} groupLabel Display label for the row's group (e.g. "gitlab · group/project").
|
|
182
|
+
* @returns {string}
|
|
183
|
+
*/
|
|
184
|
+
function renderRow(row, groupLabel) {
|
|
185
|
+
const escapedPath = escapeHtml(row.path);
|
|
186
|
+
const truncatedPath = escapeHtml(truncatePathMiddle(row.path));
|
|
187
|
+
const truncatedLabel =
|
|
188
|
+
groupLabel.length > 28 ? `${groupLabel.slice(0, 24)}…` : groupLabel;
|
|
189
|
+
return `
|
|
190
|
+
<tr class="worktree-row" data-status="${escapeHtml(row.status)}">
|
|
191
|
+
<td class="worktree-cell worktree-cell-status">${renderWorktreeStatusBadge(row.status)}</td>
|
|
192
|
+
<td class="worktree-cell worktree-cell-identity">
|
|
193
|
+
<span class="worktree-project" title="${escapeHtml(groupLabel)}">${escapeHtml(truncatedLabel)}</span>
|
|
194
|
+
<span class="worktree-mr">#${escapeHtml(row.mrNumber)}</span>
|
|
195
|
+
</td>
|
|
196
|
+
<td class="worktree-cell worktree-cell-path"><span title="${escapedPath}">${truncatedPath}</span></td>
|
|
197
|
+
<td class="worktree-cell worktree-cell-age">${escapeHtml(formatRelativeAge(row.ageSeconds))}</td>
|
|
198
|
+
<td class="worktree-cell worktree-cell-size">${escapeHtml(formatBytes(row.sizeBytes))}</td>
|
|
199
|
+
<td class="worktree-cell worktree-cell-mtime">${escapeHtml(formatMtime(row.mtime))}</td>
|
|
200
|
+
</tr>
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* @param {WorktreeGroupViewModel} group
|
|
206
|
+
* @returns {string}
|
|
207
|
+
*/
|
|
208
|
+
function renderGroupRows(group) {
|
|
209
|
+
return group.worktrees.map((row) => renderRow(row, `${group.platform} · ${group.projectPath}`)).join('');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Flattens the view model status counts into a single record consumed by the
|
|
214
|
+
* animation layer (count-up + change-flash). The presenter is the single source
|
|
215
|
+
* of truth for these counts — this helper just renames the keys.
|
|
216
|
+
*
|
|
217
|
+
* @param {WorktreePanelViewModel} viewModel
|
|
218
|
+
* @returns {WorktreeTotals}
|
|
219
|
+
*/
|
|
220
|
+
export function snapshotTotals(viewModel) {
|
|
221
|
+
return {
|
|
222
|
+
total: viewModel.totalCount,
|
|
223
|
+
active: viewModel.activeCount,
|
|
224
|
+
idle: viewModel.idleCount,
|
|
225
|
+
stale: viewModel.staleCount,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Returns the metric keys whose value changed between two snapshots. A key
|
|
231
|
+
* whose previous value is null (cold start) is ignored — only transitions
|
|
232
|
+
* between known values are surfaced.
|
|
233
|
+
*
|
|
234
|
+
* @param {NullableWorktreeTotals} previous
|
|
235
|
+
* @param {WorktreeTotals} next
|
|
236
|
+
* @returns {Array<'total' | 'active' | 'idle' | 'stale'>}
|
|
237
|
+
*/
|
|
238
|
+
export function computeChangedMetricKeys(previous, next) {
|
|
239
|
+
const keys = /** @type {Array<'total' | 'active' | 'idle' | 'stale'>} */ ([
|
|
240
|
+
'total',
|
|
241
|
+
'active',
|
|
242
|
+
'idle',
|
|
243
|
+
'stale',
|
|
244
|
+
]);
|
|
245
|
+
return keys.filter((key) => previous[key] !== null && previous[key] !== next[key]);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param {LastSweepViewModel | null} lastSweep
|
|
250
|
+
* @returns {string}
|
|
251
|
+
*/
|
|
252
|
+
function renderLastSweep(lastSweep) {
|
|
253
|
+
if (lastSweep === null) {
|
|
254
|
+
return '<span class="worktree-lastsweep-value">never</span>';
|
|
255
|
+
}
|
|
256
|
+
const ranAt = formatMtime(lastSweep.ranAt);
|
|
257
|
+
return `<span class="worktree-lastsweep-value">${escapeHtml(ranAt)} UTC · removed ${escapeHtml(lastSweep.removed)} · failures ${escapeHtml(lastSweep.failures)} · scanned ${escapeHtml(lastSweep.scanned)}</span>`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @param {string} nextSweepAt
|
|
262
|
+
* @returns {string}
|
|
263
|
+
*/
|
|
264
|
+
function renderNextSweep(nextSweepAt) {
|
|
265
|
+
const date = new Date(nextSweepAt);
|
|
266
|
+
if (Number.isNaN(date.getTime())) return escapeHtml(nextSweepAt);
|
|
267
|
+
const diffMs = date.getTime() - Date.now();
|
|
268
|
+
if (diffMs <= 0) return 'imminent';
|
|
269
|
+
const totalMinutes = Math.round(diffMs / 60000);
|
|
270
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
271
|
+
const minutes = totalMinutes % 60;
|
|
272
|
+
if (hours <= 0) return `in ${minutes}m`;
|
|
273
|
+
const pad = (value) => String(value).padStart(2, '0');
|
|
274
|
+
return `in ${hours}h ${pad(minutes)}m`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @param {WorktreePanelViewModel} viewModel
|
|
279
|
+
* @returns {string}
|
|
280
|
+
*/
|
|
281
|
+
export function renderWorktreeSection(viewModel) {
|
|
282
|
+
const isEmpty = viewModel.totalCount === 0;
|
|
283
|
+
|
|
284
|
+
const body = isEmpty
|
|
285
|
+
? renderWorktreeEmptyState()
|
|
286
|
+
: `
|
|
287
|
+
<div class="worktree-metrics">
|
|
288
|
+
<div class="worktree-metric"><div class="worktree-metric-label">TOTAL</div><div class="worktree-metric-value" data-metric="total">${escapeHtml(viewModel.totalCount)}</div></div>
|
|
289
|
+
<div class="worktree-metric"><div class="worktree-metric-label">ACTIVE</div><div class="worktree-metric-value" data-metric="active">${escapeHtml(viewModel.activeCount)}</div></div>
|
|
290
|
+
<div class="worktree-metric"><div class="worktree-metric-label">IDLE</div><div class="worktree-metric-value" data-metric="idle">${escapeHtml(viewModel.idleCount)}</div></div>
|
|
291
|
+
<div class="worktree-metric"><div class="worktree-metric-label">STALE</div><div class="worktree-metric-value" data-metric="stale">${escapeHtml(viewModel.staleCount)}</div></div>
|
|
292
|
+
<div class="worktree-metric"><div class="worktree-metric-label">TOTAL SIZE</div><div class="worktree-metric-value" data-metric="size">${escapeHtml(formatBytes(viewModel.totalSizeBytes))}</div></div>
|
|
293
|
+
</div>
|
|
294
|
+
<div class="worktree-table-wrapper">
|
|
295
|
+
<table class="worktree-table">
|
|
296
|
+
<thead>
|
|
297
|
+
<tr>
|
|
298
|
+
<th class="worktree-th">STATUS</th>
|
|
299
|
+
<th class="worktree-th">PLATFORM · MR</th>
|
|
300
|
+
<th class="worktree-th">PATH</th>
|
|
301
|
+
<th class="worktree-th">AGE</th>
|
|
302
|
+
<th class="worktree-th">SIZE</th>
|
|
303
|
+
<th class="worktree-th">MTIME</th>
|
|
304
|
+
</tr>
|
|
305
|
+
</thead>
|
|
306
|
+
<tbody>
|
|
307
|
+
${viewModel.groups.map(renderGroupRows).join('')}
|
|
308
|
+
</tbody>
|
|
309
|
+
</table>
|
|
310
|
+
</div>
|
|
311
|
+
`;
|
|
312
|
+
|
|
313
|
+
return `
|
|
314
|
+
<div class="worktree-panel" data-empty="${isEmpty ? 'true' : 'false'}">
|
|
315
|
+
<div class="worktree-panel-header">
|
|
316
|
+
<span class="worktree-panel-title">// WORKTREE POOL · ${escapeHtml(viewModel.totalCount)}</span>
|
|
317
|
+
<button class="worktree-sweep-button" data-action="sweep" type="button">
|
|
318
|
+
<svg class="worktree-sweep-broom" viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
|
|
319
|
+
<g fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round">
|
|
320
|
+
<path d="M14 4 L20 10" />
|
|
321
|
+
<path d="M13 5 L4 14 L10 20 L19 11" />
|
|
322
|
+
<path d="M4 14 L2 18" />
|
|
323
|
+
<path d="M6 16 L4 20" />
|
|
324
|
+
<path d="M8 18 L6 22" />
|
|
325
|
+
</g>
|
|
326
|
+
</svg>
|
|
327
|
+
<span class="worktree-sweep-label">SWEEP NOW</span>
|
|
328
|
+
</button>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="worktree-panel-body">${body}</div>
|
|
331
|
+
<div class="worktree-panel-footer">
|
|
332
|
+
<div class="worktree-footer-block">
|
|
333
|
+
<span class="worktree-footer-label">// LAST SWEEP</span>
|
|
334
|
+
${renderLastSweep(viewModel.lastSweep)}
|
|
335
|
+
</div>
|
|
336
|
+
<div class="worktree-footer-block worktree-footer-next">
|
|
337
|
+
<span class="worktree-footer-label">// NEXT SWEEP</span>
|
|
338
|
+
<span class="worktree-nextsweep-value">${escapeHtml(renderNextSweep(viewModel.nextSweepAt))}</span>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* @param {typeof fetch} [fetchImpl]
|
|
347
|
+
* @returns {Promise<WorktreePanelViewModel>}
|
|
348
|
+
*/
|
|
349
|
+
export async function fetchWorktreeOverview(fetchImpl = fetch) {
|
|
350
|
+
const response = await fetchImpl('/api/worktrees');
|
|
351
|
+
if (!response.ok) {
|
|
352
|
+
throw new Error(`Worktree overview request failed: ${response.status}`);
|
|
353
|
+
}
|
|
354
|
+
return response.json();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @param {typeof fetch} [fetchImpl]
|
|
359
|
+
* @returns {Promise<ManualSweepResult>}
|
|
360
|
+
*/
|
|
361
|
+
export async function triggerManualSweep(fetchImpl = fetch) {
|
|
362
|
+
const response = await fetchImpl('/api/worktrees/sweep', { method: 'POST' });
|
|
363
|
+
if (response.ok) {
|
|
364
|
+
const payload = await response.json();
|
|
365
|
+
return { status: 'ok', payload };
|
|
366
|
+
}
|
|
367
|
+
if (response.status === 409) {
|
|
368
|
+
const body = await response.json();
|
|
369
|
+
return { status: 'conflict', startedAt: body.startedAt };
|
|
370
|
+
}
|
|
371
|
+
const body = await response.json().catch(() => ({}));
|
|
372
|
+
return { status: 'error', reason: body.error };
|
|
373
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktreePanel.js","sourceRoot":"","sources":["../../../src/dashboard/modules/worktreePanel.js"],"names":[],"mappings":"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"}
|
|
@@ -4107,3 +4107,315 @@ input:focus-visible,
|
|
|
4107
4107
|
.budget-slider-status { font-size: 0.8rem; min-width: 7rem; }
|
|
4108
4108
|
.budget-slider-status--ok { color: #16a34a; }
|
|
4109
4109
|
.budget-slider-status--error { color: #dc2626; }
|
|
4110
|
+
|
|
4111
|
+
/* ============================================================
|
|
4112
|
+
SPEC-173 — Worktree Pool ("Agentic OS" visual DNA)
|
|
4113
|
+
Scoped: all rules target #worktree-section so the theme stays
|
|
4114
|
+
contained — no leak into the surrounding dashboard.
|
|
4115
|
+
Reference: project_agentic_os_design_dna.md.
|
|
4116
|
+
============================================================ */
|
|
4117
|
+
|
|
4118
|
+
#worktree-section {
|
|
4119
|
+
--worktree-bg: #0a0908;
|
|
4120
|
+
--worktree-bg-elevated: #13110f;
|
|
4121
|
+
--worktree-border-faint: rgba(255, 180, 100, 0.12);
|
|
4122
|
+
--worktree-border-active: rgba(255, 138, 61, 0.65);
|
|
4123
|
+
--worktree-accent: #ff8a3d;
|
|
4124
|
+
--worktree-accent-dim: #a85a25;
|
|
4125
|
+
--worktree-success: #5ce28b;
|
|
4126
|
+
--worktree-warn: #f3c969;
|
|
4127
|
+
--worktree-text-primary: #f3eee8;
|
|
4128
|
+
--worktree-text-muted: #7a716a;
|
|
4129
|
+
--worktree-glow-active: 0 0 12px rgba(255, 138, 61, 0.55);
|
|
4130
|
+
--worktree-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Berkeley Mono", monospace;
|
|
4131
|
+
|
|
4132
|
+
display: block;
|
|
4133
|
+
margin: 24px 0;
|
|
4134
|
+
font-family: var(--worktree-mono);
|
|
4135
|
+
color: var(--worktree-text-primary);
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
#worktree-section .worktree-panel {
|
|
4139
|
+
position: relative;
|
|
4140
|
+
background: var(--worktree-bg);
|
|
4141
|
+
border: 1px solid var(--worktree-border-faint);
|
|
4142
|
+
padding: 18px 20px 16px;
|
|
4143
|
+
animation: worktree-reveal 250ms ease-out;
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
#worktree-section .worktree-panel::before,
|
|
4147
|
+
#worktree-section .worktree-panel::after {
|
|
4148
|
+
content: "";
|
|
4149
|
+
position: absolute;
|
|
4150
|
+
width: 12px;
|
|
4151
|
+
height: 12px;
|
|
4152
|
+
pointer-events: none;
|
|
4153
|
+
}
|
|
4154
|
+
#worktree-section .worktree-panel::before {
|
|
4155
|
+
top: -1px;
|
|
4156
|
+
left: -1px;
|
|
4157
|
+
border-top: 2px solid var(--worktree-border-active);
|
|
4158
|
+
border-left: 2px solid var(--worktree-border-active);
|
|
4159
|
+
}
|
|
4160
|
+
#worktree-section .worktree-panel::after {
|
|
4161
|
+
bottom: -1px;
|
|
4162
|
+
right: -1px;
|
|
4163
|
+
border-bottom: 2px solid var(--worktree-border-active);
|
|
4164
|
+
border-right: 2px solid var(--worktree-border-active);
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
@keyframes worktree-reveal {
|
|
4168
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
4169
|
+
to { opacity: 1; transform: translateY(0); }
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4172
|
+
#worktree-section .worktree-panel-header {
|
|
4173
|
+
display: flex;
|
|
4174
|
+
align-items: center;
|
|
4175
|
+
justify-content: space-between;
|
|
4176
|
+
margin-bottom: 14px;
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4179
|
+
#worktree-section .worktree-panel-title {
|
|
4180
|
+
font-family: var(--worktree-mono);
|
|
4181
|
+
font-size: 13px;
|
|
4182
|
+
letter-spacing: 0.04em;
|
|
4183
|
+
color: var(--worktree-accent);
|
|
4184
|
+
text-transform: none;
|
|
4185
|
+
}
|
|
4186
|
+
|
|
4187
|
+
#worktree-section .worktree-sweep-button {
|
|
4188
|
+
display: inline-flex;
|
|
4189
|
+
align-items: center;
|
|
4190
|
+
gap: 8px;
|
|
4191
|
+
background: transparent;
|
|
4192
|
+
color: var(--worktree-accent);
|
|
4193
|
+
border: 1px solid var(--worktree-border-active);
|
|
4194
|
+
padding: 6px 12px;
|
|
4195
|
+
font-family: var(--worktree-mono);
|
|
4196
|
+
font-size: 11px;
|
|
4197
|
+
letter-spacing: 0.1em;
|
|
4198
|
+
cursor: pointer;
|
|
4199
|
+
border-radius: 0;
|
|
4200
|
+
transition: background-color 120ms ease-out, color 120ms ease-out;
|
|
4201
|
+
}
|
|
4202
|
+
#worktree-section .worktree-sweep-button:hover {
|
|
4203
|
+
background: rgba(255, 138, 61, 0.08);
|
|
4204
|
+
}
|
|
4205
|
+
#worktree-section .worktree-sweep-button:active {
|
|
4206
|
+
background: rgba(255, 138, 61, 0.18);
|
|
4207
|
+
}
|
|
4208
|
+
#worktree-section .worktree-sweep-pending {
|
|
4209
|
+
border-color: var(--worktree-warn);
|
|
4210
|
+
color: var(--worktree-warn);
|
|
4211
|
+
}
|
|
4212
|
+
#worktree-section .worktree-sweep-conflict {
|
|
4213
|
+
border-color: var(--worktree-warn);
|
|
4214
|
+
color: var(--worktree-warn);
|
|
4215
|
+
}
|
|
4216
|
+
#worktree-section .worktree-sweep-error {
|
|
4217
|
+
border-color: #ff5252;
|
|
4218
|
+
color: #ff5252;
|
|
4219
|
+
}
|
|
4220
|
+
#worktree-section .worktree-sweep-broom {
|
|
4221
|
+
display: inline-block;
|
|
4222
|
+
}
|
|
4223
|
+
|
|
4224
|
+
#worktree-section .worktree-metrics {
|
|
4225
|
+
display: grid;
|
|
4226
|
+
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
4227
|
+
gap: 16px;
|
|
4228
|
+
padding: 12px 0 14px;
|
|
4229
|
+
border-bottom: 1px dashed var(--worktree-border-faint);
|
|
4230
|
+
}
|
|
4231
|
+
#worktree-section .worktree-metric {
|
|
4232
|
+
display: flex;
|
|
4233
|
+
flex-direction: column;
|
|
4234
|
+
gap: 6px;
|
|
4235
|
+
}
|
|
4236
|
+
#worktree-section .worktree-metric-label {
|
|
4237
|
+
font-size: 10px;
|
|
4238
|
+
letter-spacing: 0.1em;
|
|
4239
|
+
color: var(--worktree-text-muted);
|
|
4240
|
+
text-transform: uppercase;
|
|
4241
|
+
}
|
|
4242
|
+
#worktree-section .worktree-metric-value {
|
|
4243
|
+
font-family: var(--worktree-mono);
|
|
4244
|
+
font-size: 26px;
|
|
4245
|
+
color: var(--worktree-text-primary);
|
|
4246
|
+
font-weight: 500;
|
|
4247
|
+
padding: 2px 4px;
|
|
4248
|
+
margin: -2px -4px;
|
|
4249
|
+
}
|
|
4250
|
+
|
|
4251
|
+
#worktree-section .worktree-table-wrapper {
|
|
4252
|
+
overflow-x: auto;
|
|
4253
|
+
margin-top: 10px;
|
|
4254
|
+
}
|
|
4255
|
+
#worktree-section .worktree-table {
|
|
4256
|
+
width: 100%;
|
|
4257
|
+
border-collapse: collapse;
|
|
4258
|
+
font-family: var(--worktree-mono);
|
|
4259
|
+
font-size: 12px;
|
|
4260
|
+
background: var(--worktree-bg);
|
|
4261
|
+
}
|
|
4262
|
+
#worktree-section .worktree-th {
|
|
4263
|
+
text-align: left;
|
|
4264
|
+
font-size: 10px;
|
|
4265
|
+
letter-spacing: 0.1em;
|
|
4266
|
+
color: var(--worktree-text-muted);
|
|
4267
|
+
text-transform: uppercase;
|
|
4268
|
+
padding: 8px 10px;
|
|
4269
|
+
border-bottom: 1px solid var(--worktree-border-faint);
|
|
4270
|
+
font-weight: normal;
|
|
4271
|
+
}
|
|
4272
|
+
#worktree-section .worktree-row {
|
|
4273
|
+
position: relative;
|
|
4274
|
+
background: var(--worktree-bg-elevated);
|
|
4275
|
+
transition: background-color 120ms ease-out;
|
|
4276
|
+
}
|
|
4277
|
+
#worktree-section .worktree-row:hover {
|
|
4278
|
+
background: rgba(255, 138, 61, 0.04);
|
|
4279
|
+
}
|
|
4280
|
+
#worktree-section .worktree-row + .worktree-row > .worktree-cell {
|
|
4281
|
+
border-top: 1px solid var(--worktree-border-faint);
|
|
4282
|
+
}
|
|
4283
|
+
#worktree-section .worktree-cell {
|
|
4284
|
+
padding: 10px 10px;
|
|
4285
|
+
vertical-align: middle;
|
|
4286
|
+
color: var(--worktree-text-primary);
|
|
4287
|
+
}
|
|
4288
|
+
#worktree-section .worktree-cell-identity {
|
|
4289
|
+
display: flex;
|
|
4290
|
+
flex-direction: column;
|
|
4291
|
+
gap: 2px;
|
|
4292
|
+
}
|
|
4293
|
+
#worktree-section .worktree-project {
|
|
4294
|
+
font-family: var(--worktree-mono);
|
|
4295
|
+
font-size: 12px;
|
|
4296
|
+
color: var(--worktree-text-primary);
|
|
4297
|
+
}
|
|
4298
|
+
#worktree-section .worktree-mr {
|
|
4299
|
+
font-family: var(--worktree-mono);
|
|
4300
|
+
font-size: 11px;
|
|
4301
|
+
color: var(--worktree-text-muted);
|
|
4302
|
+
}
|
|
4303
|
+
#worktree-section .worktree-cell-path,
|
|
4304
|
+
#worktree-section .worktree-cell-age,
|
|
4305
|
+
#worktree-section .worktree-cell-size,
|
|
4306
|
+
#worktree-section .worktree-cell-mtime {
|
|
4307
|
+
font-family: var(--worktree-mono);
|
|
4308
|
+
font-size: 12px;
|
|
4309
|
+
color: var(--worktree-text-muted);
|
|
4310
|
+
}
|
|
4311
|
+
#worktree-section .worktree-cell-path > span {
|
|
4312
|
+
cursor: help;
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
#worktree-section .worktree-status {
|
|
4316
|
+
display: inline-flex;
|
|
4317
|
+
align-items: center;
|
|
4318
|
+
gap: 6px;
|
|
4319
|
+
font-family: var(--worktree-mono);
|
|
4320
|
+
font-size: 11px;
|
|
4321
|
+
letter-spacing: 0.08em;
|
|
4322
|
+
}
|
|
4323
|
+
#worktree-section .worktree-status-glyph {
|
|
4324
|
+
font-size: 11px;
|
|
4325
|
+
line-height: 1;
|
|
4326
|
+
}
|
|
4327
|
+
#worktree-section .worktree-status-active {
|
|
4328
|
+
color: var(--worktree-accent);
|
|
4329
|
+
}
|
|
4330
|
+
#worktree-section .worktree-status-active .worktree-status-glyph {
|
|
4331
|
+
text-shadow: var(--worktree-glow-active);
|
|
4332
|
+
animation: worktree-pulse 1.4s ease-in-out infinite;
|
|
4333
|
+
}
|
|
4334
|
+
#worktree-section .worktree-status-idle {
|
|
4335
|
+
color: var(--worktree-accent-dim);
|
|
4336
|
+
opacity: 0.85;
|
|
4337
|
+
}
|
|
4338
|
+
#worktree-section .worktree-status-stale {
|
|
4339
|
+
color: var(--worktree-warn);
|
|
4340
|
+
}
|
|
4341
|
+
@keyframes worktree-pulse {
|
|
4342
|
+
0%, 100% { opacity: 0.75; text-shadow: 0 0 8px rgba(255, 138, 61, 0.45); }
|
|
4343
|
+
50% { opacity: 1; text-shadow: 0 0 14px rgba(255, 138, 61, 0.7); }
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4346
|
+
#worktree-section .worktree-empty {
|
|
4347
|
+
display: flex;
|
|
4348
|
+
flex-direction: column;
|
|
4349
|
+
align-items: center;
|
|
4350
|
+
justify-content: center;
|
|
4351
|
+
padding: 32px 16px 24px;
|
|
4352
|
+
gap: 12px;
|
|
4353
|
+
color: var(--worktree-text-muted);
|
|
4354
|
+
}
|
|
4355
|
+
#worktree-section .worktree-empty-illustration {
|
|
4356
|
+
color: var(--worktree-accent-dim);
|
|
4357
|
+
}
|
|
4358
|
+
#worktree-section .worktree-empty-leaf {
|
|
4359
|
+
animation: worktree-leaf 2s ease-in-out infinite;
|
|
4360
|
+
transform-origin: center;
|
|
4361
|
+
}
|
|
4362
|
+
@keyframes worktree-leaf {
|
|
4363
|
+
0%, 100% { opacity: 0.5; }
|
|
4364
|
+
50% { opacity: 1; }
|
|
4365
|
+
}
|
|
4366
|
+
#worktree-section .worktree-empty-title {
|
|
4367
|
+
font-family: var(--worktree-mono);
|
|
4368
|
+
font-size: 12px;
|
|
4369
|
+
letter-spacing: 0.08em;
|
|
4370
|
+
color: var(--worktree-accent);
|
|
4371
|
+
}
|
|
4372
|
+
#worktree-section .worktree-empty-subtitle {
|
|
4373
|
+
font-family: var(--worktree-mono);
|
|
4374
|
+
font-size: 11px;
|
|
4375
|
+
color: var(--worktree-text-muted);
|
|
4376
|
+
max-width: 360px;
|
|
4377
|
+
text-align: center;
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
#worktree-section .worktree-panel-footer {
|
|
4381
|
+
display: flex;
|
|
4382
|
+
justify-content: space-between;
|
|
4383
|
+
gap: 16px;
|
|
4384
|
+
margin-top: 14px;
|
|
4385
|
+
padding-top: 10px;
|
|
4386
|
+
border-top: 1px dashed var(--worktree-border-faint);
|
|
4387
|
+
font-family: var(--worktree-mono);
|
|
4388
|
+
font-size: 11px;
|
|
4389
|
+
}
|
|
4390
|
+
#worktree-section .worktree-footer-block {
|
|
4391
|
+
display: flex;
|
|
4392
|
+
flex-direction: column;
|
|
4393
|
+
gap: 4px;
|
|
4394
|
+
}
|
|
4395
|
+
#worktree-section .worktree-footer-label {
|
|
4396
|
+
font-size: 10px;
|
|
4397
|
+
letter-spacing: 0.1em;
|
|
4398
|
+
color: var(--worktree-text-muted);
|
|
4399
|
+
}
|
|
4400
|
+
#worktree-section .worktree-lastsweep-value,
|
|
4401
|
+
#worktree-section .worktree-nextsweep-value {
|
|
4402
|
+
color: var(--worktree-text-primary);
|
|
4403
|
+
}
|
|
4404
|
+
#worktree-section .worktree-footer-next {
|
|
4405
|
+
text-align: right;
|
|
4406
|
+
}
|
|
4407
|
+
|
|
4408
|
+
#worktree-section .worktree-panel-error {
|
|
4409
|
+
font-family: var(--worktree-mono);
|
|
4410
|
+
font-size: 12px;
|
|
4411
|
+
color: var(--worktree-warn);
|
|
4412
|
+
padding: 14px 16px;
|
|
4413
|
+
}
|
|
4414
|
+
|
|
4415
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4416
|
+
#worktree-section .worktree-panel,
|
|
4417
|
+
#worktree-section .worktree-status-active .worktree-status-glyph,
|
|
4418
|
+
#worktree-section .worktree-empty-leaf {
|
|
4419
|
+
animation: none !important;
|
|
4420
|
+
}
|
|
4421
|
+
}
|