anentrypoint-design 0.0.322 → 0.0.324
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 +22 -2
- package/app-shell.css +24 -4782
- package/colors_and_type.css +43 -0
- package/dist/247420.css +74 -3
- package/dist/247420.js +16 -16
- package/package.json +14 -8
- package/src/bootstrap.js +10 -1
- package/src/components/chat-message-parts.js +355 -0
- package/src/components/chat.js +23 -320
- package/src/components/data-density.js +15 -10
- package/src/components/files-modals.js +14 -2
- package/src/components/files.js +5 -1
- package/src/components/freddie/helpers.js +46 -8
- package/src/css/app-shell/base.css +157 -0
- package/src/css/app-shell/catalog-theme.css +144 -0
- package/src/css/app-shell/chat-basic.css +12 -0
- package/src/css/app-shell/chat-polish.css +459 -0
- package/src/css/app-shell/data-density.css +150 -0
- package/src/css/app-shell/files.css +709 -0
- package/src/css/app-shell/hero-content.css +407 -0
- package/src/css/app-shell/kits-appended.css +361 -0
- package/src/css/app-shell/loading-alerts.css +120 -0
- package/src/css/app-shell/panel-row.css +177 -0
- package/src/css/app-shell/primitives.css +197 -0
- package/src/css/app-shell/responsive.css +266 -0
- package/src/css/app-shell/responsive2-workspace.css +465 -0
- package/src/css/app-shell/row-print.css +75 -0
- package/src/css/app-shell/sidebar-misc.css +79 -0
- package/src/css/app-shell/states-interactions.css +696 -0
- package/src/css/app-shell/topbar.css +319 -0
- package/src/deck-stage.js +6 -4
- package/src/highlight.js +20 -4
- package/src/index.js +15 -5
- package/src/kits/os/freddie/pages-chat.js +1 -1
- package/src/kits/os/freddie/pages-core.js +1 -1
- package/src/kits/os/freddie/pages-os.js +1 -1
- package/src/kits/os/freddie/pages-tools.js +1 -1
- package/src/kits/os/wm.js +38 -0
- package/src/kits/slides/deck-stage-overlay.js +68 -0
- package/src/kits/slides/deck-stage-state.js +81 -0
- package/src/kits/slides/deck-stage-style.js +117 -0
- package/src/kits/slides/deck-stage.js +167 -0
- package/src/kits/slides/index.js +5 -0
- package/src/markdown.js +89 -11
- package/src/kits/os/freddie/helpers.js +0 -33
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
File surface — responsive grid + row layouts
|
|
3
|
+
============================================================ */
|
|
4
|
+
|
|
5
|
+
/* Default file grid — a vertical list of rows. Each row lays out its own
|
|
6
|
+
columns internally; the container only stacks them. Card mode (multi-column)
|
|
7
|
+
is opt-in via [data-columns]. */
|
|
8
|
+
.ds-file-grid {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 4px;
|
|
12
|
+
}
|
|
13
|
+
.ds-file-grid:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); border-radius: var(--r-2); }
|
|
14
|
+
|
|
15
|
+
/* Listing wrapper holds the optional filter + sort header above the grid. */
|
|
16
|
+
.ds-file-listing { display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; }
|
|
17
|
+
/* The quick filter is a compact control, not a full-width form row - a giant
|
|
18
|
+
input above the grid reads as a search page and costs fold height. */
|
|
19
|
+
/* The filter is now an inline item in the single .ds-file-controls toolbar row
|
|
20
|
+
(the standalone right-aligned .ds-file-filter strip was removed). */
|
|
21
|
+
.ds-file-filter-input {
|
|
22
|
+
width: min(280px, 100%); padding: 6px 12px; font-size: var(--fs-sm);
|
|
23
|
+
background: var(--bg); color: var(--fg);
|
|
24
|
+
border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
|
|
25
|
+
}
|
|
26
|
+
.ds-file-controls .ds-file-filter-wrap { flex: 0 1 220px; display: contents; }
|
|
27
|
+
.ds-file-controls .ds-file-filter-wrap .ds-file-filter-input { flex: 0 1 220px; }
|
|
28
|
+
.ds-file-filter-input:focus-visible { outline: none; box-shadow: var(--focus-ring-inset); }
|
|
29
|
+
.ds-file-sort { display: flex; gap: var(--space-2); padding: 0 var(--space-2) var(--space-1); }
|
|
30
|
+
.ds-file-sort-btn {
|
|
31
|
+
font-size: var(--fs-tiny); font-family: var(--ff-mono); color: var(--fg-3);
|
|
32
|
+
background: none; border: none; cursor: pointer; padding: 2px 4px; border-radius: var(--r-1);
|
|
33
|
+
}
|
|
34
|
+
.ds-file-sort-btn:hover { color: var(--fg-2); background: var(--bg-2); }
|
|
35
|
+
.ds-file-sort-btn.active { color: var(--accent-ink); }
|
|
36
|
+
.ds-file-sort-btn:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
37
|
+
|
|
38
|
+
/* Skeleton shimmer rows shown while a directory loads. */
|
|
39
|
+
.ds-file-row-skeleton { cursor: default; pointer-events: none; }
|
|
40
|
+
/* In-place refresh: dim the populated grid instead of swapping it for shimmer
|
|
41
|
+
rows (the rows stay; the round-trip reads as a settle, not a reload). */
|
|
42
|
+
.ds-file-grid.is-refreshing { opacity: .6; }
|
|
43
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
44
|
+
.ds-file-grid { transition: opacity .15s var(--ease); }
|
|
45
|
+
}
|
|
46
|
+
/* EventList loading skeleton (shares the .ds-skel shimmer base above). */
|
|
47
|
+
.ds-event-row-skeleton { display: flex; align-items: center; gap: var(--space-3); padding: 12px 16px; pointer-events: none; }
|
|
48
|
+
.ds-skel-rank { height: 12px; width: 2.5ch; }
|
|
49
|
+
.ds-event-state { padding: var(--space-2) 16px; }
|
|
50
|
+
.ds-skel { display: inline-block; border-radius: var(--r-1); background: var(--bg-2); position: relative; overflow: hidden; }
|
|
51
|
+
.ds-skel::after {
|
|
52
|
+
content: ''; position: absolute; inset: 0;
|
|
53
|
+
background: linear-gradient(90deg, transparent, var(--bg-3), transparent);
|
|
54
|
+
transform: translateX(-100%); animation: ds-skel-shimmer 1.2s infinite;
|
|
55
|
+
}
|
|
56
|
+
.ds-skel-icon { width: 22px; height: 22px; }
|
|
57
|
+
.ds-skel-title { height: 12px; width: 60%; }
|
|
58
|
+
.ds-skel-meta { height: 10px; width: 64px; margin-left: auto; }
|
|
59
|
+
@keyframes ds-skel-shimmer { 100% { transform: translateX(100%); } }
|
|
60
|
+
@media (prefers-reduced-motion: reduce) { .ds-skel::after { animation: none; } }
|
|
61
|
+
|
|
62
|
+
/* File row — linear layout (checkbox? · open-button[code · icon · title · meta] · actions).
|
|
63
|
+
The row is a flex strip; the open <button> is the stretching member and lays
|
|
64
|
+
out the entry's own columns. It needs a full reset (UA button chrome off)
|
|
65
|
+
because FileRow wraps the row content in a real button for native keyboard
|
|
66
|
+
semantics. */
|
|
67
|
+
.ds-file-row {
|
|
68
|
+
display: flex; align-items: center; gap: 10px;
|
|
69
|
+
padding: 11px 16px; background: var(--bg);
|
|
70
|
+
border: var(--bw-hair) solid transparent;
|
|
71
|
+
border-radius: var(--r-2); color: var(--fg);
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
transition: background var(--dur-snap) var(--ease),
|
|
74
|
+
border-color var(--dur-snap) var(--ease),
|
|
75
|
+
transform var(--dur-snap) var(--ease);
|
|
76
|
+
}
|
|
77
|
+
.ds-file-open {
|
|
78
|
+
display: flex; align-items: center; gap: 14px;
|
|
79
|
+
flex: 1 1 auto; min-width: 0;
|
|
80
|
+
background: none; border: 0; padding: 0; margin: 0;
|
|
81
|
+
color: inherit; font: inherit; text-align: left; cursor: pointer;
|
|
82
|
+
}
|
|
83
|
+
.ds-file-open[disabled] { cursor: default; }
|
|
84
|
+
.ds-file-open:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); border-radius: var(--r-1); }
|
|
85
|
+
.ds-file-row:hover {
|
|
86
|
+
background: var(--bg-2);
|
|
87
|
+
border-color: var(--rule);
|
|
88
|
+
}
|
|
89
|
+
.ds-file-row:active { transform: translateY(1px); }
|
|
90
|
+
.ds-file-row.active { background: var(--accent-tint); border-color: var(--accent); }
|
|
91
|
+
.ds-file-row[data-file-type="dir"] { border-left-width: 3px; border-left-color: var(--accent); }
|
|
92
|
+
.ds-file-row[data-file-type="image"],
|
|
93
|
+
.ds-file-row[data-file-type="video"],
|
|
94
|
+
.ds-file-row[data-file-type="audio"],
|
|
95
|
+
.ds-file-row[data-file-type="code"],
|
|
96
|
+
.ds-file-row[data-file-type="text"],
|
|
97
|
+
.ds-file-row[data-file-type="archive"],
|
|
98
|
+
.ds-file-row[data-file-type="document"],
|
|
99
|
+
.ds-file-row[data-file-type="symlink"],
|
|
100
|
+
.ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--rule); }
|
|
101
|
+
|
|
102
|
+
/* Row internals — these are the classes FileRow actually renders. */
|
|
103
|
+
.ds-file-row .title {
|
|
104
|
+
font-weight: 500; font-size: var(--fs-sm);
|
|
105
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
106
|
+
min-width: 0; flex: 1 1 auto;
|
|
107
|
+
}
|
|
108
|
+
.ds-file-row .code {
|
|
109
|
+
font-family: var(--ff-mono); font-size: var(--fs-micro);
|
|
110
|
+
color: var(--fg-3);
|
|
111
|
+
}
|
|
112
|
+
.ds-file-row .ds-file-meta {
|
|
113
|
+
font-family: var(--ff-mono); font-size: var(--fs-xs);
|
|
114
|
+
color: var(--fg-3); white-space: nowrap;
|
|
115
|
+
/* Never shrink below content: a flex sibling with white-space:nowrap and
|
|
116
|
+
the default flex-shrink:1 silently overflows its own box (no ellipsis)
|
|
117
|
+
when the row runs out of width, painting the date/size over the title
|
|
118
|
+
and action icons at mobile widths. Fixed-size the meta column instead
|
|
119
|
+
and let .title (flex:1, min-width:0, ellipsis) absorb the squeeze. */
|
|
120
|
+
flex-shrink: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* File icon — glyph chip, tinted per type. */
|
|
124
|
+
.ds-file-icon {
|
|
125
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
126
|
+
width: 26px; height: 26px; font-size: 15px; line-height: 1;
|
|
127
|
+
color: var(--fg-2);
|
|
128
|
+
}
|
|
129
|
+
.ds-file-row[data-file-type="dir"] .ds-file-icon { color: var(--accent-ink); }
|
|
130
|
+
.ds-file-row[data-file-type="image"] .ds-file-icon { color: var(--mascot); }
|
|
131
|
+
.ds-file-row[data-file-type="video"] .ds-file-icon { color: var(--purple-2); }
|
|
132
|
+
.ds-file-row[data-file-type="audio"] .ds-file-icon { color: var(--sky); }
|
|
133
|
+
.ds-file-row[data-file-type="code"] .ds-file-icon { color: var(--green-2); }
|
|
134
|
+
.ds-file-row[data-file-type="archive"] .ds-file-icon { color: var(--flame); }
|
|
135
|
+
.ds-file-row[data-file-type="document"] .ds-file-icon { color: var(--amber, var(--sun)); }
|
|
136
|
+
|
|
137
|
+
/* Row actions — a faint baseline keeps them discoverable at rest (not fully
|
|
138
|
+
hidden, which left mouse/trackpad users unable to find rename/move/delete
|
|
139
|
+
without accidentally hovering a row), full opacity on hover/focus. */
|
|
140
|
+
.ds-file-actions {
|
|
141
|
+
display: inline-flex; gap: 2px; align-items: center;
|
|
142
|
+
opacity: 0.35; transition: opacity var(--dur-base) var(--ease);
|
|
143
|
+
}
|
|
144
|
+
.ds-file-row:hover .ds-file-actions,
|
|
145
|
+
.ds-file-row:focus-within .ds-file-actions { opacity: 1; }
|
|
146
|
+
.ds-file-act {
|
|
147
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
148
|
+
width: 28px; height: 28px; padding: 0;
|
|
149
|
+
background: transparent; border: 0; border-radius: var(--r-1);
|
|
150
|
+
color: var(--fg-3); font-size: 14px; cursor: pointer;
|
|
151
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
152
|
+
}
|
|
153
|
+
.ds-file-act:hover { background: var(--bg-3); color: var(--fg); }
|
|
154
|
+
.ds-file-act-warn:hover { background: color-mix(in oklab, var(--warn) 18%, var(--bg)); color: var(--warn); }
|
|
155
|
+
|
|
156
|
+
/* Multi-select — per-row checkbox, header tri-state, bulk action strip. */
|
|
157
|
+
/* CSS-drawn multi-select checkbox: a bordered box that fills with the accent +
|
|
158
|
+
a border-drawn tick when its parent button is .is-marked or aria-checked.
|
|
159
|
+
Shared by FileRow/FileCell (.is-marked) and the file/session select-all +
|
|
160
|
+
SessionCard select (aria-checked true/mixed) since the bundle concatenates
|
|
161
|
+
all CSS. Replaces the old [x]/[ ] bracket text - no glyphs, AT keeps the
|
|
162
|
+
role=checkbox/aria-checked name+state.
|
|
163
|
+
See also .ds-check in editor-primitives.css — a separate native-input
|
|
164
|
+
checkbox family for form/settings contexts; intentionally distinct
|
|
165
|
+
rendering (native vs CSS-drawn), do not let one drift toward the other
|
|
166
|
+
by accident. */
|
|
167
|
+
.ds-check-box {
|
|
168
|
+
width: 15px; height: 15px; box-sizing: border-box; flex: 0 0 auto; position: relative;
|
|
169
|
+
border: 1.5px solid var(--fg-3); border-radius: var(--r-0);
|
|
170
|
+
transition: background var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease);
|
|
171
|
+
}
|
|
172
|
+
.is-marked > .ds-check-box,
|
|
173
|
+
[aria-checked="true"] > .ds-check-box { background: var(--fg); border-color: var(--fg); }
|
|
174
|
+
.is-marked > .ds-check-box::after,
|
|
175
|
+
[aria-checked="true"] > .ds-check-box::after {
|
|
176
|
+
content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
|
|
177
|
+
border: solid var(--bg); border-width: 0 1.5px 1.5px 0; transform: rotate(45deg);
|
|
178
|
+
}
|
|
179
|
+
[aria-checked="mixed"] > .ds-check-box { background: var(--fg); border-color: var(--fg); }
|
|
180
|
+
[aria-checked="mixed"] > .ds-check-box::after {
|
|
181
|
+
content: ""; position: absolute; left: 3px; right: 3px; top: 6px; height: 2px; background: var(--bg); border-radius: var(--r-hair);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.ds-file-check {
|
|
185
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
186
|
+
min-width: 28px; height: 28px; padding: 0 4px;
|
|
187
|
+
background: transparent; border: 0; border-radius: var(--r-1);
|
|
188
|
+
color: var(--fg-3);
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
191
|
+
}
|
|
192
|
+
.ds-file-check:hover { background: var(--bg-3); color: var(--fg); }
|
|
193
|
+
.ds-file-check.is-marked { color: var(--accent-ink); }
|
|
194
|
+
.ds-file-check:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
195
|
+
.ds-file-check[disabled] { opacity: 0.4; cursor: default; }
|
|
196
|
+
.ds-file-row.is-marked { background: var(--accent-tint); border-color: var(--accent); }
|
|
197
|
+
.ds-file-selectall {
|
|
198
|
+
display: inline-flex; align-items: center; gap: var(--space-1);
|
|
199
|
+
font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-2);
|
|
200
|
+
cursor: pointer; background: none; border: none;
|
|
201
|
+
padding: var(--space-1); border-radius: var(--r-1);
|
|
202
|
+
}
|
|
203
|
+
.ds-file-selectall:hover { background: var(--bg-2); color: var(--fg); }
|
|
204
|
+
.ds-file-selectall:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
205
|
+
.ds-file-controls { display: flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-2) var(--space-1); flex-wrap: wrap; }
|
|
206
|
+
.ds-file-controls .spread { flex: 1 1 auto; }
|
|
207
|
+
.ds-file-controls .ds-file-sort { padding: 0; }
|
|
208
|
+
.ds-bulkbar {
|
|
209
|
+
display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
|
|
210
|
+
padding: var(--space-1) var(--space-2);
|
|
211
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
|
|
212
|
+
}
|
|
213
|
+
.ds-bulkbar-count { font-size: var(--fs-sm); font-weight: 600; color: var(--fg-2); }
|
|
214
|
+
/* BulkBar is a toolbar (it even declares role=toolbar): compact rect buttons,
|
|
215
|
+
quiet destructive outline - the app arm-confirms destructive bulk actions
|
|
216
|
+
via ConfirmDialog, so the strip never needs a loud CTA. */
|
|
217
|
+
.ds-bulkbar .btn, .ds-bulkbar .btn-primary {
|
|
218
|
+
padding: 5px 12px; min-height: 32px; border-radius: var(--r-1); font-weight: 500;
|
|
219
|
+
}
|
|
220
|
+
.ds-bulkbar .btn { background: transparent; border: var(--bw-hair) solid var(--rule); color: var(--fg-2); }
|
|
221
|
+
.ds-bulkbar .btn:hover { background: var(--bg-2); color: var(--fg); }
|
|
222
|
+
.ds-bulkbar .btn-primary.danger { background: transparent; color: var(--warn); border: var(--bw-hair) solid var(--warn); }
|
|
223
|
+
.ds-bulkbar .btn-primary.danger:hover { background: color-mix(in oklab, var(--warn) 12%, transparent); color: var(--warn); }
|
|
224
|
+
@media (pointer: coarse) {
|
|
225
|
+
.ds-bulkbar .btn, .ds-bulkbar .btn-primary { min-height: 44px; }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Density picker — list / compact / thumbnails. */
|
|
229
|
+
.ds-density {
|
|
230
|
+
display: inline-flex; gap: 2px; padding: 2px;
|
|
231
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
|
|
232
|
+
}
|
|
233
|
+
.ds-density-btn {
|
|
234
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
235
|
+
width: 28px; height: 28px; color: var(--fg-2);
|
|
236
|
+
background: none; border: none; cursor: pointer;
|
|
237
|
+
padding: 0; border-radius: calc(var(--r-1) - 2px);
|
|
238
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
239
|
+
}
|
|
240
|
+
.ds-density-btn:hover { color: var(--fg); }
|
|
241
|
+
.ds-density-btn.active { background: var(--accent-tint); color: var(--fg); }
|
|
242
|
+
.ds-density-btn:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
243
|
+
|
|
244
|
+
/* Compact density — tighter rows for long listings. */
|
|
245
|
+
.ds-file-grid[data-density="compact"] { gap: 2px; }
|
|
246
|
+
.ds-file-grid[data-density="compact"] .ds-file-row { padding: 5px 12px; gap: 10px; }
|
|
247
|
+
.ds-file-grid[data-density="compact"] .ds-file-row .title { font-size: var(--fs-xs); }
|
|
248
|
+
.ds-file-grid[data-density="compact"] .ds-file-row .ds-file-meta { font-size: var(--fs-micro); }
|
|
249
|
+
.ds-file-grid[data-density="compact"] .ds-file-icon { width: 20px; height: 20px; font-size: 13px; }
|
|
250
|
+
|
|
251
|
+
/* Thumbnail density — tile grid; image entries carry a real lazy thumbnail. */
|
|
252
|
+
.ds-file-grid-thumb {
|
|
253
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
|
|
254
|
+
gap: var(--space-3); align-items: start;
|
|
255
|
+
}
|
|
256
|
+
.ds-file-cell {
|
|
257
|
+
position: relative;
|
|
258
|
+
background: var(--bg); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
|
|
259
|
+
overflow: hidden;
|
|
260
|
+
transition: border-color var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease);
|
|
261
|
+
}
|
|
262
|
+
.ds-file-cell:hover { border-color: var(--fg-3); }
|
|
263
|
+
.ds-file-cell.active { border-color: var(--accent); background: var(--accent-tint); }
|
|
264
|
+
.ds-file-cell.is-marked { border-color: var(--accent); background: var(--accent-tint); }
|
|
265
|
+
.ds-file-cell.is-locked { opacity: 0.6; }
|
|
266
|
+
.ds-file-row.is-locked { opacity: 0.6; }
|
|
267
|
+
.ds-file-row.is-restricted .title { color: var(--fg-2); }
|
|
268
|
+
/* A fourth, deliberately bordered/mono tag variant for file-permission tags
|
|
269
|
+
specifically (distinct from .ds-badge for numeric counts and .chip for
|
|
270
|
+
status/label words) - is-noaccess reuses the shared --flame tone token
|
|
271
|
+
so 'no access' reads the same red as chip.tone-error/miss elsewhere. */
|
|
272
|
+
.ds-file-perm-tag { font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--fg-3); padding: 1px 6px; border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1); white-space: nowrap; }
|
|
273
|
+
.ds-file-perm-tag.is-noaccess { color: var(--flame); border-color: var(--flame); }
|
|
274
|
+
.ds-file-cell-open {
|
|
275
|
+
display: flex; flex-direction: column; align-items: stretch; gap: var(--space-1);
|
|
276
|
+
width: 100%; padding: var(--space-1); margin: 0;
|
|
277
|
+
background: none; border: none; cursor: pointer; color: var(--fg);
|
|
278
|
+
}
|
|
279
|
+
.ds-file-cell-open[disabled] { cursor: default; }
|
|
280
|
+
.ds-file-cell-open:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; border-radius: var(--r-2); }
|
|
281
|
+
.ds-file-cell-media {
|
|
282
|
+
display: flex; align-items: center; justify-content: center;
|
|
283
|
+
width: 100%; aspect-ratio: 4 / 3;
|
|
284
|
+
background: var(--bg-2); border-radius: var(--r-1); overflow: hidden;
|
|
285
|
+
}
|
|
286
|
+
.ds-file-cell-media .ds-file-icon { width: 34px; height: 34px; font-size: 24px; }
|
|
287
|
+
.ds-file-cell-thumb { width: 100%; height: 100%; object-fit: cover; }
|
|
288
|
+
/* Per-type icon tints on thumb tiles so the grid keeps the colour-coding the
|
|
289
|
+
list rows already carry (mirrors .ds-file-row[data-file-type] .ds-file-icon). */
|
|
290
|
+
.ds-file-cell[data-file-type="dir"] .ds-file-icon { color: var(--accent-ink); }
|
|
291
|
+
.ds-file-cell[data-file-type="image"] .ds-file-icon { color: var(--mascot); }
|
|
292
|
+
.ds-file-cell[data-file-type="video"] .ds-file-icon { color: var(--purple-2); }
|
|
293
|
+
.ds-file-cell[data-file-type="audio"] .ds-file-icon { color: var(--sky); }
|
|
294
|
+
.ds-file-cell[data-file-type="code"] .ds-file-icon { color: var(--green-2); }
|
|
295
|
+
.ds-file-cell[data-file-type="archive"] .ds-file-icon { color: var(--flame); }
|
|
296
|
+
.ds-file-cell[data-file-type="document"] .ds-file-icon { color: var(--amber); }
|
|
297
|
+
.ds-file-cell[data-file-type="symlink"] .ds-file-icon { color: var(--purple); }
|
|
298
|
+
.ds-file-cell-name {
|
|
299
|
+
font-size: var(--fs-tiny); text-align: left;
|
|
300
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
301
|
+
}
|
|
302
|
+
.ds-file-cell-meta { font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--fg-3); text-align: left; }
|
|
303
|
+
.ds-file-cell-check {
|
|
304
|
+
position: absolute; top: 4px; left: 4px; z-index: 1;
|
|
305
|
+
background: color-mix(in oklab, var(--bg) 78%, transparent);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* ============================================================
|
|
309
|
+
File browser surface — stage, toolbar, breadcrumb, dropzone,
|
|
310
|
+
upload progress, empty state, modals, preview. Authored for
|
|
311
|
+
fsbrowse but lives in the shared kit under .ds-247420 scope.
|
|
312
|
+
============================================================ */
|
|
313
|
+
|
|
314
|
+
/* Stage — the scrollable content column. */
|
|
315
|
+
.ds-file-stage {
|
|
316
|
+
display: flex; flex-direction: column; gap: var(--space-3);
|
|
317
|
+
width: 100%; max-width: var(--measure-wide); margin: 0 auto;
|
|
318
|
+
padding: var(--space-2) var(--space-3) var(--space-4);
|
|
319
|
+
}
|
|
320
|
+
/* When the stage is the app-main child, the shell already supplies top
|
|
321
|
+
padding — collapse the stage's own so content isn't pushed down. */
|
|
322
|
+
.app-main > div > .ds-file-stage,
|
|
323
|
+
.app-main .ds-file-stage { padding-top: 0; }
|
|
324
|
+
|
|
325
|
+
/* Full-width files stack: the stage's space-3 vertical beat without its
|
|
326
|
+
--measure-wide cap/auto-margins, so the roots / toolbar / bulkbar / uploads / grid
|
|
327
|
+
bands keep one consistent rhythm instead of touching edge-to-edge. Carries
|
|
328
|
+
the same side/bottom padding as .ds-file-stage (top collapses the same way
|
|
329
|
+
when mounted directly under .app-main, which already supplies top padding)
|
|
330
|
+
so switching a consumer from the capped stage to this full-width stack is a
|
|
331
|
+
pure width change, not a spacing regression. */
|
|
332
|
+
.ds-files-stack {
|
|
333
|
+
display: flex; flex-direction: column; gap: var(--space-3); min-height: 0;
|
|
334
|
+
padding: 0 var(--space-3) var(--space-4);
|
|
335
|
+
}
|
|
336
|
+
.app-main > div > .ds-files-stack,
|
|
337
|
+
.app-main .ds-files-stack { padding-top: 0; }
|
|
338
|
+
|
|
339
|
+
/* Breadcrumb path */
|
|
340
|
+
.ds-crumb-path {
|
|
341
|
+
display: flex; align-items: center; flex-wrap: wrap; gap: 2px;
|
|
342
|
+
font-size: var(--fs-sm);
|
|
343
|
+
}
|
|
344
|
+
.ds-crumb-seg {
|
|
345
|
+
background: transparent; border: 0; cursor: pointer;
|
|
346
|
+
padding: 4px 8px; border-radius: var(--r-1);
|
|
347
|
+
color: var(--fg-3); font-family: inherit; font-size: var(--fs-sm);
|
|
348
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
349
|
+
/* A single long unbroken segment (hash-named dir) must never force the
|
|
350
|
+
whole path past the viewport. */
|
|
351
|
+
max-width: min(40vw, 220px);
|
|
352
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
353
|
+
}
|
|
354
|
+
.ds-crumb-seg:hover { background: var(--bg-2); color: var(--fg); }
|
|
355
|
+
.ds-crumb-seg.leaf { color: var(--fg); font-weight: 600; }
|
|
356
|
+
.ds-crumb-sep { color: var(--fg-3); opacity: 0.6; user-select: none; }
|
|
357
|
+
|
|
358
|
+
/* Toolbar */
|
|
359
|
+
.ds-file-toolbar {
|
|
360
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
361
|
+
gap: var(--space-2); flex-wrap: wrap;
|
|
362
|
+
}
|
|
363
|
+
/* Dense page header: one row, small heading, single-line muted lede. The
|
|
364
|
+
working surfaces (files, live, history, settings) lead with content, not a
|
|
365
|
+
display H1 + paragraph that costs the first 150px of every fold. */
|
|
366
|
+
.ds-page-header-dense { margin: 0 0 var(--space-3); }
|
|
367
|
+
.ds-page-header-dense-row { display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; }
|
|
368
|
+
.ds-page-header-dense-row h1 {
|
|
369
|
+
margin: 0; font-size: var(--fs-lg); line-height: 1.3; font-weight: 600;
|
|
370
|
+
letter-spacing: normal; flex: 0 0 auto;
|
|
371
|
+
}
|
|
372
|
+
.ds-page-header-dense-lede {
|
|
373
|
+
flex: 1 1 auto; min-width: 0;
|
|
374
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
375
|
+
font-size: var(--fs-sm); color: var(--fg-3);
|
|
376
|
+
}
|
|
377
|
+
.ds-page-header-dense .ds-page-header-right { flex: 0 0 auto; margin-left: auto; }
|
|
378
|
+
/* Below ~480px the single-line ellipsis truncates the majority of the lede
|
|
379
|
+
text with no way to recover it (no title attr, no expand) - let the row
|
|
380
|
+
wrap and the lede break onto its own line instead of clipping. */
|
|
381
|
+
@media (max-width: 480px) {
|
|
382
|
+
.ds-page-header-dense-row { flex-wrap: wrap; }
|
|
383
|
+
.ds-page-header-dense-lede {
|
|
384
|
+
flex-basis: 100%; overflow: visible; text-overflow: clip; white-space: normal;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
.ds-file-toolbar-left,
|
|
388
|
+
.ds-file-toolbar-right {
|
|
389
|
+
display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
|
|
390
|
+
}
|
|
391
|
+
/* Toolbar-scoped buttons: quiet, compact, rectangular - a toolbar is chrome,
|
|
392
|
+
not a CTA row; the stadium pill stays for real page-level actions. */
|
|
393
|
+
.ds-file-toolbar .btn, .ds-file-toolbar .btn-ghost {
|
|
394
|
+
padding: 5px 12px; min-height: 32px;
|
|
395
|
+
border-radius: var(--r-1);
|
|
396
|
+
font-weight: 500;
|
|
397
|
+
background: transparent;
|
|
398
|
+
border: var(--bw-hair) solid var(--rule); color: var(--fg-2);
|
|
399
|
+
}
|
|
400
|
+
.ds-file-toolbar .btn:hover, .ds-file-toolbar .btn-ghost:hover { background: var(--bg-2); color: var(--fg); }
|
|
401
|
+
@media (pointer: coarse) {
|
|
402
|
+
.ds-file-toolbar .btn, .ds-file-toolbar .btn-ghost { min-height: 44px; }
|
|
403
|
+
}
|
|
404
|
+
.ds-meta-mono {
|
|
405
|
+
font-family: var(--ff-mono); font-size: var(--fs-xs);
|
|
406
|
+
color: var(--fg-3); letter-spacing: var(--tr-caps);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Error banner — dismissible toast row. */
|
|
410
|
+
.ds-error-banner {
|
|
411
|
+
padding: 10px 14px; border-radius: var(--r-2);
|
|
412
|
+
background: color-mix(in oklab, var(--warn) 14%, var(--bg));
|
|
413
|
+
border: var(--bw-hair) solid color-mix(in oklab, var(--warn) 40%, transparent);
|
|
414
|
+
color: var(--warn); font-size: var(--fs-sm); cursor: pointer;
|
|
415
|
+
}
|
|
416
|
+
.ds-error-banner:hover { background: color-mix(in oklab, var(--warn) 20%, var(--bg)); }
|
|
417
|
+
|
|
418
|
+
/* Dropzone */
|
|
419
|
+
.ds-dropzone {
|
|
420
|
+
border: var(--bw-rule) dashed var(--rule);
|
|
421
|
+
border-radius: var(--r-3); background: var(--bg);
|
|
422
|
+
transition: border-color var(--dur-base) var(--ease), background var(--dur-base) var(--ease);
|
|
423
|
+
}
|
|
424
|
+
.ds-dropzone-inner {
|
|
425
|
+
display: flex; align-items: center; justify-content: center;
|
|
426
|
+
gap: var(--space-2); flex-wrap: wrap;
|
|
427
|
+
padding: var(--space-4) var(--space-3); text-align: center;
|
|
428
|
+
}
|
|
429
|
+
.ds-dropzone-glyph { font-size: 22px; color: var(--fg-3); }
|
|
430
|
+
.ds-dropzone-label { font-size: var(--fs-sm); color: var(--fg-3); }
|
|
431
|
+
.ds-dropzone.dragover {
|
|
432
|
+
border-color: var(--accent);
|
|
433
|
+
background: var(--accent-tint);
|
|
434
|
+
}
|
|
435
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
436
|
+
.ds-dropzone.dragover { animation: ds-dropzone-pulse 1.1s var(--ease) infinite; }
|
|
437
|
+
}
|
|
438
|
+
.ds-dropzone.dragover .ds-dropzone-glyph,
|
|
439
|
+
.ds-dropzone.dragover .ds-dropzone-label { color: var(--accent-ink); }
|
|
440
|
+
@keyframes ds-dropzone-pulse {
|
|
441
|
+
0%, 100% { border-color: var(--accent); }
|
|
442
|
+
50% { border-color: color-mix(in oklab, var(--accent) 45%, transparent); }
|
|
443
|
+
}
|
|
444
|
+
/* Wrapper form (has children): no permanent chrome - the content renders as if
|
|
445
|
+
the zone were not there, and the dashed affordance overlays it ONLY while a
|
|
446
|
+
drag is in flight. */
|
|
447
|
+
.ds-dropzone--wrap { position: relative; border: none; border-radius: 0; background: transparent; }
|
|
448
|
+
.ds-dropzone--wrap > .ds-dropzone-inner { display: none; }
|
|
449
|
+
.ds-dropzone--wrap.dragover > .ds-dropzone-inner {
|
|
450
|
+
display: flex; position: absolute; inset: 0; z-index: 5;
|
|
451
|
+
margin: 0; padding: var(--space-3);
|
|
452
|
+
align-items: center; justify-content: center;
|
|
453
|
+
background: color-mix(in srgb, var(--bg) 84%, transparent);
|
|
454
|
+
outline: var(--bw-rule) dashed var(--accent); outline-offset: -6px;
|
|
455
|
+
border-radius: var(--r-3);
|
|
456
|
+
}
|
|
457
|
+
.ds-dropzone--wrap.dragover { animation: none; }
|
|
458
|
+
/* Touch devices can never fire dragover — keep the pick-files affordance
|
|
459
|
+
visible as a compact static row so upload stays reachable in wrap mode. */
|
|
460
|
+
@media (hover: none) and (pointer: coarse) {
|
|
461
|
+
.ds-dropzone--wrap > .ds-dropzone-inner {
|
|
462
|
+
display: flex; position: static; padding: var(--space-2);
|
|
463
|
+
outline: none; background: transparent;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/* Upload progress */
|
|
468
|
+
.ds-upload-progress { display: flex; flex-direction: column; gap: 6px; }
|
|
469
|
+
.ds-upload-item {
|
|
470
|
+
display: grid; grid-template-columns: minmax(0, 1fr) 120px 44px;
|
|
471
|
+
gap: var(--space-2); align-items: center;
|
|
472
|
+
padding: 8px 12px; border-radius: var(--r-2);
|
|
473
|
+
background: var(--bg-2); font-size: var(--fs-xs);
|
|
474
|
+
}
|
|
475
|
+
.ds-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
476
|
+
.ds-upload-bar {
|
|
477
|
+
height: 6px; border-radius: var(--r-pill);
|
|
478
|
+
background: var(--bg-3); overflow: hidden;
|
|
479
|
+
}
|
|
480
|
+
.ds-upload-fill {
|
|
481
|
+
display: block; height: 100%; width: 0;
|
|
482
|
+
background: var(--fg-3); border-radius: var(--r-pill);
|
|
483
|
+
transition: width var(--dur-base) var(--ease);
|
|
484
|
+
}
|
|
485
|
+
.ds-upload-fill.is-active { background: var(--accent); }
|
|
486
|
+
/* Indeterminate: percent is unknown mid-transfer (fetch gives no chunk-level
|
|
487
|
+
progress) — a sliding marquee bar reads as "working", not stuck at 0%. */
|
|
488
|
+
.ds-upload-bar.indeterminate .ds-upload-fill {
|
|
489
|
+
width: 40%; transition: none;
|
|
490
|
+
animation: ds-upload-marquee 1.1s ease-in-out infinite;
|
|
491
|
+
}
|
|
492
|
+
@keyframes ds-upload-marquee {
|
|
493
|
+
0% { transform: translateX(-100%); }
|
|
494
|
+
100% { transform: translateX(250%); }
|
|
495
|
+
}
|
|
496
|
+
@media (prefers-reduced-motion: reduce) {
|
|
497
|
+
.ds-upload-bar.indeterminate .ds-upload-fill { animation: none; width: 100%; opacity: 0.5; }
|
|
498
|
+
}
|
|
499
|
+
.ds-upload-pct {
|
|
500
|
+
font-family: var(--ff-mono); font-size: var(--fs-micro);
|
|
501
|
+
color: var(--fg-3); text-align: right;
|
|
502
|
+
}
|
|
503
|
+
.ds-upload-item.done .ds-upload-fill { background: var(--success); }
|
|
504
|
+
.ds-upload-item.done .ds-upload-pct { color: var(--green); }
|
|
505
|
+
.ds-upload-item.error .ds-upload-fill { background: var(--warn); }
|
|
506
|
+
.ds-upload-item.error .ds-upload-pct { color: var(--warn); }
|
|
507
|
+
/* Per-row upload recovery actions (replace / dismiss) — error rows are not dead ends. */
|
|
508
|
+
.ds-upload-item { grid-template-columns: minmax(0, 1fr) minmax(60px, 120px) 44px auto; }
|
|
509
|
+
@media (max-width: 480px) {
|
|
510
|
+
.ds-upload-item { grid-template-columns: minmax(0, 1fr) 44px; }
|
|
511
|
+
.ds-upload-bar { grid-column: 1 / -1; order: 3; }
|
|
512
|
+
.ds-upload-actions { grid-column: 1 / -1; justify-content: flex-end; }
|
|
513
|
+
}
|
|
514
|
+
.ds-upload-actions { display: inline-flex; gap: var(--space-1); justify-content: flex-end; }
|
|
515
|
+
.ds-upload-act {
|
|
516
|
+
padding: 2px 10px; min-height: 24px; cursor: pointer;
|
|
517
|
+
background: var(--bg); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
|
|
518
|
+
color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-micro);
|
|
519
|
+
}
|
|
520
|
+
.ds-upload-act:hover { background: var(--bg-3); color: var(--fg); }
|
|
521
|
+
.ds-upload-act:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
522
|
+
@media (hover: none), (pointer: coarse) {
|
|
523
|
+
.ds-upload-act { min-height: 44px; min-width: 44px; }
|
|
524
|
+
/* 44px floor for file controls on ANY touch device, not only narrow ones
|
|
525
|
+
(the width-based mobile block misses wide touch tablets). */
|
|
526
|
+
.ds-file-act, .ds-file-sort-btn, .ds-crumb-seg, .ds-file-filter-input { min-height: 44px; }
|
|
527
|
+
.ds-file-act { min-width: 44px; }
|
|
528
|
+
.ds-file-check, .ds-file-selectall, .ds-density-btn { min-height: 44px; min-width: 44px; }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Empty state */
|
|
532
|
+
.ds-file-empty {
|
|
533
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
534
|
+
gap: var(--space-2); padding: var(--space-6) var(--space-3);
|
|
535
|
+
color: var(--fg-3); text-align: center;
|
|
536
|
+
}
|
|
537
|
+
.ds-file-empty-glyph { font-size: 34px; opacity: 0.55; }
|
|
538
|
+
.ds-file-empty-text { font-size: var(--fs-sm); }
|
|
539
|
+
|
|
540
|
+
/* -- Modals ------------------------------------------------- */
|
|
541
|
+
.ds-modal-backdrop {
|
|
542
|
+
position: fixed; inset: 0; z-index: var(--z-modal, 800);
|
|
543
|
+
display: flex; align-items: center; justify-content: center;
|
|
544
|
+
/* Notch/home-indicator safe: near-fullscreen modals must not slide under. */
|
|
545
|
+
padding: max(var(--space-3), env(safe-area-inset-top))
|
|
546
|
+
max(var(--space-3), env(safe-area-inset-right))
|
|
547
|
+
max(var(--space-3), env(safe-area-inset-bottom))
|
|
548
|
+
max(var(--space-3), env(safe-area-inset-left));
|
|
549
|
+
background: color-mix(in oklab, var(--ink) 55%, transparent);
|
|
550
|
+
}
|
|
551
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
552
|
+
.ds-modal-backdrop { animation: ds-modal-fade var(--dur-base) var(--ease); }
|
|
553
|
+
}
|
|
554
|
+
@keyframes ds-modal-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
555
|
+
.ds-modal {
|
|
556
|
+
display: flex; flex-direction: column;
|
|
557
|
+
width: 100%; max-width: 460px;
|
|
558
|
+
max-height: calc(100vh - 2 * var(--space-4));
|
|
559
|
+
max-height: calc(100dvh - 2 * var(--space-4));
|
|
560
|
+
background: var(--bg); color: var(--fg);
|
|
561
|
+
border: var(--bw-hair) solid var(--rule);
|
|
562
|
+
border-radius: var(--r-3); box-shadow: var(--shadow-3);
|
|
563
|
+
overflow: hidden;
|
|
564
|
+
}
|
|
565
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
566
|
+
.ds-modal { animation: ds-modal-rise var(--dur-base) var(--ease); }
|
|
567
|
+
}
|
|
568
|
+
@keyframes ds-modal-rise {
|
|
569
|
+
from { opacity: 0; transform: translateY(8px) scale(0.98); }
|
|
570
|
+
to { opacity: 1; transform: none; }
|
|
571
|
+
}
|
|
572
|
+
.ds-modal-small { max-width: 380px; }
|
|
573
|
+
.ds-modal-preview { max-width: 880px; }
|
|
574
|
+
.ds-modal-head {
|
|
575
|
+
padding: var(--space-3) var(--space-4);
|
|
576
|
+
font-weight: 600; font-size: var(--fs-body);
|
|
577
|
+
border-bottom: var(--bw-hair) solid var(--rule);
|
|
578
|
+
}
|
|
579
|
+
.ds-modal-body { padding: var(--space-4); overflow: auto; font-size: var(--fs-sm); }
|
|
580
|
+
.ds-modal-actions {
|
|
581
|
+
display: flex; justify-content: flex-end; gap: var(--space-2);
|
|
582
|
+
padding: var(--space-3) var(--space-4);
|
|
583
|
+
border-top: var(--bw-hair) solid var(--rule);
|
|
584
|
+
}
|
|
585
|
+
.ds-modal-input {
|
|
586
|
+
width: 100%; padding: 10px 12px;
|
|
587
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--rule);
|
|
588
|
+
border-radius: var(--r-2); color: var(--fg);
|
|
589
|
+
font-family: inherit; font-size: var(--fs-sm);
|
|
590
|
+
}
|
|
591
|
+
.ds-modal-input:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
592
|
+
/* iOS auto-zooms inputs under 16px on focus, breaking the modal layout. */
|
|
593
|
+
@media (hover: none), (pointer: coarse) {
|
|
594
|
+
.ds-modal-input { font-size: 16px; min-height: 44px; }
|
|
595
|
+
}
|
|
596
|
+
/* PromptDialog's optional roots-chip row (destination-path prompts with more
|
|
597
|
+
than one allowed root) - same chip idiom the cwd editor's roots row uses. */
|
|
598
|
+
.ds-prompt-roots { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
|
|
599
|
+
.ds-prompt-root-chip {
|
|
600
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--rule); color: var(--fg-2);
|
|
601
|
+
border-radius: var(--r-pill); padding: 3px 10px; font: inherit; font-size: var(--fs-tiny);
|
|
602
|
+
cursor: pointer;
|
|
603
|
+
}
|
|
604
|
+
.ds-prompt-root-chip:hover { border-color: var(--accent); color: var(--fg); }
|
|
605
|
+
.ds-prompt-root-chip:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
|
|
606
|
+
@media (pointer: coarse) { .ds-prompt-root-chip { min-height: 44px; } }
|
|
607
|
+
/* In-body modal error (role=alert): mutation failures (409/403) surface INSIDE
|
|
608
|
+
the dialog, not behind the fixed overlay. */
|
|
609
|
+
.ds-modal-error {
|
|
610
|
+
margin: var(--space-2) 0 0; padding: 8px 10px;
|
|
611
|
+
border: var(--bw-hair) solid var(--flame); border-radius: var(--r-2);
|
|
612
|
+
background: color-mix(in oklab, var(--flame) 12%, var(--bg));
|
|
613
|
+
color: var(--flame); font-size: var(--fs-xs);
|
|
614
|
+
}
|
|
615
|
+
.btn-primary.danger {
|
|
616
|
+
background: var(--warn);
|
|
617
|
+
border-color: var(--warn);
|
|
618
|
+
color: var(--on-color);
|
|
619
|
+
}
|
|
620
|
+
.btn-primary.danger:hover { filter: brightness(0.92); border-color: var(--warn); }
|
|
621
|
+
|
|
622
|
+
/* -- Preview ------------------------------------------------ */
|
|
623
|
+
.ds-preview-head {
|
|
624
|
+
display: flex; align-items: center; gap: var(--space-3);
|
|
625
|
+
padding: var(--space-3) var(--space-4);
|
|
626
|
+
border-bottom: var(--bw-hair) solid var(--rule);
|
|
627
|
+
}
|
|
628
|
+
.ds-preview-name {
|
|
629
|
+
font-weight: 600; font-size: var(--fs-body);
|
|
630
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
|
|
631
|
+
flex: 1 1 auto;
|
|
632
|
+
}
|
|
633
|
+
.ds-preview-meta {
|
|
634
|
+
font-family: var(--ff-mono); font-size: var(--fs-xs);
|
|
635
|
+
color: var(--fg-3); white-space: nowrap;
|
|
636
|
+
flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
|
|
637
|
+
}
|
|
638
|
+
.ds-preview-actions { display: inline-flex; gap: 2px; margin-left: auto; }
|
|
639
|
+
.ds-preview-body {
|
|
640
|
+
flex: 1; min-height: 0; overflow: auto;
|
|
641
|
+
padding: var(--space-3) var(--space-4);
|
|
642
|
+
display: flex; flex-direction: column;
|
|
643
|
+
}
|
|
644
|
+
.ds-preview-media {
|
|
645
|
+
max-width: 100%;
|
|
646
|
+
max-height: 70vh; max-height: 70dvh;
|
|
647
|
+
margin: 0 auto;
|
|
648
|
+
border-radius: var(--r-2); object-fit: contain;
|
|
649
|
+
}
|
|
650
|
+
.ds-preview-audio { width: 100%; }
|
|
651
|
+
.ds-preview-code,
|
|
652
|
+
.ds-preview-text {
|
|
653
|
+
margin: 0; flex: 1; min-height: 0; overflow: auto;
|
|
654
|
+
padding: var(--space-3);
|
|
655
|
+
background: var(--ink); color: var(--paper);
|
|
656
|
+
border-radius: var(--r-2);
|
|
657
|
+
font-family: var(--ff-mono); font-size: var(--fs-xs); line-height: var(--lh-base);
|
|
658
|
+
white-space: pre; tab-size: 2;
|
|
659
|
+
}
|
|
660
|
+
.ds-preview-text { white-space: pre-wrap; word-break: break-word; }
|
|
661
|
+
.ds-preview-code code, .ds-preview-text code { font: inherit; color: inherit; background: none; }
|
|
662
|
+
.ds-preview-truncated {
|
|
663
|
+
margin-top: var(--space-2); padding-top: var(--space-2);
|
|
664
|
+
border-top: var(--bw-hair) solid color-mix(in oklab, var(--paper) 20%, transparent);
|
|
665
|
+
color: var(--paper-3); font-size: var(--fs-micro);
|
|
666
|
+
}
|
|
667
|
+
.ds-preview-fallback {
|
|
668
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
669
|
+
gap: var(--space-2); padding: var(--space-6); color: var(--fg-3);
|
|
670
|
+
}
|
|
671
|
+
.ds-preview-glyph { font-size: 34px; opacity: 0.6; }
|
|
672
|
+
|
|
673
|
+
/* Thin scrollbars inside preview bodies. */
|
|
674
|
+
.ds-preview-code, .ds-preview-text, .ds-modal-body, .ds-preview-body {
|
|
675
|
+
scrollbar-width: thin;
|
|
676
|
+
}
|
|
677
|
+
.ds-preview-code::-webkit-scrollbar,
|
|
678
|
+
.ds-preview-text::-webkit-scrollbar,
|
|
679
|
+
.ds-preview-body::-webkit-scrollbar,
|
|
680
|
+
.ds-modal-body::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
681
|
+
.ds-preview-code::-webkit-scrollbar-thumb,
|
|
682
|
+
.ds-preview-text::-webkit-scrollbar-thumb {
|
|
683
|
+
background: color-mix(in oklab, var(--paper) 28%, transparent);
|
|
684
|
+
border-radius: var(--r-pill); border: 2px solid transparent; background-clip: padding-box;
|
|
685
|
+
}
|
|
686
|
+
.ds-preview-body::-webkit-scrollbar-thumb,
|
|
687
|
+
.ds-modal-body::-webkit-scrollbar-thumb {
|
|
688
|
+
background: var(--bg-3); border-radius: var(--r-pill);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* Mobile — modals go near-fullscreen, preview media shrinks. */
|
|
692
|
+
@media (max-width: 560px) {
|
|
693
|
+
.ds-modal {
|
|
694
|
+
max-width: 100%;
|
|
695
|
+
max-height: calc(100vh - 2 * var(--space-3));
|
|
696
|
+
max-height: calc(100dvh - 2 * var(--space-3));
|
|
697
|
+
}
|
|
698
|
+
.ds-modal-preview {
|
|
699
|
+
width: 100%; max-height: none;
|
|
700
|
+
height: calc(100vh - 2 * var(--space-3));
|
|
701
|
+
height: calc(100dvh - 2 * var(--space-3));
|
|
702
|
+
}
|
|
703
|
+
.ds-preview-media { max-height: 50vh; max-height: 50dvh; }
|
|
704
|
+
.ds-file-stage { padding: var(--space-3) var(--space-2); }
|
|
705
|
+
/* Preview head: let it wrap and drop the nowrap meta (duplicated in body)
|
|
706
|
+
so the close button can never be pushed past the modal edge. */
|
|
707
|
+
.ds-preview-head { flex-wrap: wrap; row-gap: 4px; }
|
|
708
|
+
.ds-preview-meta { display: none; }
|
|
709
|
+
}
|