@quilltap/theme-storybook 1.0.41 → 1.0.44

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.
@@ -15,11 +15,12 @@ import {
15
15
  Loading,
16
16
  Participant,
17
17
  Spacing,
18
+ Surfaces,
18
19
  Tabs,
19
20
  Terminal,
20
21
  ThemeComparison,
21
22
  Typography
22
- } from "../chunk-U2PTQQL5.mjs";
23
+ } from "../chunk-77W5TNBS.mjs";
23
24
  import "../chunk-WUKYLWAZ.mjs";
24
25
  export {
25
26
  Avatars,
@@ -38,6 +39,7 @@ export {
38
39
  Loading,
39
40
  Participant,
40
41
  Spacing,
42
+ Surfaces,
41
43
  Tabs,
42
44
  Terminal,
43
45
  ThemeComparison,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quilltap/theme-storybook",
3
- "version": "1.0.41",
3
+ "version": "1.0.44",
4
4
  "description": "Storybook preset and stories for developing Quilltap theme plugins",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -287,6 +287,17 @@
287
287
  --qt-rp-button-ooc-bg: hsl(152 60% 28%);
288
288
  --qt-rp-button-ooc-fg: hsl(152 60% 80%);
289
289
 
290
+ /* Roleplay-template palette (qt-roleplay-1..4): four distinct high-contrast
291
+ chips a delimiter can opt into. Themes override these four pairs to taste. */
292
+ --qt-roleplay-1-bg: hsl(217 91% 53%);
293
+ --qt-roleplay-1-fg: hsl(0 0% 100%);
294
+ --qt-roleplay-2-bg: hsl(270 65% 55%);
295
+ --qt-roleplay-2-fg: hsl(0 0% 100%);
296
+ --qt-roleplay-3-bg: hsl(38 92% 50%);
297
+ --qt-roleplay-3-fg: hsl(30 45% 12%);
298
+ --qt-roleplay-4-bg: hsl(168 78% 36%);
299
+ --qt-roleplay-4-fg: hsl(0 0% 100%);
300
+
290
301
  /* Queue badge variables */
291
302
  --qt-queue-badge-radius: var(--radius-sm);
292
303
  --qt-queue-badge-padding-x: 0.375rem;
@@ -1546,6 +1557,26 @@
1546
1557
  color: var(--color-accent-foreground);
1547
1558
  }
1548
1559
 
1560
+ /* Hoverable accent surface — list rows, menu items, and option cards that
1561
+ should highlight on hover. On hover it paints the accent background AND
1562
+ forces the accent foreground onto the row and every descendant, so the whole
1563
+ row stays legible even on themes where --color-accent is a bold colour (e.g.
1564
+ Madman's Box amber). Without the descendant flip, children carrying their own
1565
+ colour (qt-text-secondary, qt-text-primary, …) would wash out — or, when their
1566
+ colour matches the accent, vanish entirely. The descendant selector is wrapped
1567
+ in :where() so it adds no specificity to the resting state (children own their
1568
+ colour as usual); the :hover rules still win via the hover pseudo-class. */
1569
+ .qt-hover-accent {
1570
+ transition: background-color 0.15s ease, color 0.15s ease;
1571
+ }
1572
+ .qt-hover-accent:hover {
1573
+ background-color: var(--color-accent);
1574
+ color: var(--color-accent-foreground);
1575
+ }
1576
+ .qt-hover-accent:hover :where(*) {
1577
+ color: var(--color-accent-foreground);
1578
+ }
1579
+
1549
1580
  /* ==========================================================================
1550
1581
  STATUS COLOR UTILITIES (with opacity variants)
1551
1582
  ========================================================================== */
@@ -2526,3 +2557,44 @@
2526
2557
  background-color: var(--qt-terminal-closed-badge-bg);
2527
2558
  color: var(--qt-terminal-closed-badge-fg);
2528
2559
  }
