agentgui 1.0.1038 → 1.0.1039
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.
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'gui-ai-tell-sweep',
|
|
3
|
+
description: 'Hunt and fix AI-generated-design tells (gradients, glassmorphism, generic shadows, emoji icons, oversaturated color, boilerplate copy) in the agentgui GUI and its design kit',
|
|
4
|
+
whenToUse: '35th agentgui sweep, explicit ask: remove tell-tale AI design elements',
|
|
5
|
+
phases: [
|
|
6
|
+
{ title: 'Hunt', detail: 'one hunter per AI-tell lens against real source in both repos' },
|
|
7
|
+
{ title: 'Verify', detail: 'adversarial verifier per finding' },
|
|
8
|
+
{ title: 'Implement', detail: 'apply confirmed fixes directly to kit/app source' },
|
|
9
|
+
],
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const KIT = '/config/workspace/design'
|
|
13
|
+
const APP = '/config/workspace/agentgui'
|
|
14
|
+
|
|
15
|
+
const COMMON = `
|
|
16
|
+
You are auditing the agentgui GUI (a Claude-Code-web-class multi-agent chat product) and its design kit for AI-GENERATED-DESIGN TELLS specifically -- the visual/copy patterns that make a product instantly readable as "an LLM built this UI" rather than a deliberate, human-designed product.
|
|
17
|
+
Repos: app at ${APP} (client: site/app/js/app.js, site/app/js/backend.js, site/app/index.html; server: lib/http-handler.js, server.js), kit at ${KIT} (src/components/*.js, CSS at root or src/styles/, scripts/build.mjs; the kit is the published anentrypoint-design library and OWNS all visual CSS -- the app only wires state/callbacks).
|
|
18
|
+
Reference bar: claude.ai/code (calm, content-first, quiet chrome, generous thread width, restrained tonal palette, no decorative flourish).
|
|
19
|
+
HARD GUARDS:
|
|
20
|
+
- The middot separator, the ellipsis char, and em/en dashes in prose are KEPT product typography -- never flag them.
|
|
21
|
+
- Status is words + CSS-drawn .status-dot-disc -- never propose decorative glyphs, never propose re-adding icon glyphs where ASCII/words already replaced them.
|
|
22
|
+
- GUI fixes land in the KIT (CSS/components); app gets wiring only; index.html gets NO new !important.
|
|
23
|
+
- 34 prior sweeps already fixed: focus-ring tokenization, contrast (light+dark), typography-rhythm, copy-tone vocabulary unification, negative-space (clean 3 runs running), interaction-density, kit-dx docs, perceived-perf. Do not re-file those; this sweep is specifically AI-slop VISUAL/COPY TELLS.
|
|
24
|
+
Read the REAL source before claiming anything; every finding needs file:line evidence. Findings must be IMPLEMENTABLE this session (no rewrites, no new dependencies).
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
const FINDINGS_SCHEMA = {
|
|
28
|
+
type: 'object',
|
|
29
|
+
required: ['findings'],
|
|
30
|
+
properties: {
|
|
31
|
+
findings: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
required: ['id', 'title', 'evidence', 'fixLocation', 'fix', 'severity'],
|
|
36
|
+
properties: {
|
|
37
|
+
id: { type: 'string', description: 'kebab-case slug' },
|
|
38
|
+
title: { type: 'string' },
|
|
39
|
+
evidence: { type: 'string', description: 'file:line + what the source shows' },
|
|
40
|
+
fixLocation: { type: 'string', enum: ['kit', 'app', 'server', 'kit+app'] },
|
|
41
|
+
fix: { type: 'string', description: 'concrete implementable change' },
|
|
42
|
+
severity: { type: 'string', enum: ['high', 'medium', 'low'] },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const VERDICT_SCHEMA = {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: ['isReal', 'reason'],
|
|
52
|
+
properties: {
|
|
53
|
+
isReal: { type: 'boolean' },
|
|
54
|
+
reason: { type: 'string' },
|
|
55
|
+
refinedFix: { type: 'string' },
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const LENSES = [
|
|
60
|
+
{
|
|
61
|
+
key: 'gradient-glow-glass',
|
|
62
|
+
prompt: `Lens: GRADIENTS / GLASSMORPHISM / GLOW. Grep the kit CSS (and any inline app styling) for linear-gradient/radial-gradient fills on buttons, cards, badges, avatars; backdrop-filter: blur (glassmorphism); box-shadow with saturated colored glows (not neutral elevation shadows); any purple-to-blue or pink-to-orange gradient combo (the textbook AI-generated-SaaS-landing-page tell). These read as templated AI output vs the flat, tonal, restrained claude.ai/code aesthetic. 3-8 findings.`,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'emoji-generic-icons',
|
|
66
|
+
prompt: `Lens: EMOJI-AS-ICON + GENERIC ICON TELLS. Grep app.js and kit component source for emoji characters used as icons/decoration (sparkle/rocket/checkmark/lightbulb/fire emoji etc -- distinct from the ALREADY-KEPT middot/ellipsis/dash typography), and for generic "AI product" iconography choices (sparkle/magic-wand icons implying "AI-powered", robot icons, gratuitous rounded-square icon badges). Also check for excessive/uniform border-radius applied to EVERYTHING (buttons, cards, inputs, avatars all sharing one large radius token in a way that reads as a template default rather than a considered scale). 3-8 findings.`,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'oversaturated-color',
|
|
70
|
+
prompt: `Lens: COLOR OVERUSE / OVERSATURATION. Audit the kit's color tokens (colors_and_type.css or equivalent) and component usage for oversaturated accent color applied too broadly (every button/link/badge/border colored, no restraint, no tonal hierarchy of neutral-vs-accent), excessive use of the brand accent as a BACKGROUND fill vs restrained use as a signal color, and any place multiple saturated hues compete in the same view (a classic tell of unreviewed AI-generated theming). Compare to claude.ai/code's mostly-neutral palette with color reserved for real signal. 3-8 findings.`,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: 'boilerplate-copy',
|
|
74
|
+
prompt: `Lens: BOILERPLATE AI-ASSISTANT COPY TONE. Sweep user-facing strings in ${APP}/site/app/js/app.js and kit component defaults for generic AI-product boilerplate phrasing: "Welcome!", "Get started!", exclamation-heavy onboarding copy, "Let's dive in", "Unleash the power of...", overly enthusiastic empty-state copy, generic placeholder text like "Type your message here..." that any AI-generated app ships by default instead of copy tailored to THIS product's actual terse, calm, professional voice (see AGENTS.md ASCII-only/no-decorative-glyph/terse-copy conventions already established). 3-8 findings with exact strings.`,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'layout-uniformity-tells',
|
|
78
|
+
prompt: `Lens: LAYOUT/COMPONENT UNIFORMITY TELLS. Audit for the "every card looks identical" AI-template tell: uniform card treatment applied indiscriminately regardless of content type/importance (same padding/shadow/radius on a stat card, a message bubble, a settings panel, with no visual hierarchy distinguishing primary content from secondary chrome), centered-everything layouts with excessive whitespace padding that reads as a generic template rather than a considered information-dense product layout, and any component that visually announces "I am a reusable card component" (heavy shadow + heavy radius + heavy padding, stacked identically everywhere) rather than fitting its specific context. 3-8 findings.`,
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
phase('Hunt')
|
|
83
|
+
const results = await pipeline(
|
|
84
|
+
LENSES,
|
|
85
|
+
l => agent(COMMON + '\n' + l.prompt + '\n\nReturn findings via the structured output schema; ids prefixed ' + l.key + '-.', { label: 'hunt:' + l.key, phase: 'Hunt', schema: FINDINGS_SCHEMA }),
|
|
86
|
+
(res, l) => {
|
|
87
|
+
if (!res || !res.findings || !res.findings.length) return []
|
|
88
|
+
return parallel(res.findings.map(f => () =>
|
|
89
|
+
agent(
|
|
90
|
+
COMMON + `\nYou are an ADVERSARIAL VERIFIER. A hunter claims:\nTITLE: ${f.title}\nEVIDENCE: ${f.evidence}\nPROPOSED FIX: ${f.fix}\nfixLocation: ${f.fixLocation}\n\nRead the cited source yourself. Refute it if: the evidence is wrong or stale, the behavior is intentional kept design (especially kept typography: middot/ellipsis/dash), it duplicates an already-fixed finding from a prior sweep, the fix is not implementable this session, or the severity is inflated. Default isReal=false when uncertain. If real, refine the fix to the most concrete minimal change.`,
|
|
91
|
+
{ label: 'verify:' + f.id, phase: 'Verify', schema: VERDICT_SCHEMA }
|
|
92
|
+
).then(v => ({ ...f, verdict: v }))
|
|
93
|
+
))
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
const confirmed = results.flat().filter(Boolean).filter(f => f.verdict && f.verdict.isReal)
|
|
97
|
+
log(`confirmed ${confirmed.length} findings`)
|
|
98
|
+
|
|
99
|
+
phase('Implement')
|
|
100
|
+
const byLocation = {}
|
|
101
|
+
for (const f of confirmed) {
|
|
102
|
+
const loc = f.fixLocation || 'kit'
|
|
103
|
+
byLocation[loc] = byLocation[loc] || []
|
|
104
|
+
byLocation[loc].push(f)
|
|
105
|
+
}
|
|
106
|
+
const implGroups = Object.entries(byLocation)
|
|
107
|
+
const implResults = await parallel(implGroups.map(([loc, items]) => () =>
|
|
108
|
+
agent(
|
|
109
|
+
`You are implementing confirmed UX findings directly in source. Repos: app at ${APP}, kit at ${KIT}. This group's fixLocation is "${loc}".\nApply EVERY fix below directly to real source files. Be surgical -- minimal diffs, no unrelated refactors, no new dependencies.\n\n` +
|
|
110
|
+
items.map(f => `- [${f.id}] (${f.severity}) ${f.title}\n evidence: ${f.evidence}\n fix: ${f.fix}`).join('\n\n') +
|
|
111
|
+
`\n\nAfter editing, report a short list of exactly which files you changed and how.`,
|
|
112
|
+
{ label: 'implement:' + loc, phase: 'Implement' }
|
|
113
|
+
)
|
|
114
|
+
))
|
|
115
|
+
|
|
116
|
+
return { confirmed: confirmed.map(f => ({ id: f.id, title: f.title, severity: f.severity, fixLocation: f.fixLocation, evidence: f.evidence, fix: f.verdict.refinedFix || f.fix })), implResults }
|
package/.gm/prd.yml
CHANGED
|
@@ -2901,3 +2901,27 @@
|
|
|
2901
2901
|
notes: 'standing rule: kit changes only reach the shipped app after node scripts/build.mjs + copy dist into site/app/vendor/anentrypoint-design + push kit'
|
|
2902
2902
|
status: completed
|
|
2903
2903
|
witness: node scripts/build.mjs in /config/workspace/design completed with 0 errors; dist/247420.css grew 454635->456041 bytes, dist/247420.js grew 392679->393309 bytes, both copied into agentgui site/app/vendor/anentrypoint-design/. Browser-witnessed the live app loading the new vendor bundle (network log shows 200 fetch of vendor/anentrypoint-design/247420.js and .css, 0 pageErrors) and a specific new rule (.ds-select min-height:32px) computing correctly in the live DOM.
|
|
2904
|
+
- id: ai-tell-gradients
|
|
2905
|
+
notes: audit design kit + app CSS for purple-blue/rainbow gradient fills, glassmorphism blur, generic drop-shadow overuse - hallmark AI-generated UI tells
|
|
2906
|
+
status: pending
|
|
2907
|
+
- id: ai-tell-emoji-icons
|
|
2908
|
+
notes: sweep for emoji-as-icon usage and generic rounded-everything corner-radius patterns that read as templated AI output
|
|
2909
|
+
status: pending
|
|
2910
|
+
- id: ai-tell-generic-copy
|
|
2911
|
+
notes: sweep copy for generic AI-assistant boilerplate phrasing (Welcome!, Get started!, excessive exclamation) vs the established terse product voice
|
|
2912
|
+
status: pending
|
|
2913
|
+
- id: ai-tell-color-overuse
|
|
2914
|
+
notes: check for oversaturated accent overuse (everything colored) vs restrained tonal hierarchy
|
|
2915
|
+
status: pending
|
|
2916
|
+
- id: wfgy-critique-pass
|
|
2917
|
+
notes: run gui-ux-craft-style multi-lens workflow against the live GUI targeting AI-design-tell removal specifically, confirm+implement findings
|
|
2918
|
+
status: pending
|
|
2919
|
+
- id: kit-rebuild-vendor
|
|
2920
|
+
notes: rebuild design kit dist and re-vendor into site/app/vendor after any kit CSS/JS changes
|
|
2921
|
+
status: pending
|
|
2922
|
+
- id: browser-witness-final
|
|
2923
|
+
notes: browser-witness all changes live, 0 console errors, before/after screenshots
|
|
2924
|
+
status: pending
|
|
2925
|
+
- id: ci-green-push
|
|
2926
|
+
notes: push kit + agentgui changes, confirm CI green both repos
|
|
2927
|
+
status: pending
|
package/package.json
CHANGED
package/site/app/js/app.js
CHANGED
|
@@ -2735,7 +2735,7 @@ function historyMain() {
|
|
|
2735
2735
|
// lone spinner collapsing the slowest pane in the product.
|
|
2736
2736
|
: h('div', { key: 'loading' }, EventList({ items: [], loading: true,
|
|
2737
2737
|
loadingText: state.eventsSlow ? 'Indexing your Claude history — the first load can take a minute…' : 'loading events…' }));
|
|
2738
|
-
return [reconnectAlert(), head, actions, Panel({ title: 'events', children: body })].filter(Boolean);
|
|
2738
|
+
return [reconnectAlert(), head, actions, Panel({ title: 'events', kind: 'wide', children: body })].filter(Boolean);
|
|
2739
2739
|
}
|
|
2740
2740
|
|
|
2741
2741
|
if (!state.expandedEvents) state.expandedEvents = new Set();
|
|
@@ -2777,7 +2777,7 @@ function historyMain() {
|
|
|
2777
2777
|
].filter(Boolean),
|
|
2778
2778
|
});
|
|
2779
2779
|
if (filteredEvents.length === 0) {
|
|
2780
|
-
return [reconnectAlert(), head, actions, Panel({ title: 'events', children: [
|
|
2780
|
+
return [reconnectAlert(), head, actions, Panel({ title: 'events', kind: 'wide', children: [
|
|
2781
2781
|
h('div', { key: 'evmeta' }, meta),
|
|
2782
2782
|
h('div', { key: 'evfp' }, filterPills),
|
|
2783
2783
|
h('div', { key: 'nofilt', class: 'lede empty-state', role: 'status' },
|
|
@@ -2808,6 +2808,7 @@ function historyMain() {
|
|
|
2808
2808
|
actions,
|
|
2809
2809
|
Panel({
|
|
2810
2810
|
title: plural(total, 'event') + (ef !== 'all' ? ' (' + ef + ' filter)' : '') + (hiddenCount > 0 ? ' (showing last ' + shown.length + '; ' + hiddenCount + ' older)' : ''),
|
|
2811
|
+
kind: 'wide',
|
|
2811
2812
|
children: [h('div', { key: 'evmeta' }, meta), h('div', { key: 'evfp' }, filterPills), eventControls, EventList({
|
|
2812
2813
|
items: shown.map((e, i) => {
|
|
2813
2814
|
// Stable key: prefer the server-assigned event index, else the
|
|
@@ -3189,6 +3190,7 @@ function settingsMain() {
|
|
|
3189
3190
|
Panel({
|
|
3190
3191
|
id: 'appearance',
|
|
3191
3192
|
title: 'appearance',
|
|
3193
|
+
kind: 'wide',
|
|
3192
3194
|
children: [
|
|
3193
3195
|
h('div', { key: 'apl', class: 't-meta agentgui-field-mb' }, 'theme - follows the OS in auto, or pick light/dark.'),
|
|
3194
3196
|
ThemeToggle({ key: 'tt' }),
|
|
@@ -230,12 +230,11 @@
|
|
|
230
230
|
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
|
|
231
231
|
|
|
232
232
|
/* Print texture — the signature surface treatment. --grain is a tiled
|
|
233
|
-
fractal-noise data-URI
|
|
234
|
-
|
|
233
|
+
fractal-noise data-URI, opt-in via the .ds-grain utility and tuned faint
|
|
234
|
+
so type stays crisp. */
|
|
235
235
|
--grain-opacity: 0.05;
|
|
236
236
|
--grain-blend: multiply;
|
|
237
237
|
--grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
|
238
|
-
--halftone-size: 6px;
|
|
239
238
|
/* Offset-ink registration — the small print-mark offset used on editorial
|
|
240
239
|
edges (panel spine, expo numbering). A literal length, not a color. */
|
|
241
240
|
--ink-offset: 3px;
|
|
@@ -745,8 +744,7 @@
|
|
|
745
744
|
align-items: center; gap: var(--space-4);
|
|
746
745
|
min-height: var(--app-topbar-h);
|
|
747
746
|
padding: 10px var(--pad-x);
|
|
748
|
-
background: color-mix(in oklab, var(--bg)
|
|
749
|
-
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
|
747
|
+
background: color-mix(in oklab, var(--bg) 97%, transparent);
|
|
750
748
|
}
|
|
751
749
|
|
|
752
750
|
/* Merged chrome: when AppShell gets both a topbar and a crumb it wraps them
|
|
@@ -759,8 +757,7 @@
|
|
|
759
757
|
display: flex; flex-wrap: wrap; align-items: center;
|
|
760
758
|
gap: var(--space-2) var(--space-4);
|
|
761
759
|
padding: 8px var(--pad-x);
|
|
762
|
-
background: color-mix(in oklab, var(--bg)
|
|
763
|
-
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
|
760
|
+
background: color-mix(in oklab, var(--bg) 97%, transparent);
|
|
764
761
|
}
|
|
765
762
|
.ds-247420 .app-chrome > .app-topbar,
|
|
766
763
|
.ds-247420 .app-chrome > .app-crumb {
|
|
@@ -1167,7 +1164,7 @@
|
|
|
1167
1164
|
border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
|
|
1168
1165
|
}
|
|
1169
1166
|
.ds-247420 .panel.panel-inline { background: var(--bg-2); padding: var(--space-3) var(--space-3); }
|
|
1170
|
-
.ds-247420 .panel.panel-wide { background: transparent; box-shadow: none; }
|
|
1167
|
+
.ds-247420 .panel.panel-wide { background: transparent; box-shadow: none; border: none; }
|
|
1171
1168
|
.ds-247420 .panel-head {
|
|
1172
1169
|
display: flex; align-items: baseline; justify-content: space-between;
|
|
1173
1170
|
/* Shares the .panel-body 16px side padding so the caps label and the body
|
|
@@ -1310,16 +1307,6 @@
|
|
|
1310
1307
|
max-width: var(--measure-wide);
|
|
1311
1308
|
position: relative; isolation: isolate;
|
|
1312
1309
|
}
|
|
1313
|
-
/* Restrained depth wash, not a marketing glow-gradient: a single very-low-
|
|
1314
|
-
opacity accent-ink radial anchored top-right, sitting under the grain/
|
|
1315
|
-
halftone texture (z-index -1, same isolation trick as .ds-hero-aside::after)
|
|
1316
|
-
so it reads as ambient light rather than a colored blob. Token-only. */
|
|
1317
|
-
.ds-247420 .ds-hero::before {
|
|
1318
|
-
content: ''; position: absolute; z-index: -1;
|
|
1319
|
-
top: -10%; right: -10%; width: 60%; height: 140%;
|
|
1320
|
-
background: radial-gradient(closest-side, color-mix(in oklab, var(--accent-ink) 10%, transparent), transparent 70%);
|
|
1321
|
-
pointer-events: none;
|
|
1322
|
-
}
|
|
1323
1310
|
.ds-247420 .ds-hero-head { grid-area: title; display: grid; gap: var(--space-3); }
|
|
1324
1311
|
.ds-247420 .ds-hero-body { grid-area: body; }
|
|
1325
1312
|
/* The right column carries its own visual weight — a spined, halftone-
|
|
@@ -1339,13 +1326,6 @@
|
|
|
1339
1326
|
width: var(--bw-chunk); background: var(--accent);
|
|
1340
1327
|
border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
|
|
1341
1328
|
}
|
|
1342
|
-
.ds-247420 .ds-hero-aside::after {
|
|
1343
|
-
content: ''; position: absolute; inset: 0; z-index: -1;
|
|
1344
|
-
pointer-events: none;
|
|
1345
|
-
background-image: radial-gradient(currentColor 1px, transparent 1.2px);
|
|
1346
|
-
background-size: var(--halftone-size) var(--halftone-size);
|
|
1347
|
-
opacity: 0.1; border-radius: inherit;
|
|
1348
|
-
}
|
|
1349
1329
|
.ds-247420 .ds-hero-stats { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
1350
1330
|
.ds-247420 .ds-hero-stat {
|
|
1351
1331
|
font-family: var(--ff-mono); font-size: var(--fs-sm);
|
|
@@ -1421,16 +1401,6 @@
|
|
|
1421
1401
|
opacity: var(--grain-opacity);
|
|
1422
1402
|
mix-blend-mode: var(--grain-blend);
|
|
1423
1403
|
}
|
|
1424
|
-
/* Halftone dot screen — a heavier print tell for accent panels/hero. */
|
|
1425
|
-
.ds-247420 .ds-halftone { position: relative; isolation: isolate; }
|
|
1426
|
-
.ds-247420 .ds-halftone::after {
|
|
1427
|
-
content: ''; position: absolute; inset: 0; z-index: -1;
|
|
1428
|
-
pointer-events: none;
|
|
1429
|
-
background-image: radial-gradient(currentColor 1px, transparent 1.2px);
|
|
1430
|
-
background-size: var(--halftone-size) var(--halftone-size);
|
|
1431
|
-
opacity: 0.12;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
1404
|
/* ============================================================
|
|
1435
1405
|
Marquee / ticker — brand kinetic device ("always open 24/7"). The track
|
|
1436
1406
|
holds two identical runs so the loop is seamless; reduced-motion freezes it
|
|
@@ -2076,15 +2046,15 @@
|
|
|
2076
2046
|
.ds-247420 .ds-file-row:active { transform: translateY(1px); }
|
|
2077
2047
|
.ds-247420 .ds-file-row.active { background: var(--accent-tint); border-color: var(--accent); }
|
|
2078
2048
|
.ds-247420 .ds-file-row[data-file-type="dir"] { border-left-width: 3px; border-left-color: var(--accent); }
|
|
2079
|
-
.ds-247420 .ds-file-row[data-file-type="image"]
|
|
2080
|
-
.ds-247420 .ds-file-row[data-file-type="video"]
|
|
2081
|
-
.ds-247420 .ds-file-row[data-file-type="audio"]
|
|
2082
|
-
.ds-247420 .ds-file-row[data-file-type="code"]
|
|
2083
|
-
.ds-247420 .ds-file-row[data-file-type="text"]
|
|
2084
|
-
.ds-247420 .ds-file-row[data-file-type="archive"]
|
|
2085
|
-
.ds-247420 .ds-file-row[data-file-type="document"]
|
|
2086
|
-
.ds-247420 .ds-file-row[data-file-type="symlink"]
|
|
2087
|
-
.ds-247420 .ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--
|
|
2049
|
+
.ds-247420 .ds-file-row[data-file-type="image"],
|
|
2050
|
+
.ds-247420 .ds-file-row[data-file-type="video"],
|
|
2051
|
+
.ds-247420 .ds-file-row[data-file-type="audio"],
|
|
2052
|
+
.ds-247420 .ds-file-row[data-file-type="code"],
|
|
2053
|
+
.ds-247420 .ds-file-row[data-file-type="text"],
|
|
2054
|
+
.ds-247420 .ds-file-row[data-file-type="archive"],
|
|
2055
|
+
.ds-247420 .ds-file-row[data-file-type="document"],
|
|
2056
|
+
.ds-247420 .ds-file-row[data-file-type="symlink"],
|
|
2057
|
+
.ds-247420 .ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--rule); }
|
|
2088
2058
|
|
|
2089
2059
|
/* Row internals — these are the classes FileRow actually renders. */
|
|
2090
2060
|
.ds-247420 .ds-file-row .title {
|
|
@@ -2299,7 +2269,7 @@
|
|
|
2299
2269
|
/* Stage — the scrollable content column. */
|
|
2300
2270
|
.ds-247420 .ds-file-stage {
|
|
2301
2271
|
display: flex; flex-direction: column; gap: var(--space-3);
|
|
2302
|
-
width: 100%; max-width:
|
|
2272
|
+
width: 100%; max-width: var(--measure-wide); margin: 0 auto;
|
|
2303
2273
|
padding: var(--space-2) var(--space-3) var(--space-4);
|
|
2304
2274
|
}
|
|
2305
2275
|
/* When the stage is the app-main child, the shell already supplies top
|
|
@@ -2308,7 +2278,7 @@
|
|
|
2308
2278
|
.ds-247420 .app-main .ds-file-stage { padding-top: 0; }
|
|
2309
2279
|
|
|
2310
2280
|
/* Full-width files stack: the stage's space-3 vertical beat without its
|
|
2311
|
-
|
|
2281
|
+
--measure-wide cap/auto-margins, so the roots / toolbar / bulkbar / uploads / grid
|
|
2312
2282
|
bands keep one consistent rhythm instead of touching edge-to-edge. Carries
|
|
2313
2283
|
the same side/bottom padding as .ds-file-stage (top collapses the same way
|
|
2314
2284
|
when mounted directly under .app-main, which already supplies top padding)
|
|
@@ -2531,7 +2501,6 @@
|
|
|
2531
2501
|
max(var(--space-3), env(safe-area-inset-bottom))
|
|
2532
2502
|
max(var(--space-3), env(safe-area-inset-left));
|
|
2533
2503
|
background: color-mix(in oklab, var(--ink) 55%, transparent);
|
|
2534
|
-
backdrop-filter: blur(3px);
|
|
2535
2504
|
}
|
|
2536
2505
|
@media (prefers-reduced-motion: no-preference) {
|
|
2537
2506
|
.ds-247420 .ds-modal-backdrop { animation: ds-modal-fade var(--dur-base) var(--ease); }
|
|
@@ -2993,7 +2962,7 @@
|
|
|
2993
2962
|
}
|
|
2994
2963
|
.ds-247420 .chat-msg:hover .chat-image,
|
|
2995
2964
|
.ds-247420 .chat-image:hover {
|
|
2996
|
-
border-color:
|
|
2965
|
+
border-color: var(--rule);
|
|
2997
2966
|
}
|
|
2998
2967
|
.ds-247420 .chat-image img { display: block; width: 100%; height: auto; max-height: 360px; object-fit: cover; }
|
|
2999
2968
|
.ds-247420 .chat-image .cap,
|
|
@@ -3008,12 +2977,12 @@
|
|
|
3008
2977
|
max-width: min(100%, 360px);
|
|
3009
2978
|
transition: background var(--dur-snap, .15s) var(--ease, ease), border-color var(--dur-snap, .15s) var(--ease, ease);
|
|
3010
2979
|
}
|
|
3011
|
-
.ds-247420 .chat-file:hover { background: var(--bg-3);
|
|
2980
|
+
.ds-247420 .chat-file:hover { background: var(--bg-3); }
|
|
3012
2981
|
.ds-247420 .chat-file .glyph {
|
|
3013
2982
|
font-size: 22px; width: 36px; height: 36px;
|
|
3014
2983
|
display: inline-flex; align-items: center; justify-content: center;
|
|
3015
|
-
background:
|
|
3016
|
-
color: var(--
|
|
2984
|
+
background: var(--bg-3);
|
|
2985
|
+
color: var(--fg-2); border-radius: var(--r-0); flex-shrink: 0; font-family: var(--ff-mono);
|
|
3017
2986
|
}
|
|
3018
2987
|
.ds-247420 .chat-file > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
3019
2988
|
.ds-247420 .chat-file .name {
|
|
@@ -3034,7 +3003,7 @@
|
|
|
3034
3003
|
border-radius: var(--r-1); overflow: hidden; max-width: min(100%, 480px);
|
|
3035
3004
|
transition: box-shadow 0.12s ease, border-color 0.12s ease;
|
|
3036
3005
|
}
|
|
3037
|
-
.ds-247420 .chat-msg:hover .chat-pdf { border-color:
|
|
3006
|
+
.ds-247420 .chat-msg:hover .chat-pdf { border-color: var(--rule); }
|
|
3038
3007
|
.ds-247420 .chat-pdf-head {
|
|
3039
3008
|
display: flex; align-items: center; gap: 10px; padding: 10px 14px;
|
|
3040
3009
|
background: color-mix(in oklab, var(--fg) 5%, var(--bg-2));
|
|
@@ -3061,7 +3030,6 @@
|
|
|
3061
3030
|
.ds-247420 .chat-msg:hover .chat-link,
|
|
3062
3031
|
.ds-247420 .chat-link:hover {
|
|
3063
3032
|
background: var(--bg-3);
|
|
3064
|
-
border-color: color-mix(in oklab, var(--accent) 40%, transparent);
|
|
3065
3033
|
}
|
|
3066
3034
|
.ds-247420 .chat-link:not(:has(.thumb)):not(:has(img)) { grid-template-columns: 1fr; }
|
|
3067
3035
|
.ds-247420 .chat-link .thumb,
|
|
@@ -3094,7 +3062,7 @@
|
|
|
3094
3062
|
transition: background var(--dur-snap, .12s) var(--ease, ease), border-color var(--dur-snap, .12s) var(--ease, ease), transform 0.12s ease;
|
|
3095
3063
|
position: relative;
|
|
3096
3064
|
}
|
|
3097
|
-
.ds-247420 .chat-reactions .rxn:hover { background: var(--bg-3);
|
|
3065
|
+
.ds-247420 .chat-reactions .rxn:hover { background: var(--bg-3); transform: scale(1.06); }
|
|
3098
3066
|
.ds-247420 .chat-reactions .rxn.you {
|
|
3099
3067
|
background: color-mix(in oklab, var(--accent) 18%, var(--bg-2));
|
|
3100
3068
|
border-color: color-mix(in oklab, var(--accent) 55%, transparent);
|
|
@@ -7232,6 +7200,17 @@
|
|
|
7232
7200
|
.ds-247420 .ds-context .row .meta { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-2); }
|
|
7233
7201
|
.ds-247420 .ds-context .row .sub { font-size: var(--fs-xs); color: var(--fg-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
7234
7202
|
.ds-247420 .ds-context-actions { display: flex; gap: var(--space-2); }
|
|
7203
|
+
/* Lighter fact groups for secondary context blocks (conversation/last turn/
|
|
7204
|
+
recent files): a small caps label + Row list with no card chrome, so a
|
|
7205
|
+
2-row fact doesn't compete visually with the primary bordered panel above
|
|
7206
|
+
it. Separated from neighbors by a hairline rule, not a border box. */
|
|
7207
|
+
.ds-247420 .ds-context-group { padding-top: var(--space-3); border-top: var(--bw-hair) solid var(--rule); }
|
|
7208
|
+
.ds-247420 .ds-context-group-label {
|
|
7209
|
+
font-size: var(--fs-tiny); font-weight: 600; color: var(--fg-3);
|
|
7210
|
+
letter-spacing: var(--tr-caps); text-transform: uppercase;
|
|
7211
|
+
padding: 0 0 var(--space-2);
|
|
7212
|
+
}
|
|
7213
|
+
.ds-247420 .ds-context-group .row + .row { margin-top: 2px; }
|
|
7235
7214
|
|
|
7236
7215
|
/* ============================================================================
|
|
7237
7216
|
Cohesion sweep (gui-cohesion.js): one-product polish across surfaces.
|