@shendeguize/dsh-agent-sidecar 0.1.0 → 0.2.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/README.md +78 -11
- package/lib/client.js +4708 -2932
- package/lib/client.js.map +1 -1
- package/package.json +3 -1
- package/src/client/analysis/AnalysisPanel.tsx +19 -27
- package/src/client/analysis/analysis.module.css +36 -97
- package/src/client/board/Board.tsx +255 -48
- package/src/client/board/board.module.css +113 -92
- package/src/client/board/logic.ts +99 -21
- package/src/client/board/project-view-logic.ts +27 -34
- package/src/client/board/project-view.module.css +46 -83
- package/src/client/board/project-view.tsx +50 -8
- package/src/client/board/strings.ts +70 -85
- package/src/client/commands.ts +30 -15
- package/src/client/controller.ts +27 -7
- package/src/client/detail/SessionDetail.tsx +234 -37
- package/src/client/detail/detail.module.css +100 -153
- package/src/client/detail/logic.ts +220 -29
- package/src/client/detail/strings.ts +66 -77
- package/src/client/detail-glue.ts +33 -0
- package/src/client/detail-view.module.css +43 -35
- package/src/client/detail-view.tsx +138 -118
- package/src/client/dsh-tools/LineageTree.tsx +22 -13
- package/src/client/dsh-tools/SearchPanel.tsx +18 -11
- package/src/client/dsh-tools/dsh-tools.module.css +53 -140
- package/src/client/dsh-tools/strings.ts +42 -77
- package/src/client/index.ts +285 -124
- package/src/client/inject/InjectPanel.tsx +81 -61
- package/src/client/inject/inject.module.css +97 -197
- package/src/client/inject/logic.ts +38 -0
- package/src/client/lifecycle/handoff.ts +83 -0
- package/src/client/locales/en.ts +91 -4
- package/src/client/locales/host.ts +131 -0
- package/src/client/locales/index.ts +196 -8
- package/src/client/locales/react.ts +10 -0
- package/src/client/locales/view.ts +38 -0
- package/src/client/locales/zh.ts +200 -125
- package/src/client/mount.tsx +75 -41
- package/src/client/navigation/CenterOverlay.tsx +40 -0
- package/src/client/navigation/center-overlay.module.css +51 -0
- package/src/client/navigation/center.ts +45 -0
- package/src/client/navigation/modal-isolation.ts +152 -0
- package/src/client/navigation/modal-surface-anchor.ts +72 -0
- package/src/client/navigation/sidebar-entry.module.css +73 -0
- package/src/client/navigation/sidebar-entry.ts +309 -0
- package/src/client/primitives/StaticPill.tsx +21 -0
- package/src/client/settings-card.module.css +35 -119
- package/src/client/settings-card.tsx +31 -153
- package/src/client/settings-fields.tsx +131 -0
- package/src/client/sidebar/SidebarTab.tsx +156 -0
- package/src/client/sidebar/model.ts +65 -0
- package/src/client/sidebar/sidebar-tab.module.css +118 -0
- package/src/client/sidebar-tab.tsx +46 -320
- package/src/client/theme/agsc.module.css +31 -0
- package/src/client/theme/parts.ts +56 -0
- package/src/client/ui-integration.ts +86 -0
- package/src/client/widget.tsx +42 -16
- package/src/client/inject/overlay.module.css +0 -22
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/** First-class Agent Center row inserted after the shell's New Session row. */
|
|
2
|
+
import type { CenterNavigation } from './center.ts'
|
|
3
|
+
import css from './sidebar-entry.module.css'
|
|
4
|
+
import { PLUGIN_DOM_ID, surfaceProps } from '../theme/parts.ts'
|
|
5
|
+
|
|
6
|
+
export const SIDEBAR_ENTRY_SELECTOR = '[data-agent-sidecar-sidebar-entry]'
|
|
7
|
+
const ENTRY_ATTRIBUTE = 'data-agent-sidecar-sidebar-entry'
|
|
8
|
+
const LABEL_ATTRIBUTE = 'data-agent-sidecar-sidebar-entry-label'
|
|
9
|
+
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
10
|
+
const ENTRY_BINDING = Symbol.for('@shendeguize/dsh-agent-sidecar/sidebar-entry-binding')
|
|
11
|
+
const ENTRY_BRAND = Symbol.for('@shendeguize/dsh-agent-sidecar/sidebar-entry-brand')
|
|
12
|
+
const ENTRY_DISPATCHER = Symbol.for('@shendeguize/dsh-agent-sidecar/sidebar-entry-dispatcher')
|
|
13
|
+
const warnedForeignEntries = new WeakSet<object>()
|
|
14
|
+
|
|
15
|
+
interface QueryScope { querySelector(selector: string): unknown | null }
|
|
16
|
+
export interface SidebarEntryCopyPort {
|
|
17
|
+
readonly label: string
|
|
18
|
+
readonly accessibilityLabel: string
|
|
19
|
+
subscribe(listener: () => void): () => void
|
|
20
|
+
}
|
|
21
|
+
export interface SidebarEntryCopyTarget {
|
|
22
|
+
readonly button: {
|
|
23
|
+
title: string
|
|
24
|
+
setAttribute(name: string, value: string): void
|
|
25
|
+
}
|
|
26
|
+
readonly label: { textContent: string | null }
|
|
27
|
+
}
|
|
28
|
+
interface SidebarEntryElements extends SidebarEntryCopyTarget { readonly button: HTMLButtonElement }
|
|
29
|
+
interface SidebarEntryObserver { disconnect(): void }
|
|
30
|
+
interface SidebarEntryBinding {
|
|
31
|
+
readonly owner: object
|
|
32
|
+
readonly openCenter: CenterNavigation
|
|
33
|
+
stopCopy: () => void
|
|
34
|
+
observer: SidebarEntryObserver | null
|
|
35
|
+
}
|
|
36
|
+
export interface SidebarEntryOwnerTarget extends SidebarEntryCopyTarget {
|
|
37
|
+
readonly button: SidebarEntryCopyTarget['button'] & { remove(): void }
|
|
38
|
+
}
|
|
39
|
+
/** Narrow, DOM-independent idempotency check used by mount and unit tests. */
|
|
40
|
+
export function hasSidebarEntry(scope: QueryScope): boolean {
|
|
41
|
+
return scope.querySelector(SIDEBAR_ENTRY_SELECTOR) !== null
|
|
42
|
+
}
|
|
43
|
+
export function applyCopy(target: SidebarEntryCopyTarget, copy: SidebarEntryCopyPort): void {
|
|
44
|
+
target.label.textContent = copy.label
|
|
45
|
+
target.button.setAttribute('aria-label', copy.accessibilityLabel)
|
|
46
|
+
target.button.title = copy.accessibilityLabel
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function bindSidebarEntryCopy(
|
|
50
|
+
target: SidebarEntryCopyTarget,
|
|
51
|
+
copy: SidebarEntryCopyPort,
|
|
52
|
+
): () => void {
|
|
53
|
+
const update = (): void => { applyCopy(target, copy) }
|
|
54
|
+
update()
|
|
55
|
+
return copy.subscribe(update)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function bindingOf(button: object): SidebarEntryBinding | undefined {
|
|
59
|
+
return (button as Record<PropertyKey, unknown>)[ENTRY_BINDING] as SidebarEntryBinding | undefined
|
|
60
|
+
}
|
|
61
|
+
function hasCurrentDispatcher(button: object): boolean {
|
|
62
|
+
const record = button as Record<PropertyKey, unknown>
|
|
63
|
+
return (record[ENTRY_BRAND] === true || record[ENTRY_BINDING] !== undefined)
|
|
64
|
+
&& record[ENTRY_DISPATCHER] === true
|
|
65
|
+
}
|
|
66
|
+
/** A pure, DOM-independent ownership check; the idempotency attribute alone is foreign. */
|
|
67
|
+
export function isOwnedSidebarEntry(candidate: unknown): boolean {
|
|
68
|
+
if (candidate === null
|
|
69
|
+
|| (typeof candidate !== 'object' && typeof candidate !== 'function')) return false
|
|
70
|
+
const record = candidate as Record<PropertyKey, unknown>
|
|
71
|
+
if (record[ENTRY_BRAND] === true || record[ENTRY_BINDING] !== undefined) return true
|
|
72
|
+
const element = candidate as {
|
|
73
|
+
tagName?: unknown
|
|
74
|
+
getAttribute?: (name: string) => string | null
|
|
75
|
+
querySelector?: (selector: string) => unknown | null
|
|
76
|
+
}
|
|
77
|
+
return element.tagName === 'BUTTON'
|
|
78
|
+
&& element.getAttribute?.('data-dsh-plugin') === PLUGIN_DOM_ID
|
|
79
|
+
&& element.getAttribute?.('data-dsh-part') === 'sidebar-entry'
|
|
80
|
+
&& element.querySelector?.(`[${LABEL_ATTRIBUTE}]`) != null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function brandSidebarEntry(button: object): void {
|
|
84
|
+
(button as Record<PropertyKey, unknown>)[ENTRY_BRAND] = true
|
|
85
|
+
}
|
|
86
|
+
function installClickDispatcher(button: HTMLButtonElement): void {
|
|
87
|
+
const record = button as unknown as Record<PropertyKey, unknown>
|
|
88
|
+
if (record[ENTRY_DISPATCHER] === true) return
|
|
89
|
+
button.addEventListener('click', () => { openBoundSidebarEntry(button) })
|
|
90
|
+
record[ENTRY_DISPATCHER] = true
|
|
91
|
+
}
|
|
92
|
+
function once(dispose: () => void): () => void {
|
|
93
|
+
let active = true
|
|
94
|
+
return () => {
|
|
95
|
+
if (!active) return
|
|
96
|
+
active = false
|
|
97
|
+
dispose()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Invoke the latest cross-bundle binding, never a captured HMR closure. */
|
|
101
|
+
export function openBoundSidebarEntry(button: object): void {
|
|
102
|
+
try {
|
|
103
|
+
bindingOf(button)?.openCenter()
|
|
104
|
+
} catch {
|
|
105
|
+
// Navigation is a best-effort bridge to host-owned tab DOM.
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Latest-owner-wins binding over one shared DOM button. */
|
|
109
|
+
export function bindSidebarEntryOwner(
|
|
110
|
+
target: SidebarEntryOwnerTarget,
|
|
111
|
+
owner: object,
|
|
112
|
+
openCenter: CenterNavigation,
|
|
113
|
+
copy: SidebarEntryCopyPort,
|
|
114
|
+
startObserver: () => SidebarEntryObserver | null,
|
|
115
|
+
): () => void {
|
|
116
|
+
const previous = bindingOf(target.button)
|
|
117
|
+
previous?.observer?.disconnect()
|
|
118
|
+
previous?.stopCopy()
|
|
119
|
+
|
|
120
|
+
const binding: SidebarEntryBinding = {
|
|
121
|
+
owner,
|
|
122
|
+
openCenter,
|
|
123
|
+
stopCopy: () => {},
|
|
124
|
+
observer: null,
|
|
125
|
+
}
|
|
126
|
+
const sharedButton = target.button as unknown as Record<PropertyKey, unknown>
|
|
127
|
+
sharedButton[ENTRY_BINDING] = binding
|
|
128
|
+
binding.stopCopy = once(bindSidebarEntryCopy(target, copy))
|
|
129
|
+
binding.observer = startObserver()
|
|
130
|
+
|
|
131
|
+
return () => {
|
|
132
|
+
binding.observer?.disconnect()
|
|
133
|
+
if (bindingOf(target.button)?.owner !== owner) return
|
|
134
|
+
binding.stopCopy()
|
|
135
|
+
delete sharedButton[ENTRY_BINDING]
|
|
136
|
+
target.button.remove()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function createIcon(): SVGSVGElement {
|
|
141
|
+
const icon = document.createElementNS(SVG_NS, 'svg')
|
|
142
|
+
for (const [name, value] of Object.entries({
|
|
143
|
+
viewBox: '0 0 16 16',
|
|
144
|
+
width: '16',
|
|
145
|
+
height: '16',
|
|
146
|
+
fill: 'none',
|
|
147
|
+
stroke: 'currentColor',
|
|
148
|
+
'stroke-width': '1.4',
|
|
149
|
+
'stroke-linecap': 'round',
|
|
150
|
+
'stroke-linejoin': 'round',
|
|
151
|
+
'aria-hidden': 'true',
|
|
152
|
+
focusable: 'false',
|
|
153
|
+
})) {
|
|
154
|
+
icon.setAttribute(name, value)
|
|
155
|
+
}
|
|
156
|
+
const orbit = document.createElementNS(SVG_NS, 'circle')
|
|
157
|
+
orbit.setAttribute('cx', '8')
|
|
158
|
+
orbit.setAttribute('cy', '8')
|
|
159
|
+
orbit.setAttribute('r', '5.5')
|
|
160
|
+
const center = document.createElementNS(SVG_NS, 'circle')
|
|
161
|
+
center.setAttribute('cx', '8')
|
|
162
|
+
center.setAttribute('cy', '8')
|
|
163
|
+
center.setAttribute('r', '1.75')
|
|
164
|
+
const path = document.createElementNS(SVG_NS, 'path')
|
|
165
|
+
path.setAttribute('d', 'M8 2.5v3.75M8 9.75v3.75M2.5 8h3.75M9.75 8h3.75')
|
|
166
|
+
icon.append(orbit, center, path)
|
|
167
|
+
return icon
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function createEntry(): SidebarEntryElements {
|
|
171
|
+
const entry = document.createElement('button')
|
|
172
|
+
brandSidebarEntry(entry)
|
|
173
|
+
const surface = surfaceProps('sidebar-entry', css.entry)
|
|
174
|
+
entry.type = 'button'
|
|
175
|
+
entry.className = surface.className
|
|
176
|
+
entry.setAttribute(ENTRY_ATTRIBUTE, '')
|
|
177
|
+
entry.setAttribute('data-dsh-plugin', surface['data-dsh-plugin'])
|
|
178
|
+
entry.setAttribute('data-dsh-part', surface['data-dsh-part'])
|
|
179
|
+
|
|
180
|
+
const icon = document.createElement('span')
|
|
181
|
+
icon.className = css.entryIcon ?? ''
|
|
182
|
+
icon.setAttribute('aria-hidden', 'true')
|
|
183
|
+
icon.appendChild(createIcon())
|
|
184
|
+
const label = document.createElement('span')
|
|
185
|
+
label.className = css.entryLabel ?? ''
|
|
186
|
+
label.setAttribute(LABEL_ATTRIBUTE, '')
|
|
187
|
+
entry.append(icon, label)
|
|
188
|
+
installClickDispatcher(entry)
|
|
189
|
+
return { button: entry, label }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function existingEntry(button: HTMLButtonElement): SidebarEntryElements | null {
|
|
193
|
+
const label = button.querySelector<HTMLElement>(`[${LABEL_ATTRIBUTE}]`)
|
|
194
|
+
?? (button.lastElementChild as HTMLElement | null)
|
|
195
|
+
return label === null ? null : { button, label }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function rejectLegacyEntry(): null {
|
|
199
|
+
console.warn('[agent-sidecar] Sidebar legacy entry cannot be safely replaced; leaving it untouched.')
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function replaceLegacyEntry(button: HTMLButtonElement): SidebarEntryElements | null {
|
|
204
|
+
if (typeof button.cloneNode !== 'function' || typeof button.replaceWith !== 'function') {
|
|
205
|
+
return rejectLegacyEntry()
|
|
206
|
+
}
|
|
207
|
+
let clone: HTMLButtonElement
|
|
208
|
+
try {
|
|
209
|
+
clone = button.cloneNode(true) as HTMLButtonElement
|
|
210
|
+
} catch {
|
|
211
|
+
return rejectLegacyEntry()
|
|
212
|
+
}
|
|
213
|
+
const elements = clone.tagName === 'BUTTON' && typeof clone.addEventListener === 'function'
|
|
214
|
+
? existingEntry(clone)
|
|
215
|
+
: null
|
|
216
|
+
if (elements === null) return rejectLegacyEntry()
|
|
217
|
+
try {
|
|
218
|
+
button.replaceWith(clone)
|
|
219
|
+
} catch {
|
|
220
|
+
return rejectLegacyEntry()
|
|
221
|
+
}
|
|
222
|
+
brandSidebarEntry(clone)
|
|
223
|
+
installClickDispatcher(clone)
|
|
224
|
+
return elements
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function sidebarRoot(): HTMLElement | null {
|
|
228
|
+
const column = document.querySelector<HTMLElement>(
|
|
229
|
+
'[data-pane="sidebar"], [class*="sidebarCol"]',
|
|
230
|
+
)
|
|
231
|
+
if (column === null) return null
|
|
232
|
+
const logoOwner = column.querySelector<HTMLElement>('[class*="logoRow"]')?.parentElement
|
|
233
|
+
return logoOwner ?? (column.firstElementChild as HTMLElement | null)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function newSessionRow(root: HTMLElement): Element | null {
|
|
237
|
+
const nested = root.querySelector<HTMLButtonElement>('button[class*="newSession"]')
|
|
238
|
+
const button = nested ?? Array.from(root.children).find((child) => child.tagName === 'BUTTON')
|
|
239
|
+
if (button === undefined) return null
|
|
240
|
+
const row = button.closest('[class*="logoRow"]')
|
|
241
|
+
if (row !== null && row.parentElement === root) return row
|
|
242
|
+
return button.parentElement === root ? button : null
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function placeEntry(entry: HTMLButtonElement): boolean {
|
|
246
|
+
const root = sidebarRoot()
|
|
247
|
+
if (root === null) return false
|
|
248
|
+
const anchor = newSessionRow(root)
|
|
249
|
+
if (anchor === null) return false
|
|
250
|
+
if (entry.parentElement !== root || anchor.nextElementSibling !== entry) {
|
|
251
|
+
root.insertBefore(entry, anchor.nextElementSibling)
|
|
252
|
+
}
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Wait for the sidebar, restore the row after React rebuilds, and return full
|
|
258
|
+
* cleanup. Overlapping applies synchronously adopt the existing row.
|
|
259
|
+
*/
|
|
260
|
+
export function mountSidebarEntry(
|
|
261
|
+
openCenter: CenterNavigation,
|
|
262
|
+
copy: SidebarEntryCopyPort,
|
|
263
|
+
): () => void {
|
|
264
|
+
if (typeof document === 'undefined') return () => {}
|
|
265
|
+
const candidate = document.querySelector<HTMLButtonElement>(SIDEBAR_ENTRY_SELECTOR)
|
|
266
|
+
if (candidate !== null && !isOwnedSidebarEntry(candidate)) {
|
|
267
|
+
if (!warnedForeignEntries.has(candidate)) {
|
|
268
|
+
warnedForeignEntries.add(candidate)
|
|
269
|
+
console.warn('[agent-sidecar] Sidebar entry collision: refusing to modify a foreign '
|
|
270
|
+
+ SIDEBAR_ENTRY_SELECTOR + ' node.')
|
|
271
|
+
}
|
|
272
|
+
return () => {}
|
|
273
|
+
}
|
|
274
|
+
const elements = candidate === null
|
|
275
|
+
? createEntry()
|
|
276
|
+
: hasCurrentDispatcher(candidate)
|
|
277
|
+
? existingEntry(candidate)
|
|
278
|
+
: replaceLegacyEntry(candidate)
|
|
279
|
+
if (elements === null) return () => {}
|
|
280
|
+
brandSidebarEntry(elements.button)
|
|
281
|
+
const entry = elements.button
|
|
282
|
+
const owner = {}
|
|
283
|
+
let disposed = false
|
|
284
|
+
const ensurePlaced = (): void => {
|
|
285
|
+
if (disposed) return
|
|
286
|
+
if (entry.isConnected) return
|
|
287
|
+
const existing = document.querySelector(SIDEBAR_ENTRY_SELECTOR)
|
|
288
|
+
if (existing !== null && existing !== entry) return
|
|
289
|
+
placeEntry(entry)
|
|
290
|
+
}
|
|
291
|
+
ensurePlaced()
|
|
292
|
+
const disposeBinding = bindSidebarEntryOwner(
|
|
293
|
+
elements,
|
|
294
|
+
owner,
|
|
295
|
+
openCenter,
|
|
296
|
+
copy,
|
|
297
|
+
() => {
|
|
298
|
+
if (typeof MutationObserver === 'undefined') return null
|
|
299
|
+
const observer = new MutationObserver(ensurePlaced)
|
|
300
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
301
|
+
return observer
|
|
302
|
+
},
|
|
303
|
+
)
|
|
304
|
+
return () => {
|
|
305
|
+
if (disposed) return
|
|
306
|
+
disposed = true
|
|
307
|
+
disposeBinding()
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Pill } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
2
|
+
import type { HTMLAttributes, ReactElement } from 'react'
|
|
3
|
+
|
|
4
|
+
export type StaticPillProps = HTMLAttributes<HTMLSpanElement>
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Preserve native span attributes around the rc.2 Pill static branch, which
|
|
8
|
+
* currently omits its rest props. The inner primitive remains the sole owner
|
|
9
|
+
* of the DSH pill visuals.
|
|
10
|
+
*/
|
|
11
|
+
export function StaticPill({
|
|
12
|
+
className,
|
|
13
|
+
children,
|
|
14
|
+
...rest
|
|
15
|
+
}: StaticPillProps): ReactElement {
|
|
16
|
+
return (
|
|
17
|
+
<span className={className} {...rest}>
|
|
18
|
+
<Pill>{children}</Pill>
|
|
19
|
+
</span>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
/* Agent Sidecar settings
|
|
2
|
-
*
|
|
3
|
-
* sectioned form body); every color rides a dsh --dsw-alias-* design token,
|
|
4
|
-
* no literal colors. */
|
|
1
|
+
/* Layout specific to the Agent Sidecar settings composition. Control chrome
|
|
2
|
+
* comes from dsh primitives; native select/checkbox styles stay token-backed. */
|
|
5
3
|
|
|
6
4
|
.card {
|
|
5
|
+
width: 100%;
|
|
6
|
+
max-width: 760px;
|
|
7
|
+
box-sizing: border-box;
|
|
7
8
|
list-style: none;
|
|
8
9
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
9
|
-
border-radius:
|
|
10
|
+
border-radius: var(--agsc-radius-card);
|
|
10
11
|
background: var(--dsw-alias-bg-layer-3);
|
|
11
12
|
transition: border-color .16s, background .16s;
|
|
12
13
|
}
|
|
@@ -20,8 +21,6 @@
|
|
|
20
21
|
border-color: var(--dsw-alias-label-dimmed);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
/* ── disclosure header ─────────────────────────────────────────────────── */
|
|
24
|
-
|
|
25
24
|
.header {
|
|
26
25
|
width: 100%;
|
|
27
26
|
appearance: none;
|
|
@@ -35,7 +34,7 @@
|
|
|
35
34
|
align-items: center;
|
|
36
35
|
gap: 12px;
|
|
37
36
|
padding: 14px 16px;
|
|
38
|
-
border-radius:
|
|
37
|
+
border-radius: var(--agsc-radius-card);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
.header:focus-visible {
|
|
@@ -64,37 +63,21 @@
|
|
|
64
63
|
color: var(--dsw-alias-label-tertiary);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
/* Carried on the header so a collapsed card still says it holds edits. */
|
|
68
66
|
.pending {
|
|
69
67
|
flex: none;
|
|
70
|
-
border-radius: 999px;
|
|
71
|
-
padding: 1px 8px;
|
|
72
|
-
font-size: 11px;
|
|
73
|
-
line-height: 17px;
|
|
74
|
-
font-weight: 500;
|
|
75
68
|
white-space: nowrap;
|
|
76
|
-
background: var(--dsw-alias-bg-module-platform);
|
|
77
|
-
color: var(--dsw-alias-label-secondary);
|
|
78
69
|
}
|
|
79
70
|
|
|
80
|
-
/* CSS-drawn caret (ui-primitives icons are not on this package's dependency
|
|
81
|
-
* surface, so the chevron is a rotated border square). */
|
|
82
71
|
.chevron {
|
|
83
72
|
flex: none;
|
|
84
|
-
|
|
85
|
-
height: 8px;
|
|
86
|
-
border-right: 1.5px solid var(--dsw-alias-label-tertiary);
|
|
87
|
-
border-bottom: 1.5px solid var(--dsw-alias-label-tertiary);
|
|
88
|
-
transform: rotate(45deg) translateY(-2px);
|
|
73
|
+
color: var(--dsw-alias-label-tertiary);
|
|
89
74
|
transition: transform .16s;
|
|
90
75
|
}
|
|
91
76
|
|
|
92
77
|
.chevronOpen {
|
|
93
|
-
transform: rotate(
|
|
78
|
+
transform: rotate(180deg);
|
|
94
79
|
}
|
|
95
80
|
|
|
96
|
-
/* ── body ──────────────────────────────────────────────────────────────── */
|
|
97
|
-
|
|
98
81
|
.body {
|
|
99
82
|
border-top: 1px solid var(--dsw-alias-border-l2);
|
|
100
83
|
margin: 0 16px;
|
|
@@ -124,8 +107,6 @@
|
|
|
124
107
|
color: var(--dsw-alias-label-primary);
|
|
125
108
|
}
|
|
126
109
|
|
|
127
|
-
/* ── fields ────────────────────────────────────────────────────────────── */
|
|
128
|
-
|
|
129
110
|
.field {
|
|
130
111
|
padding: 8px 0;
|
|
131
112
|
display: flex;
|
|
@@ -150,46 +131,47 @@
|
|
|
150
131
|
margin: 0;
|
|
151
132
|
font-size: 12px;
|
|
152
133
|
line-height: 1.5;
|
|
153
|
-
color: var(--dsw-alias-
|
|
134
|
+
color: var(--dsw-alias-state-error-primary);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.input {
|
|
138
|
+
width: 100%;
|
|
139
|
+
max-width: 420px;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.inputInvalid {
|
|
144
|
+
border-color: var(--dsw-alias-state-error-primary);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.input:has(input:disabled) {
|
|
148
|
+
opacity: .5;
|
|
154
149
|
}
|
|
155
150
|
|
|
156
|
-
.input,
|
|
157
151
|
.select {
|
|
158
|
-
|
|
152
|
+
width: 100%;
|
|
159
153
|
max-width: 420px;
|
|
154
|
+
height: 32px;
|
|
155
|
+
box-sizing: border-box;
|
|
160
156
|
border: 1px solid var(--dsw-alias-border-l2);
|
|
161
|
-
border-radius:
|
|
162
|
-
background: var(--dsw-alias-bg-layer-
|
|
163
|
-
padding:
|
|
157
|
+
border-radius: var(--agsc-radius-control);
|
|
158
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
159
|
+
padding: 0 8px;
|
|
164
160
|
font: inherit;
|
|
165
161
|
font-size: 13px;
|
|
166
162
|
line-height: 1.5;
|
|
167
163
|
color: var(--dsw-alias-label-primary);
|
|
168
164
|
}
|
|
169
165
|
|
|
170
|
-
.input:focus-visible,
|
|
171
166
|
.select:focus-visible {
|
|
172
167
|
outline: none;
|
|
173
168
|
border-color: var(--dsw-alias-brand-primary);
|
|
174
169
|
}
|
|
175
170
|
|
|
176
|
-
.input::placeholder {
|
|
177
|
-
color: var(--dsw-alias-label-tertiary);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.input:disabled,
|
|
181
171
|
.select:disabled {
|
|
182
172
|
opacity: 0.5;
|
|
183
173
|
cursor: default;
|
|
184
174
|
}
|
|
185
|
-
|
|
186
|
-
/* Modifier applied ALONGSIDE .input (no `composes`: the tsdown class-map
|
|
187
|
-
* emitter keeps only the local hashed name and would drop composed bases). */
|
|
188
|
-
.inputInvalid {
|
|
189
|
-
border-color: var(--dsw-alias-label-error);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/* Toggle row: label text left, native checkbox right-aligned with it. */
|
|
193
175
|
.toggleRow {
|
|
194
176
|
display: flex;
|
|
195
177
|
align-items: center;
|
|
@@ -209,8 +191,6 @@
|
|
|
209
191
|
cursor: default;
|
|
210
192
|
}
|
|
211
193
|
|
|
212
|
-
/* ── notes (safety / daemon guidance) ──────────────────────────────────── */
|
|
213
|
-
|
|
214
194
|
.note {
|
|
215
195
|
margin: 6px 0 2px;
|
|
216
196
|
border-radius: 8px;
|
|
@@ -221,8 +201,6 @@
|
|
|
221
201
|
color: var(--dsw-alias-label-secondary);
|
|
222
202
|
}
|
|
223
203
|
|
|
224
|
-
/* ── daemon status row ─────────────────────────────────────────────────── */
|
|
225
|
-
|
|
226
204
|
.statusRow {
|
|
227
205
|
display: flex;
|
|
228
206
|
align-items: center;
|
|
@@ -235,16 +213,15 @@
|
|
|
235
213
|
width: 8px;
|
|
236
214
|
height: 8px;
|
|
237
215
|
border-radius: 50%;
|
|
238
|
-
background: var(--
|
|
216
|
+
background: var(--agsc-fg-dimmed);
|
|
239
217
|
}
|
|
240
218
|
|
|
241
|
-
/* Modifiers applied alongside .statusDot (same no-`composes` rationale). */
|
|
242
219
|
.statusOk {
|
|
243
|
-
background: var(--
|
|
220
|
+
background: var(--agsc-ok);
|
|
244
221
|
}
|
|
245
222
|
|
|
246
223
|
.statusError {
|
|
247
|
-
background: var(--
|
|
224
|
+
background: var(--agsc-err);
|
|
248
225
|
}
|
|
249
226
|
|
|
250
227
|
.statusText {
|
|
@@ -260,30 +237,9 @@
|
|
|
260
237
|
}
|
|
261
238
|
|
|
262
239
|
.retry {
|
|
263
|
-
|
|
264
|
-
border: 1px solid var(--dsw-alias-border-l2);
|
|
265
|
-
border-radius: 8px;
|
|
266
|
-
background: none;
|
|
267
|
-
padding: 3px 12px;
|
|
268
|
-
font: inherit;
|
|
269
|
-
font-size: 12px;
|
|
270
|
-
line-height: 1.5;
|
|
271
|
-
color: var(--dsw-alias-label-secondary);
|
|
272
|
-
cursor: pointer;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.retry:hover:not(:disabled) {
|
|
276
|
-
color: var(--dsw-alias-label-primary);
|
|
277
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
.retry:focus-visible {
|
|
281
|
-
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
282
|
-
outline-offset: 1px;
|
|
240
|
+
margin-left: auto;
|
|
283
241
|
}
|
|
284
242
|
|
|
285
|
-
/* ── footer ────────────────────────────────────────────────────────────── */
|
|
286
|
-
|
|
287
243
|
.footer {
|
|
288
244
|
display: flex;
|
|
289
245
|
align-items: center;
|
|
@@ -311,49 +267,9 @@
|
|
|
311
267
|
text-align: right;
|
|
312
268
|
font-size: 12px;
|
|
313
269
|
line-height: 1.5;
|
|
314
|
-
color: var(--dsw-alias-
|
|
270
|
+
color: var(--dsw-alias-state-error-primary);
|
|
315
271
|
}
|
|
316
272
|
|
|
317
273
|
.spacer {
|
|
318
274
|
flex: 1;
|
|
319
275
|
}
|
|
320
|
-
|
|
321
|
-
.discard,
|
|
322
|
-
.save {
|
|
323
|
-
appearance: none;
|
|
324
|
-
border: 1px solid transparent;
|
|
325
|
-
border-radius: 8px;
|
|
326
|
-
padding: 5px 14px;
|
|
327
|
-
font: inherit;
|
|
328
|
-
font-size: 13px;
|
|
329
|
-
line-height: 1.5;
|
|
330
|
-
cursor: pointer;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
.discard {
|
|
334
|
-
border-color: var(--dsw-alias-border-l2);
|
|
335
|
-
background: none;
|
|
336
|
-
color: var(--dsw-alias-label-secondary);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.discard:hover:not(:disabled) {
|
|
340
|
-
color: var(--dsw-alias-label-primary);
|
|
341
|
-
border-color: var(--dsw-alias-label-dimmed);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
.save {
|
|
345
|
-
background: var(--dsw-alias-label-primary);
|
|
346
|
-
color: var(--dsw-alias-bg-layer-3);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.discard:disabled,
|
|
350
|
-
.save:disabled {
|
|
351
|
-
opacity: 0.4;
|
|
352
|
-
cursor: default;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
.discard:focus-visible,
|
|
356
|
-
.save:focus-visible {
|
|
357
|
-
outline: 2px solid var(--dsw-alias-brand-primary);
|
|
358
|
-
outline-offset: 1px;
|
|
359
|
-
}
|