2560
+
2561
+ /* ==========================================================================
2562
+ ROLEPLAY-TEMPLATE STYLES
2563
+ The chips + add-on flourishes a roleplay-template delimiter can opt into. In
2564
+ the app these are scoped under .qt-chat-message-content to override prose; the
2565
+ stylebook has no prose layer, so they are declared plainly here for preview.
2566
+ ========================================================================== */
2567
+ .qt-roleplay-1, .qt-roleplay-2, .qt-roleplay-3, .qt-roleplay-4,
2568
+ .qt-roleplay-danger, .qt-roleplay-warning, .qt-roleplay-success,
2569
+ .qt-roleplay-info, .qt-roleplay-muted, .qt-roleplay-code {
2570
+ padding: 0.125rem 0.375rem;
2571
+ border-radius: 0.1875rem;
2572
+ box-decoration-break: clone;
2573
+ -webkit-box-decoration-break: clone;
2574
+ }
2575
+
2576
+ .qt-roleplay-1 { --qt-rp-fg: var(--qt-roleplay-1-fg); --qt-rp-bg: var(--qt-roleplay-1-bg); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2577
+ .qt-roleplay-2 { --qt-rp-fg: var(--qt-roleplay-2-fg); --qt-rp-bg: var(--qt-roleplay-2-bg); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2578
+ .qt-roleplay-3 { --qt-rp-fg: var(--qt-roleplay-3-fg); --qt-rp-bg: var(--qt-roleplay-3-bg); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2579
+ .qt-roleplay-4 { --qt-rp-fg: var(--qt-roleplay-4-fg); --qt-rp-bg: var(--qt-roleplay-4-bg); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2580
+
2581
+ .qt-roleplay-danger { --qt-rp-fg: var(--color-destructive-foreground); --qt-rp-bg: var(--color-destructive); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2582
+ .qt-roleplay-warning { --qt-rp-fg: var(--color-warning-foreground); --qt-rp-bg: var(--color-warning); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2583
+ .qt-roleplay-success { --qt-rp-fg: var(--color-success-foreground); --qt-rp-bg: var(--color-success); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2584
+ .qt-roleplay-info { --qt-rp-fg: var(--color-info-foreground); --qt-rp-bg: var(--color-info); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2585
+ .qt-roleplay-muted { --qt-rp-fg: var(--color-muted-foreground); --qt-rp-bg: var(--color-muted); color: var(--qt-rp-fg); background: var(--qt-rp-bg); }
2586
+ .qt-roleplay-code { --qt-rp-fg: var(--color-foreground); --qt-rp-bg: var(--color-muted); color: var(--qt-rp-fg); background: var(--qt-rp-bg); font-family: var(--font-mono); }
2587
+
2588
+ /* Add-on flourishes (composed on top of any base style). */
2589
+ .qt-rp-bold { font-weight: 700; }
2590
+ .qt-rp-italic { font-style: italic; }
2591
+ .qt-rp-underline { text-decoration: underline; }
2592
+ .qt-rp-underline-double { text-decoration: underline; text-decoration-style: double; }
2593
+ .qt-rp-border { border: 1px solid currentColor; padding: 0.0625rem 0.25rem; border-radius: 0.1875rem; }
2594
+ .qt-rp-border-dashed { border: 1px dashed currentColor; padding: 0.0625rem 0.25rem; border-radius: 0.1875rem; }
2595
+ .qt-rp-reverse { color: var(--qt-rp-bg, var(--color-background)); background-color: var(--qt-rp-fg, var(--color-foreground)); }
2596
+ .qt-rp-font-sans { font-family: var(--theme-font-sans, ui-sans-serif, system-ui, sans-serif); }
2597
+ .qt-rp-font-serif { font-family: var(--theme-font-serif, ui-serif, Georgia, "Times New Roman", serif); }
2598
+ .qt-rp-font-mono { font-family: var(--theme-font-mono, ui-monospace, "SFMono-Regular", Menlo, monospace); }
2599
+ .qt-rp-font-display { font-family: var(--qt-rp-font-display, var(--theme-font-serif, Georgia, "Times New Roman", serif)); }
2600
+ .qt-rp-font-script { font-family: var(--qt-rp-font-script, "Segoe Script", "Brush Script MT", cursive); }
@@ -193,6 +193,40 @@ export const Chat: React.FC = () => {
193
193
  </div>
194
194
  </section>
195
195
 
196
+ {/* Roleplay-Template Styles */}
197
+ <section style={{ marginBottom: '2rem' }}>
198
+ <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
199
+ Roleplay-Template Styles
200
+ </h3>
201
+ <p style={{ color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
202
+ Style classes a roleplay-template delimiter can be assigned: four high-contrast chips (each theme picks its own four hues) plus a semantic set. Add-on flourishes (bold, italic, reverse, underline, border, font) compose on top.
203
+ </p>
204
+ <div className="qt-chat-message-content" style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: '36rem' }}>
205
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center' }}>
206
+ <span className="qt-roleplay-1">Style 1</span>
207
+ <span className="qt-roleplay-2">Style 2</span>
208
+ <span className="qt-roleplay-3">Style 3</span>
209
+ <span className="qt-roleplay-4">Style 4</span>
210
+ </div>
211
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center' }}>
212
+ <span className="qt-roleplay-danger">Danger</span>
213
+ <span className="qt-roleplay-warning">Warning</span>
214
+ <span className="qt-roleplay-success">Success</span>
215
+ <span className="qt-roleplay-info">Info</span>
216
+ <span className="qt-roleplay-muted">Muted</span>
217
+ <span className="qt-roleplay-code">code</span>
218
+ </div>
219
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center' }}>
220
+ <span className="qt-roleplay-2 qt-rp-bold">Bold</span>
221
+ <span className="qt-roleplay-3 qt-rp-italic">Italic</span>
222
+ <span className="qt-roleplay-1 qt-rp-reverse">Reverse</span>
223
+ <span className="qt-roleplay-4 qt-rp-underline-double">Underline</span>
224
+ <span className="qt-roleplay-info qt-rp-border-dashed">Dashed border</span>
225
+ <span className="qt-roleplay-muted qt-rp-font-serif">Serif font</span>
226
+ </div>
227
+ </div>
228
+ </section>
229
+
196
230
  {/* Whisper Messages */}
197
231
  <section style={{ marginBottom: '2rem' }}>
198
232
  <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
@@ -23,7 +23,7 @@ const ICON_GROUPS: IconGroup[] = [
23
23
  category: 'General UI',
24
24
  names: [
25
25
  'close', 'pencil', 'refresh', 'check', 'check-circle', 'chat', 'info', 'trash',
26
- 'copy', 'plus', 'search', 'download', 'upload', 'cloud-upload', 'external-link',
26
+ 'copy', 'plus', 'minus', 'search', 'download', 'upload', 'cloud-upload', 'external-link',
27
27
  'link', 'send', 'paperclip', 'eye', 'eye-off', 'star', 'bookmark', 'tag', 'expand', 'compress',
28
28
  ],
29
29
  },
@@ -31,7 +31,7 @@ const ICON_GROUPS: IconGroup[] = [
31
31
  category: 'Navigation arrows',
32
32
  names: [
33
33
  'chevron-down', 'chevron-right', 'chevron-left',
34
- 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down',
34
+ 'arrow-left', 'arrow-right', 'arrow-up', 'arrow-down', 'sort',
35
35
  ],
36
36
  },
37
37
  {
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Surfaces Story Component
3
+ *
4
+ * Demonstrates the accent-surface contract for theme authors. The app uses
5
+ * `accent` as a quiet hover/selected/surface tint, but a theme is free to map
6
+ * `accent` to a bold colour (e.g. Madman's Box amber). These patterns stay
7
+ * legible either way, because they pair the accent background with the theme's
8
+ * own `accentForeground`, or use the quiet `muted` / faint-`primary` surfaces.
9
+ */
10
+
11
+ import React from 'react';
12
+
13
+ const sectionHeading: React.CSSProperties = {
14
+ fontSize: '1.125rem',
15
+ fontWeight: 700,
16
+ marginBottom: '1rem',
17
+ borderBottom: '1px solid var(--color-border)',
18
+ paddingBottom: '0.5rem',
19
+ };
20
+
21
+ const note: React.CSSProperties = {
22
+ color: 'var(--color-muted-foreground)',
23
+ marginBottom: '1rem',
24
+ fontSize: '0.875rem',
25
+ };
26
+
27
+ export const Surfaces: React.FC = () => {
28
+ return (
29
+ <div style={{ padding: '1.5rem' }}>
30
+ <h2 style={{ fontSize: '1.5rem', fontWeight: 700, marginBottom: '0.5rem' }}>Surfaces</h2>
31
+ <p style={{ ...note, maxWidth: '44rem' }}>
32
+ How accent-coloured surfaces stay legible no matter how bold a theme makes
33
+ its <code>accent</code> token. Filled surfaces pair the background with{' '}
34
+ <code>qt-text-on-accent</code>; hover rows use <code>qt-hover-accent</code>;
35
+ quiet selected/panel surfaces lean on <code>muted</code> and faint{' '}
36
+ <code>primary</code> instead of the accent.
37
+ </p>
38
+
39
+ {/* Filled accent surfaces */}
40
+ <section style={{ marginBottom: '2rem' }}>
41
+ <h3 style={sectionHeading}>Filled accent surfaces</h3>
42
+ <p style={note}>
43
+ A solid <code>qt-bg-accent</code> fill <strong>must</strong> be paired with{' '}
44
+ <code>qt-text-on-accent</code> (the theme&apos;s <code>accentForeground</code>).
45
+ A fill without it falls back to the page foreground and washes out on bold accents.
46
+ </p>
47
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
48
+ <span className="qt-bg-accent qt-text-on-accent" style={{ padding: '0.125rem 0.5rem', borderRadius: '0.25rem', fontSize: '0.75rem' }}>
49
+ keyword
50
+ </span>
51
+ <span className="qt-bg-accent qt-text-on-accent" style={{ padding: '0.125rem 0.5rem', borderRadius: '0.25rem', fontSize: '0.75rem' }}>
52
+ another keyword
53
+ </span>
54
+ <kbd className="qt-bg-accent qt-text-on-accent" style={{ padding: '0.125rem 0.375rem', borderRadius: '0.25rem', fontSize: '0.75rem' }}>
55
+
56
+ </kbd>
57
+ </div>
58
+ </section>
59
+
60
+ {/* Hover-highlight rows */}
61
+ <section style={{ marginBottom: '2rem' }}>
62
+ <h3 style={sectionHeading}>Hover-highlight rows — <code>qt-hover-accent</code></h3>
63
+ <p style={note}>
64
+ For list rows, menu items, and option cards. On hover it paints the accent
65
+ background and forces the accent foreground onto the row <em>and every
66
+ descendant</em> — so children that carry their own colour (the title and the
67
+ muted subtitle below) flip too, instead of washing out (or, when their colour
68
+ matches the accent, vanishing). Hover the rows to see it.
69
+ </p>
70
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem', maxWidth: '24rem', border: '1px solid var(--color-border)', borderRadius: '0.5rem', padding: '0.375rem' }}>
71
+ <div className="qt-hover-accent" style={{ display: 'flex', flexDirection: 'column', padding: '0.5rem 0.75rem', borderRadius: '0.375rem', cursor: 'pointer' }}>
72
+ <span className="qt-text-primary">Compose a reply</span>
73
+ <span className="qt-text-secondary" style={{ fontSize: '0.8125rem' }}>Draft a response in this thread</span>
74
+ </div>
75
+ <div className="qt-hover-accent" style={{ display: 'flex', flexDirection: 'column', padding: '0.5rem 0.75rem', borderRadius: '0.375rem', cursor: 'pointer' }}>
76
+ <span className="qt-text-primary">Mark as resolved</span>
77
+ <span className="qt-text-secondary" style={{ fontSize: '0.8125rem' }}>Close and archive this item</span>
78
+ </div>
79
+ <div className="qt-hover-accent" style={{ display: 'flex', flexDirection: 'column', padding: '0.5rem 0.75rem', borderRadius: '0.375rem', cursor: 'pointer' }}>
80
+ <span className="qt-text-primary">Delete</span>
81
+ <span className="qt-text-secondary" style={{ fontSize: '0.8125rem' }}>Remove permanently</span>
82
+ </div>
83
+ </div>
84
+ </section>
85
+
86
+ {/* Quiet selected / panel surfaces */}
87
+ <section>
88
+ <h3 style={sectionHeading}>Quiet selected &amp; panel surfaces</h3>
89
+ <p style={note}>
90
+ Persistent surfaces (selected cards, informational panels) deliberately avoid
91
+ the bold accent. Selected states use a faint <code>qt-bg-primary/10</code> tint;
92
+ informational panels use the neutral <code>qt-bg-muted</code> surface.
93
+ </p>
94
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
95
+ <div className="qt-bg-primary/10" style={{ padding: '0.75rem 1rem', borderRadius: '0.5rem', border: '1px solid var(--color-primary)', maxWidth: '16rem' }}>
96
+ <div className="qt-text-primary" style={{ fontWeight: 600 }}>Selected option</div>
97
+ <div className="qt-text-secondary" style={{ fontSize: '0.8125rem' }}>Faint primary tint marks the choice.</div>
98
+ </div>
99
+ <div className="qt-bg-muted" style={{ padding: '0.75rem 1rem', borderRadius: '0.5rem', border: '1px solid var(--color-border)', maxWidth: '16rem' }}>
100
+ <div style={{ fontWeight: 600 }}>Informational panel</div>
101
+ <div className="qt-text-secondary" style={{ fontSize: '0.8125rem' }}>Neutral muted surface, never the accent.</div>
102
+ </div>
103
+ </div>
104
+ </section>
105
+ </div>
106
+ );
107
+ };
@@ -14,6 +14,7 @@ export * from './components/Buttons';
14
14
  export * from './components/Cards';
15
15
  export * from './components/Inputs';
16
16
  export * from './components/Badges';
17
+ export * from './components/Surfaces';
17
18
  export * from './components/Avatars';
18
19
  export * from './components/Dialogs';
19
20
  export * from './components/Tabs';