@lijian-ui/dsh-file-manager 0.1.20

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.
Files changed (169) hide show
  1. package/LICENSE +38 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +66 -0
  4. package/README.zh.md +66 -0
  5. package/cordis.patch.yml +13 -0
  6. package/lib/assets/mermaid.min.js +3587 -0
  7. package/lib/client.js +7621 -0
  8. package/lib/client.js.map +1 -0
  9. package/lib/index.js +3207 -0
  10. package/lib/tsconfig.client.tsbuildinfo +1 -0
  11. package/lib/tsconfig.host.tsbuildinfo +1 -0
  12. package/lib/types/client/FileManagerSettingsCard.d.ts +56 -0
  13. package/lib/types/client/FileManagerSettingsCard.d.ts.map +1 -0
  14. package/lib/types/client/PluginSettingsCard.d.ts +104 -0
  15. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -0
  16. package/lib/types/client/api.d.ts +75 -0
  17. package/lib/types/client/api.d.ts.map +1 -0
  18. package/lib/types/client/chat/AttachedFilesDock.d.ts +51 -0
  19. package/lib/types/client/chat/AttachedFilesDock.d.ts.map +1 -0
  20. package/lib/types/client/chat/file-ref.d.ts +35 -0
  21. package/lib/types/client/chat/file-ref.d.ts.map +1 -0
  22. package/lib/types/client/chat/mermaid-chat.d.ts +30 -0
  23. package/lib/types/client/chat/mermaid-chat.d.ts.map +1 -0
  24. package/lib/types/client/chat/placeholder-hint.d.ts +16 -0
  25. package/lib/types/client/chat/placeholder-hint.d.ts.map +1 -0
  26. package/lib/types/client/components/ExplorerPanel.d.ts +26 -0
  27. package/lib/types/client/components/ExplorerPanel.d.ts.map +1 -0
  28. package/lib/types/client/components/FileIcon.d.ts +15 -0
  29. package/lib/types/client/components/FileIcon.d.ts.map +1 -0
  30. package/lib/types/client/components/ScmPanel.d.ts +23 -0
  31. package/lib/types/client/components/ScmPanel.d.ts.map +1 -0
  32. package/lib/types/client/components/a11y.d.ts +12 -0
  33. package/lib/types/client/components/a11y.d.ts.map +1 -0
  34. package/lib/types/client/components/icons.d.ts +41 -0
  35. package/lib/types/client/components/icons.d.ts.map +1 -0
  36. package/lib/types/client/components/overlay.d.ts +45 -0
  37. package/lib/types/client/components/overlay.d.ts.map +1 -0
  38. package/lib/types/client/drag/DragFileInlay.d.ts +31 -0
  39. package/lib/types/client/drag/DragFileInlay.d.ts.map +1 -0
  40. package/lib/types/client/drag/file-drag.d.ts +47 -0
  41. package/lib/types/client/drag/file-drag.d.ts.map +1 -0
  42. package/lib/types/client/drag.d.ts +31 -0
  43. package/lib/types/client/drag.d.ts.map +1 -0
  44. package/lib/types/client/file-source.d.ts +29 -0
  45. package/lib/types/client/file-source.d.ts.map +1 -0
  46. package/lib/types/client/fileType.d.ts +31 -0
  47. package/lib/types/client/fileType.d.ts.map +1 -0
  48. package/lib/types/client/floating.d.ts +27 -0
  49. package/lib/types/client/floating.d.ts.map +1 -0
  50. package/lib/types/client/hooks/useResizableSplit.d.ts +37 -0
  51. package/lib/types/client/hooks/useResizableSplit.d.ts.map +1 -0
  52. package/lib/types/client/hooks/useStore.d.ts +10 -0
  53. package/lib/types/client/hooks/useStore.d.ts.map +1 -0
  54. package/lib/types/client/index.d.ts +57 -0
  55. package/lib/types/client/index.d.ts.map +1 -0
  56. package/lib/types/client/layout.d.ts +104 -0
  57. package/lib/types/client/layout.d.ts.map +1 -0
  58. package/lib/types/client/locales.d.ts +125 -0
  59. package/lib/types/client/locales.d.ts.map +1 -0
  60. package/lib/types/client/maximize.d.ts +24 -0
  61. package/lib/types/client/maximize.d.ts.map +1 -0
  62. package/lib/types/client/mention.d.ts +44 -0
  63. package/lib/types/client/mention.d.ts.map +1 -0
  64. package/lib/types/client/mount.d.ts +22 -0
  65. package/lib/types/client/mount.d.ts.map +1 -0
  66. package/lib/types/client/persist.d.ts +55 -0
  67. package/lib/types/client/persist.d.ts.map +1 -0
  68. package/lib/types/client/picker/FilePickerModal.d.ts +47 -0
  69. package/lib/types/client/picker/FilePickerModal.d.ts.map +1 -0
  70. package/lib/types/client/picker/file-picker.d.ts +62 -0
  71. package/lib/types/client/picker/file-picker.d.ts.map +1 -0
  72. package/lib/types/client/preview/PreviewPanel.d.ts +17 -0
  73. package/lib/types/client/preview/PreviewPanel.d.ts.map +1 -0
  74. package/lib/types/client/preview/PreviewTabs.d.ts +30 -0
  75. package/lib/types/client/preview/PreviewTabs.d.ts.map +1 -0
  76. package/lib/types/client/preview/PreviewToolbar.d.ts +37 -0
  77. package/lib/types/client/preview/PreviewToolbar.d.ts.map +1 -0
  78. package/lib/types/client/preview/content.d.ts +43 -0
  79. package/lib/types/client/preview/content.d.ts.map +1 -0
  80. package/lib/types/client/preview/markdown.d.ts +60 -0
  81. package/lib/types/client/preview/markdown.d.ts.map +1 -0
  82. package/lib/types/client/preview/mermaid.d.ts +78 -0
  83. package/lib/types/client/preview/mermaid.d.ts.map +1 -0
  84. package/lib/types/client/preview/selection.d.ts +26 -0
  85. package/lib/types/client/preview/selection.d.ts.map +1 -0
  86. package/lib/types/client/reference.d.ts +75 -0
  87. package/lib/types/client/reference.d.ts.map +1 -0
  88. package/lib/types/client/settings-form.d.ts +206 -0
  89. package/lib/types/client/settings-form.d.ts.map +1 -0
  90. package/lib/types/client/store.d.ts +279 -0
  91. package/lib/types/client/store.d.ts.map +1 -0
  92. package/lib/types/core/types.d.ts +132 -0
  93. package/lib/types/core/types.d.ts.map +1 -0
  94. package/lib/types/host/fs-service.d.ts +96 -0
  95. package/lib/types/host/fs-service.d.ts.map +1 -0
  96. package/lib/types/host/gate.d.ts +45 -0
  97. package/lib/types/host/gate.d.ts.map +1 -0
  98. package/lib/types/host/git-runner.d.ts +80 -0
  99. package/lib/types/host/git-runner.d.ts.map +1 -0
  100. package/lib/types/host/git-service.d.ts +130 -0
  101. package/lib/types/host/git-service.d.ts.map +1 -0
  102. package/lib/types/host/loopback.d.ts +25 -0
  103. package/lib/types/host/loopback.d.ts.map +1 -0
  104. package/lib/types/host/poll-guard.d.ts +55 -0
  105. package/lib/types/host/poll-guard.d.ts.map +1 -0
  106. package/lib/types/host/routes.d.ts +54 -0
  107. package/lib/types/host/routes.d.ts.map +1 -0
  108. package/lib/types/index.d.ts +35 -0
  109. package/lib/types/index.d.ts.map +1 -0
  110. package/lib/types/mount-once.d.ts +25 -0
  111. package/lib/types/mount-once.d.ts.map +1 -0
  112. package/package.json +85 -0
  113. package/src/client/FileManagerSettingsCard.tsx +117 -0
  114. package/src/client/PluginSettingsCard.tsx +337 -0
  115. package/src/client/api.ts +141 -0
  116. package/src/client/chat/file-ref.ts +144 -0
  117. package/src/client/chat/mermaid-chat.tsx +102 -0
  118. package/src/client/chat/placeholder-hint.tsx +46 -0
  119. package/src/client/components/ExplorerPanel.tsx +534 -0
  120. package/src/client/components/FileIcon.tsx +46 -0
  121. package/src/client/components/ScmPanel.tsx +480 -0
  122. package/src/client/components/a11y.ts +20 -0
  123. package/src/client/components/icons.tsx +274 -0
  124. package/src/client/components/overlay.tsx +225 -0
  125. package/src/client/css-modules.d.ts +5 -0
  126. package/src/client/drag/DragFileInlay.tsx +89 -0
  127. package/src/client/drag/file-drag.ts +67 -0
  128. package/src/client/drag.ts +154 -0
  129. package/src/client/file-source.ts +58 -0
  130. package/src/client/fileType.ts +129 -0
  131. package/src/client/floating.ts +63 -0
  132. package/src/client/hooks/useResizableSplit.ts +91 -0
  133. package/src/client/hooks/useStore.ts +15 -0
  134. package/src/client/index.ts +389 -0
  135. package/src/client/layout.ts +546 -0
  136. package/src/client/locales.ts +247 -0
  137. package/src/client/maximize.ts +34 -0
  138. package/src/client/mount.tsx +74 -0
  139. package/src/client/persist.ts +194 -0
  140. package/src/client/picker/FilePickerModal.tsx +506 -0
  141. package/src/client/picker/file-picker.ts +86 -0
  142. package/src/client/preview/PreviewPanel.tsx +233 -0
  143. package/src/client/preview/PreviewTabs.tsx +159 -0
  144. package/src/client/preview/PreviewToolbar.tsx +160 -0
  145. package/src/client/preview/content.tsx +617 -0
  146. package/src/client/preview/markdown.ts +351 -0
  147. package/src/client/preview/mermaid.ts +274 -0
  148. package/src/client/preview/selection.ts +82 -0
  149. package/src/client/reference.ts +161 -0
  150. package/src/client/settings-card.module.css +316 -0
  151. package/src/client/settings-form.ts +451 -0
  152. package/src/client/store.ts +1290 -0
  153. package/src/client/styles/chip.module.css +38 -0
  154. package/src/client/styles/drag.module.css +30 -0
  155. package/src/client/styles/explorer.module.css +371 -0
  156. package/src/client/styles/picker.module.css +344 -0
  157. package/src/client/styles/preview.module.css +904 -0
  158. package/src/client/styles/scm.module.css +380 -0
  159. package/src/client/styles/tokens.module.css +480 -0
  160. package/src/core/types.ts +162 -0
  161. package/src/host/fs-service.ts +513 -0
  162. package/src/host/gate.ts +74 -0
  163. package/src/host/git-runner.ts +116 -0
  164. package/src/host/git-service.ts +395 -0
  165. package/src/host/loopback.ts +63 -0
  166. package/src/host/poll-guard.ts +109 -0
  167. package/src/host/routes.ts +695 -0
  168. package/src/index.ts +97 -0
  169. package/src/mount-once.ts +48 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Translate a text selection inside the preview into a file reference (path
3
+ * plus start/end line). The selection must live inside a `<pre>` of a file-
4
+ * backed preview (code / text / markdown source / diff); returns null for
5
+ * anything else so the caller can hide the floating action.
6
+ *
7
+ * The reference carries only what the agent needs: the workspace-relative
8
+ * path of the file and the 1-based line range. The selected text itself is
9
+ * not copied into the composer — the agent re-reads the file at the line
10
+ * range when the message is sent.
11
+ * @module dsh-filemgr/client/preview/selection
12
+ */
13
+
14
+ import type { PreviewTabState } from '../store.ts'
15
+
16
+ /** The file-backed reference we surface from a preview selection. */
17
+ export interface FileReferenceSelection {
18
+ path: string
19
+ startLine: number
20
+ endLine: number
21
+ }
22
+
23
+ /** Content types that map cleanly to line-based references. */
24
+ const LINE_BASED_CONTENT_TYPES: ReadonlySet<PreviewTabState['contentType']> = new Set([
25
+ 'code',
26
+ 'text',
27
+ 'markdown', // markdown source-mode selection still maps to text lines
28
+ 'diff',
29
+ ])
30
+
31
+ /** Number the 1-based line of `offset` within `text` (counting newlines before it). */
32
+ function lineOf(text: string, offset: number): number {
33
+ const upto = text.length === 0 ? '' : text.slice(0, Math.max(0, Math.min(offset, text.length)))
34
+ let line = 1
35
+ for (let i = 0; i < upto.length; i += 1) {
36
+ if (upto.charCodeAt(i) === 10) line += 1
37
+ }
38
+ return line
39
+ }
40
+
41
+ /** Return the character offset of (node, offset) measured from the start of `pre`. */
42
+ function offsetInPre(pre: HTMLPreElement, node: Node | null, offset: number): number | null {
43
+ if (node === null) return null
44
+ try {
45
+ const range = document.createRange()
46
+ range.setStart(pre, 0)
47
+ range.setEnd(node, offset)
48
+ return range.toString().length
49
+ } catch {
50
+ // Cross-boundary nodes (e.g. detached text) raise; fall through.
51
+ return null
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Build a FileReferenceSelection from a browser selection if it points to a
57
+ * supported preview's `<pre>`. Returns null otherwise (image / pdf / url /
58
+ * non-line content, or a selection that lives outside any code block).
59
+ */
60
+ export function findFileReferenceSelection(
61
+ anchorElement: Element | null,
62
+ selection: Selection,
63
+ tab: PreviewTabState | null,
64
+ ): FileReferenceSelection | null {
65
+ if (anchorElement === null) return null
66
+ if (tab === null || tab.content === null) return null
67
+ if (!LINE_BASED_CONTENT_TYPES.has(tab.contentType)) return null
68
+ const pre = anchorElement.closest('pre') as HTMLPreElement | null
69
+ if (pre === null) return null
70
+ const startInPre = offsetInPre(pre, selection.anchorNode, selection.anchorOffset)
71
+ const endInPre = offsetInPre(pre, selection.focusNode, selection.focusOffset)
72
+ if (startInPre === null || endInPre === null) return null
73
+ const start = Math.min(startInPre, endInPre)
74
+ const end = Math.max(startInPre, endInPre)
75
+ if (start === end) return null
76
+ const text = pre.textContent ?? ''
77
+ return {
78
+ path: tab.path,
79
+ startLine: lineOf(text, start),
80
+ endLine: lineOf(text, end),
81
+ }
82
+ }
@@ -0,0 +1,161 @@
1
+ import type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
2
+
3
+ /**
4
+ * Composer draft helpers for file references.
5
+ *
6
+ * References ride the shell's native U+FFFC chip pipeline
7
+ * (`slash/input-insert-reference` bail): the composer shows one chip pill in
8
+ * place of the hidden `@path:lines` token, and the model text is serialized
9
+ * on submit through the `file` reference source's codec (registered in
10
+ * file-source.ts). Without that codec the submit attempt fails with
11
+ * `slash: no serializer for reference source "file"` — the codec MUST stay
12
+ * registered while any chip is in a draft.
13
+ */
14
+
15
+ /**
16
+ * Minimal structural view of the conversation input service. Declared locally
17
+ * so this helper has no value import from the conversation package (keeps the
18
+ * client bundle purity gate happy).
19
+ */
20
+ interface DraftInput {
21
+ state: { getSnapshot(): { draft: string; draftRev: number } }
22
+ setDraft(draft: string): void
23
+ }
24
+ export interface ConversationService {
25
+ input: { for(actx: ClientContext): DraftInput }
26
+ }
27
+
28
+ /** The file-backed reference a composer chip stands for. */
29
+ export interface FileReference {
30
+ /** Workspace-relative path (what the agent will read). */
31
+ path: string
32
+ /** First line of the referenced range (1-based, inclusive). */
33
+ startLine: number
34
+ /** Last line of the referenced range (1-based, inclusive). */
35
+ endLine: number
36
+ }
37
+
38
+ /** Basename of a `/`-separated relative path ('' when empty). */
39
+ function basenameOf(rel: string): string {
40
+ const idx = rel.lastIndexOf('/')
41
+ return idx >= 0 ? rel.slice(idx + 1) : rel
42
+ }
43
+
44
+ /** Directory of a `/`-separated relative path ('' when none). */
45
+ function dirnameOf(rel: string): string {
46
+ const idx = rel.lastIndexOf('/')
47
+ return idx > 0 ? rel.slice(0, idx) : ''
48
+ }
49
+
50
+ /**
51
+ * The chip label: basename, or `basename - dir` when the basename is
52
+ * ambiguous across the workspace (matches the reference screenshot: a short
53
+ * name plus the line range, never the full path).
54
+ */
55
+ function formatRefLabel(ref: FileReference): string {
56
+ const basename = basenameOf(ref.path)
57
+ const dirname = dirnameOf(ref.path)
58
+ const name = dirname === '' ? basename : `${basename} - ${dirname}`
59
+ return ref.endLine > ref.startLine ? `${name} ${ref.startLine}-${ref.endLine}` : `${name} ${ref.startLine}`
60
+ }
61
+
62
+ /** The `@path:lines` model form (what the codec serializes to on submit). */
63
+ export function formatFileRefToken(ref: { path: string; startLine: number; endLine: number }): string {
64
+ const range = ref.endLine > ref.startLine ? `${ref.startLine}-${ref.endLine}` : `${ref.startLine}`
65
+ return `@${ref.path}:${range}`
66
+ }
67
+
68
+ /** Reference shape the conversation machine mints into a chip occurrence. */
69
+ interface ConversationReference {
70
+ source: string
71
+ ref: string
72
+ label: string
73
+ clipboardText: string
74
+ }
75
+
76
+ /** The DRAFT-REV+CAS span the conversation machine expects for chip inserts. */
77
+ export interface RefSpan {
78
+ start: number
79
+ end: number
80
+ draftRev: number
81
+ }
82
+
83
+ /** Append `text` to the session's composer draft (plain text — no chip). */
84
+ export function appendToDraft(ctx: ClientContext, sessionId: SessionId | undefined, text: string): boolean {
85
+ try {
86
+ if (sessionId === undefined) return false
87
+ const actx = ctx.sessions.scope(sessionId)
88
+ if (actx === undefined) return false
89
+ const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
90
+ if (conversation === undefined) return false
91
+ const input = conversation.input.for(actx as unknown as ClientContext)
92
+ const draft = input.state.getSnapshot().draft
93
+ input.setDraft(draft.trim() === '' ? text : `${draft} ${text}`)
94
+ return true
95
+ } catch (error) {
96
+ console.warn('[dsh-filemgr] draft insert failed:', error)
97
+ return false
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Insert a file-reference chip into the composer draft at `span`, replacing
103
+ * its range with an occurrence the machine renders as a `</> name N-M`
104
+ * pill. The model text (`@path:lines`) is serialized on submit by the `file`
105
+ * source's codec (file-source.ts) — never the clipboard text.
106
+ *
107
+ * @returns true when the bail was accepted (CAS will still reject on
108
+ * stale draftRev — caller can re-read draft after).
109
+ */
110
+ export function insertFileReference(
111
+ ctx: ClientContext,
112
+ sessionId: SessionId | undefined,
113
+ ref: FileReference,
114
+ span: RefSpan,
115
+ ): boolean {
116
+ try {
117
+ if (sessionId === undefined) return false
118
+ const actx = ctx.sessions.scope(sessionId)
119
+ if (actx === undefined) return false
120
+ const reference: ConversationReference = {
121
+ source: 'file',
122
+ ref: formatFileRefToken(ref),
123
+ label: formatRefLabel(ref),
124
+ clipboardText: formatFileRefToken(ref),
125
+ }
126
+ return (actx as unknown as {
127
+ bail(target: unknown, event: string, payload: unknown): boolean
128
+ }).bail(actx, 'slash/input-insert-reference', { reference, span })
129
+ } catch (error) {
130
+ console.warn('[dsh-filemgr] file-reference insert failed:', error)
131
+ return false
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Append a file-reference chip at the end of the current draft (the explorer
137
+ * `@` button and the preview "添加到对话" action path). Reads the latest
138
+ * draft + draftRev from the conversation shell and inserts a zero-length chip
139
+ * there, so the chip lands after whatever the user has already typed.
140
+ *
141
+ * @returns true on success.
142
+ */
143
+ export function appendFileReferenceChip(
144
+ ctx: ClientContext,
145
+ sessionId: SessionId | undefined,
146
+ ref: FileReference,
147
+ ): boolean {
148
+ if (sessionId === undefined) return false
149
+ const actx = ctx.sessions.scope(sessionId)
150
+ if (actx === undefined) return false
151
+ const conversation = (ctx as unknown as { conversation?: ConversationService }).conversation
152
+ if (conversation === undefined) return false
153
+ const shell = conversation.input.for(actx as unknown as ClientContext)
154
+ const snap = shell.state.getSnapshot()
155
+ return insertFileReference(
156
+ ctx,
157
+ sessionId,
158
+ ref,
159
+ { start: snap.draft.length, end: snap.draft.length, draftRev: snap.draftRev },
160
+ )
161
+ }
@@ -0,0 +1,316 @@
1
+ /* Generated by scripts/sync-shared.mjs from shared/client/settings/settings-card.module.css. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs". */
2
+ /* Plugin settings card chrome + staged form fields.
3
+ * Aligned with the official ui-settings-plugins PluginCard / fields CSS:
4
+ * same semantic tokens, radius, typography and states so family cards read
5
+ * as siblings of the built-in Shell / Agent loop / Web search cards. */
6
+
7
+ .card {
8
+ border: 1px solid var(--dsw-alias-border-l2);
9
+ background: var(--dsw-alias-bg-layer-3);
10
+ border-radius: 12px;
11
+ list-style: none;
12
+ transition: border-color 0.16s, background 0.16s;
13
+ }
14
+
15
+ .card:hover {
16
+ border-color: var(--dsw-alias-label-dimmed);
17
+ }
18
+
19
+ .cardOpen {
20
+ background: var(--dsw-alias-bg-layer-2);
21
+ border-color: var(--dsw-alias-label-dimmed);
22
+ }
23
+
24
+ .header {
25
+ appearance: none;
26
+ width: 100%;
27
+ font: inherit;
28
+ color: inherit;
29
+ text-align: left;
30
+ cursor: pointer;
31
+ background: transparent;
32
+ border: 0;
33
+ border-radius: 12px;
34
+ align-items: center;
35
+ gap: 12px;
36
+ padding: 14px 16px;
37
+ display: flex;
38
+ }
39
+
40
+ .header:focus-visible {
41
+ outline: 2px solid var(--dsw-alias-brand-primary);
42
+ outline-offset: -2px;
43
+ }
44
+
45
+ .headerStatic {
46
+ width: 100%;
47
+ border-radius: 12px;
48
+ align-items: center;
49
+ gap: 12px;
50
+ padding: 14px 16px;
51
+ display: flex;
52
+ }
53
+
54
+ .headText {
55
+ flex-direction: column;
56
+ flex: 1;
57
+ gap: 4px;
58
+ min-width: 0;
59
+ display: flex;
60
+ }
61
+
62
+ .name {
63
+ color: var(--dsw-alias-label-primary);
64
+ font-size: 15px;
65
+ font-weight: 600;
66
+ line-height: 1.4;
67
+ }
68
+
69
+ .description {
70
+ color: var(--dsw-alias-label-tertiary);
71
+ font-size: 13px;
72
+ line-height: 1.5;
73
+ }
74
+
75
+ .pending {
76
+ white-space: nowrap;
77
+ background: var(--dsw-alias-bg-module-platform);
78
+ color: var(--dsw-alias-label-secondary);
79
+ border-radius: 999px;
80
+ flex: none;
81
+ padding: 1px 8px;
82
+ font-size: 11px;
83
+ font-weight: 500;
84
+ line-height: 17px;
85
+ }
86
+
87
+ .chevron {
88
+ color: var(--dsw-alias-label-tertiary);
89
+ flex: none;
90
+ transition: transform 0.16s;
91
+ }
92
+
93
+ .chevronOpen {
94
+ transform: rotate(180deg);
95
+ }
96
+
97
+ .body {
98
+ border-top: 1px solid var(--dsw-alias-border-l2);
99
+ margin: 0 16px;
100
+ padding-bottom: 8px;
101
+ }
102
+
103
+ .readOnly {
104
+ color: var(--dsw-alias-label-tertiary);
105
+ margin: 12px 0 0;
106
+ font-size: 12px;
107
+ line-height: 1.5;
108
+ }
109
+
110
+ .notExposed {
111
+ color: var(--dsw-alias-state-warn-primary);
112
+ margin: 12px 0 0;
113
+ font-size: 12px;
114
+ line-height: 1.5;
115
+ }
116
+
117
+ .footer {
118
+ border-top: 1px solid var(--dsw-alias-border-l2);
119
+ justify-content: flex-end;
120
+ align-items: center;
121
+ gap: 8px;
122
+ padding: 12px 0 4px;
123
+ display: flex;
124
+ }
125
+
126
+ .failed {
127
+ min-width: 0;
128
+ color: var(--dsw-alias-label-error);
129
+ flex: 1;
130
+ margin: 0;
131
+ font-size: 12px;
132
+ line-height: 1.5;
133
+ text-overflow: ellipsis;
134
+ overflow: hidden;
135
+ white-space: nowrap;
136
+ }
137
+
138
+ .discard,
139
+ .save {
140
+ appearance: none;
141
+ font: inherit;
142
+ cursor: pointer;
143
+ border: 1px solid transparent;
144
+ border-radius: 8px;
145
+ padding: 5px 14px;
146
+ font-size: 13px;
147
+ line-height: 1.5;
148
+ }
149
+
150
+ .discard {
151
+ border-color: var(--dsw-alias-border-l2);
152
+ color: var(--dsw-alias-label-secondary);
153
+ background: transparent;
154
+ }
155
+
156
+ .discard:hover:not(:disabled) {
157
+ color: var(--dsw-alias-label-primary);
158
+ border-color: var(--dsw-alias-label-dimmed);
159
+ }
160
+
161
+ .save {
162
+ background: var(--dsw-alias-label-primary);
163
+ color: var(--dsw-alias-bg-layer-3);
164
+ }
165
+
166
+ .discard:disabled,
167
+ .save:disabled {
168
+ opacity: 0.4;
169
+ cursor: default;
170
+ }
171
+
172
+ .discard:focus-visible,
173
+ .save:focus-visible {
174
+ outline: 2px solid var(--dsw-alias-brand-primary);
175
+ outline-offset: 1px;
176
+ }
177
+
178
+ .field {
179
+ flex-direction: column;
180
+ gap: 6px;
181
+ padding: 12px 0;
182
+ display: flex;
183
+ }
184
+
185
+ .field + .field {
186
+ border-top: 1px solid var(--dsw-alias-border-l2);
187
+ }
188
+
189
+ .head {
190
+ align-items: center;
191
+ gap: 8px;
192
+ display: flex;
193
+ }
194
+
195
+ .label {
196
+ min-width: 0;
197
+ color: var(--dsw-alias-label-primary);
198
+ flex: 1;
199
+ font-size: 13px;
200
+ font-weight: 500;
201
+ line-height: 1.5;
202
+ }
203
+
204
+ .badges {
205
+ align-items: center;
206
+ gap: 8px;
207
+ display: inline-flex;
208
+ }
209
+
210
+ .badge {
211
+ white-space: nowrap;
212
+ background: var(--dsw-alias-bg-module-platform);
213
+ color: var(--dsw-alias-label-secondary);
214
+ border-radius: 999px;
215
+ padding: 1px 8px;
216
+ font-size: 11px;
217
+ font-weight: 500;
218
+ line-height: 17px;
219
+ }
220
+
221
+ .reset {
222
+ font: inherit;
223
+ color: var(--dsw-alias-label-secondary);
224
+ cursor: pointer;
225
+ background: transparent;
226
+ border: none;
227
+ padding: 0;
228
+ font-size: 12px;
229
+ line-height: 1.5;
230
+ }
231
+
232
+ .reset:hover:not(:disabled) {
233
+ color: var(--dsw-alias-label-primary);
234
+ }
235
+
236
+ .reset:disabled {
237
+ cursor: default;
238
+ }
239
+
240
+ .reset:focus-visible {
241
+ outline: 2px solid var(--dsw-alias-brand-primary);
242
+ outline-offset: 2px;
243
+ }
244
+
245
+ .reset:focus-visible {
246
+ outline: 2px solid var(--dsw-alias-brand-primary);
247
+ outline-offset: 2px;
248
+ }
249
+
250
+ .input,
251
+ .select {
252
+ border: 1px solid var(--dsw-alias-border-l2);
253
+ background: var(--dsw-alias-bg-layer-3);
254
+ height: 34px;
255
+ font: inherit;
256
+ color: var(--dsw-alias-label-primary);
257
+ border-radius: 8px;
258
+ padding: 0 12px;
259
+ font-size: 13px;
260
+ line-height: 1.5;
261
+ }
262
+
263
+ .input:focus-visible,
264
+ .select:focus-visible {
265
+ border-color: var(--dsw-alias-brand-primary);
266
+ outline: none;
267
+ }
268
+
269
+ .input:disabled,
270
+ .select:disabled {
271
+ color: var(--dsw-alias-label-tertiary);
272
+ cursor: default;
273
+ }
274
+
275
+ .inputInvalid {
276
+ border: 1px solid var(--dsw-alias-label-error);
277
+ background: var(--dsw-alias-bg-layer-3);
278
+ height: 34px;
279
+ font: inherit;
280
+ color: var(--dsw-alias-label-primary);
281
+ border-radius: 8px;
282
+ padding: 0 12px;
283
+ font-size: 13px;
284
+ line-height: 1.5;
285
+ }
286
+
287
+ .inputInvalid:focus-visible {
288
+ outline: 2px solid var(--dsw-alias-label-error);
289
+ outline-offset: 1px;
290
+ border-color: var(--dsw-alias-label-error);
291
+ }
292
+
293
+ .invalid {
294
+ color: var(--dsw-alias-label-error);
295
+ margin: 0;
296
+ font-size: 12px;
297
+ line-height: 1.5;
298
+ }
299
+
300
+ .hint {
301
+ color: var(--dsw-alias-label-tertiary);
302
+ margin: 0;
303
+ font-size: 12px;
304
+ line-height: 1.5;
305
+ }
306
+
307
+ @media (prefers-reduced-motion: reduce) {
308
+ .card,
309
+ .header,
310
+ .chevron,
311
+ .chevronOpen,
312
+ .discard,
313
+ .save {
314
+ transition: none;
315
+ }
316
+ }