@quilltap/theme-storybook 1.0.97 → 1.0.100

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.
@@ -124,10 +124,17 @@
124
124
 
125
125
  /* ==========================================================================
126
126
  SEMANTIC ALIASES
127
- These map theme tokens to --color-* variables used by components
127
+ These map theme tokens to --color-* variables used by components.
128
+ A var() in a custom property resolves where it is declared, so the aliases
129
+ are re-declared on the decorated story container ([data-theme], .dark,
130
+ .light) as well as :root; otherwise a .dark below :root would change the
131
+ --theme-* tokens and leave every --color-* alias on its light value.
128
132
  ========================================================================== */
129
133
 
130
- :root {
134
+ :root,
135
+ [data-theme],
136
+ .dark,
137
+ .light {
131
138
  --color-background: var(--theme-background);
132
139
  --color-foreground: var(--theme-foreground);
133
140
  --color-card: var(--theme-card);
@@ -17,9 +17,9 @@ export const Badges: React.FC = () => {
17
17
  Badge Variants
18
18
  </h3>
19
19
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
20
- <span className="qt-badge qt-badge-default">Default</span>
21
20
  <span className="qt-badge qt-badge-primary">Primary</span>
22
21
  <span className="qt-badge qt-badge-secondary">Secondary</span>
22
+ <span className="qt-badge qt-badge-info">Info</span>
23
23
  <span className="qt-badge qt-badge-success">Success</span>
24
24
  <span className="qt-badge qt-badge-warning">Warning</span>
25
25
  <span className="qt-badge qt-badge-destructive">Destructive</span>
@@ -29,14 +29,16 @@ export const Badges: React.FC = () => {
29
29
  {/* Outline Badges */}
30
30
  <section style={{ marginBottom: '2rem' }}>
31
31
  <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
32
- Outline Badges
32
+ Outline Badge
33
33
  </h3>
34
+ <p style={{ color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
35
+ There is one outline variant, used for quiet labels beside an item &mdash; a Workbench tool&apos;s
36
+ roll form, an Inventory kit, a scenario already in this chat.
37
+ </p>
34
38
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
35
- <span className="qt-badge qt-badge-outline">Outline</span>
36
- <span className="qt-badge qt-badge-outline-primary">Primary</span>
37
- <span className="qt-badge qt-badge-outline-success">Success</span>
38
- <span className="qt-badge qt-badge-outline-warning">Warning</span>
39
- <span className="qt-badge qt-badge-outline-destructive">Destructive</span>
39
+ <span className="qt-badge qt-badge-outline">dice</span>
40
+ <span className="qt-badge qt-badge-outline">kit</span>
41
+ <span className="qt-badge qt-badge-outline">in this chat</span>
40
42
  </div>
41
43
  </section>
42
44
 
@@ -46,13 +48,15 @@ export const Badges: React.FC = () => {
46
48
  Provider Badges
47
49
  </h3>
48
50
  <p style={{ color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
49
- Used to identify AI provider sources.
51
+ Identify an embedding profile&apos;s provider. Each class stands alone (no <code>qt-badge</code>{' '}
52
+ beside it), one per embedding provider.
50
53
  </p>
51
54
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', alignItems: 'center' }}>
52
- <span className="qt-badge qt-badge-provider-openai">OpenAI</span>
53
- <span className="qt-badge qt-badge-provider-anthropic">Anthropic</span>
54
- <span className="qt-badge qt-badge-provider-google">Google</span>
55
- <span className="qt-badge qt-badge-provider-openrouter">OpenRouter</span>
55
+ <span className="qt-badge-provider-openai">OPENAI</span>
56
+ <span className="qt-badge-provider-ollama">OLLAMA</span>
57
+ <span className="qt-badge-provider-openrouter">OPENROUTER</span>
58
+ <span className="qt-badge-provider-nanogpt">NANOGPT</span>
59
+ <span className="qt-badge-provider-builtin">BUILTIN</span>
56
60
  </div>
57
61
  </section>
58
62
 
@@ -7,6 +7,58 @@
7
7
 
8
8
  import React from 'react';
9
9
 
10
+ /**
11
+ * The announcement bar's chevron. The app renders its `chevron-right` /
12
+ * `chevron-down` icon (sized by `.qt-chat-system-bar-chevron`); an expanded bar
13
+ * also carries `qt-chat-system-bar-chevron-down`, a hook with no rule of its own —
14
+ * the glyph itself points down. Inline SVG here, since a theme's Storybook does
15
+ * not serve the app's `/images/icons/` files.
16
+ */
17
+ const BarChevron: React.FC<{ expanded?: boolean }> = ({ expanded = false }) => (
18
+ <svg
19
+ className={`qt-chat-system-bar-chevron${expanded ? ' qt-chat-system-bar-chevron-down' : ''}`}
20
+ viewBox="0 0 24 24"
21
+ fill="none"
22
+ stroke="currentColor"
23
+ strokeWidth={2}
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ aria-hidden="true"
27
+ >
28
+ <polyline points={expanded ? '6 9 12 15 18 9' : '9 18 15 12 9 6'} />
29
+ </svg>
30
+ );
31
+
32
+ /**
33
+ * The app's QuillAnimation: the `thinking` glyph rocking under
34
+ * `.qt-thinking-indicator`, in a box of 3rem (`lg`, awaiting a reply) or 1rem
35
+ * (`sm`, trailing streamed prose).
36
+ */
37
+ const QuillAnimation: React.FC<{ size?: 'lg' | 'sm'; style?: React.CSSProperties }> = ({ size = 'lg', style }) => {
38
+ const box = size === 'lg' ? '3rem' : '1rem';
39
+ return (
40
+ <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: box, height: box, ...style }}>
41
+ <svg
42
+ className="qt-thinking-indicator"
43
+ width={box}
44
+ height={box}
45
+ viewBox="0 0 24 24"
46
+ fill="none"
47
+ stroke="currentColor"
48
+ strokeWidth={2}
49
+ strokeLinecap="round"
50
+ strokeLinejoin="round"
51
+ role="img"
52
+ aria-label="Writing…"
53
+ >
54
+ <path d="M21 3.5c-7.5 1.5-12 6-13.5 13.5 7.5-1.5 12-6 13.5-13.5Z" />
55
+ <path d="M19 5.5 9.5 15" />
56
+ <path d="m3 21 4.5-4.5" />
57
+ </svg>
58
+ </span>
59
+ );
60
+ };
61
+
10
62
  export const Chat: React.FC = () => {
11
63
  return (
12
64
  <div style={{ padding: '1.5rem' }}>
@@ -301,7 +353,7 @@ export const Chat: React.FC = () => {
301
353
  <span className="qt-chat-system-bar-sender">The Librarian</span>
302
354
  <span className="qt-chat-system-bar-kind">edited by character</span>
303
355
  <span className="qt-chat-system-bar-time">4:12 PM</span>
304
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&rsaquo;</span>
356
+ <BarChevron />
305
357
  </button>
306
358
 
307
359
  <div className="qt-chat-announcement-group" style={{ width: '100%', margin: 0 }}>
@@ -310,21 +362,21 @@ export const Chat: React.FC = () => {
310
362
  <span className="qt-chat-system-bar-sender">The Host</span>
311
363
  <span className="qt-chat-system-bar-kind">status change</span>
312
364
  <span className="qt-chat-system-bar-time">4:13 PM</span>
313
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&rsaquo;</span>
365
+ <BarChevron />
314
366
  </button>
315
367
  <button type="button" className="qt-chat-announcement-chip">
316
368
  <span className="qt-chat-announcement-dot qt-chat-announcement-dot-medium" aria-hidden="true" />
317
369
  <span className="qt-chat-system-bar-sender">The Lantern</span>
318
370
  <span className="qt-chat-system-bar-kind">background</span>
319
371
  <span className="qt-chat-system-bar-time">4:14 PM</span>
320
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&rsaquo;</span>
372
+ <BarChevron />
321
373
  </button>
322
374
  <button type="button" className="qt-chat-announcement-chip">
323
375
  <span className="qt-chat-announcement-dot qt-chat-announcement-dot-low" aria-hidden="true" />
324
376
  <span className="qt-chat-system-bar-sender">Prospero</span>
325
377
  <span className="qt-chat-system-bar-kind">project information</span>
326
378
  <span className="qt-chat-system-bar-time">4:14 PM</span>
327
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&rsaquo;</span>
379
+ <BarChevron />
328
380
  </button>
329
381
  <button type="button" className="qt-chat-announcement-chip qt-chat-announcement-chip-whisper">
330
382
  <span className="qt-chat-announcement-dot qt-chat-announcement-dot-medium" aria-hidden="true" />
@@ -332,7 +384,7 @@ export const Chat: React.FC = () => {
332
384
  <span className="qt-chat-system-bar-kind">announcement</span>
333
385
  <span className="qt-chat-system-bar-whisper">to Amy</span>
334
386
  <span className="qt-chat-system-bar-time">4:15 PM</span>
335
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&rsaquo;</span>
387
+ <BarChevron />
336
388
  </button>
337
389
  </div>
338
390
 
@@ -343,7 +395,7 @@ export const Chat: React.FC = () => {
343
395
  <span className="qt-chat-system-bar-kind">announcement</span>
344
396
  <span className="qt-chat-system-bar-whisper">to Amy, Bartholomew</span>
345
397
  <span className="qt-chat-system-bar-time">4:15 PM</span>
346
- <span className="qt-chat-system-bar-chevron qt-chat-system-bar-chevron-down" aria-hidden="true">&rsaquo;</span>
398
+ <BarChevron expanded />
347
399
  </button>
348
400
  </div>
349
401
  </section>
@@ -374,7 +426,7 @@ export const Chat: React.FC = () => {
374
426
  <span className="qt-chat-system-bar-sender">Pascal</span>
375
427
  <span className="qt-chat-system-bar-kind">{tool}</span>
376
428
  <span className="qt-chat-system-bar-time">4:15 PM</span>
377
- <span className="qt-chat-system-bar-chevron" aria-hidden="true">&and;</span>
429
+ <BarChevron expanded />
378
430
  </button>
379
431
  <div className={`qt-chat-message-body qt-chat-message-assistant qt-pascal-result qt-pascal-result--${state}`}>
380
432
  <p>🎲 <strong>{tool}</strong> &mdash; {message}</p>
@@ -552,20 +604,31 @@ export const Chat: React.FC = () => {
552
604
  </div>
553
605
  </section>
554
606
 
555
- {/* Typing Indicator */}
607
+ {/* Writing Indicator */}
556
608
  <section style={{ marginBottom: '2rem' }}>
557
609
  <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
558
- Typing Indicator
610
+ Writing Indicator
559
611
  </h3>
560
- <div style={{ display: 'flex', gap: '0.5rem', maxWidth: '32rem' }}>
561
- <div className="qt-avatar qt-avatar-sm">
562
- <div className="qt-avatar-fallback">AI</div>
612
+ <p style={{ color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
613
+ The rocking quill (<code>.qt-thinking-indicator</code>, tuned by the <code>--qt-thinking-*</code>{' '}
614
+ properties): large while a reply is awaited, small at the tail of prose still streaming in.
615
+ </p>
616
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: '32rem' }}>
617
+ {/* Awaiting the first token */}
618
+ <div className="qt-chat-message-row qt-chat-message-row-assistant">
619
+ <div className="qt-chat-message-body">
620
+ <div className="qt-text-secondary">
621
+ <QuillAnimation size="lg" />
622
+ </div>
623
+ </div>
563
624
  </div>
564
- <div className="qt-chat-message qt-chat-message-assistant">
565
- <div className="qt-typing-indicator">
566
- <span></span>
567
- <span></span>
568
- <span></span>
625
+ {/* Streaming */}
626
+ <div className="qt-chat-message-row qt-chat-message-row-assistant">
627
+ <div className="qt-chat-message-body qt-chat-message-assistant">
628
+ <p>
629
+ The merchant counts the coins twice, then a third time
630
+ <QuillAnimation size="sm" style={{ marginLeft: '0.5rem' }} />
631
+ </p>
569
632
  </div>
570
633
  </div>
571
634
  </div>
@@ -733,22 +796,24 @@ export const Chat: React.FC = () => {
733
796
  <p style={{ fontSize: '0.875rem', color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
734
797
  The fact-checker&apos;s verdict on a message, shown among the message action icons. Quiet by
735
798
  design; the discrepancy notes live in a tooltip. Hover one to see its hover state.
799
+ Same markup as the app: <code>qt-confirmation-badge qt-text-xs</code>, a glyph and a label.{' '}
800
+ <code>qt-confirmation-badge-label</code> has no rule in the app; it is a hook a theme may style.
736
801
  </p>
737
802
  <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap' }}>
738
- <button className="qt-confirmation-badge" data-confirmation-state="vouched">
739
- <span className="qt-confirmation-badge-glyph">✓</span>
803
+ <button type="button" className="qt-confirmation-badge qt-text-xs" data-confirmation-state="vouched">
804
+ <span aria-hidden="true" className="qt-confirmation-badge-glyph">✓</span>
740
805
  <span className="qt-confirmation-badge-label">Vouched</span>
741
806
  </button>
742
- <button className="qt-confirmation-badge" data-confirmation-state="amended">
743
- <span className="qt-confirmation-badge-glyph">✎</span>
807
+ <button type="button" className="qt-confirmation-badge qt-text-xs" data-confirmation-state="amended">
808
+ <span aria-hidden="true" className="qt-confirmation-badge-glyph">✎</span>
744
809
  <span className="qt-confirmation-badge-label">Amended</span>
745
810
  </button>
746
- <button className="qt-confirmation-badge" data-confirmation-state="stood-by">
747
- <span className="qt-confirmation-badge-glyph">!</span>
811
+ <button type="button" className="qt-confirmation-badge qt-text-xs" data-confirmation-state="stood-by">
812
+ <span aria-hidden="true" className="qt-confirmation-badge-glyph">!</span>
748
813
  <span className="qt-confirmation-badge-label">Stood by</span>
749
814
  </button>
750
- <button className="qt-confirmation-badge" data-confirmation-state="unvetted">
751
- <span className="qt-confirmation-badge-glyph">—</span>
815
+ <button type="button" className="qt-confirmation-badge qt-text-xs" data-confirmation-state="unvetted">
816
+ <span aria-hidden="true" className="qt-confirmation-badge-glyph">—</span>
752
817
  <span className="qt-confirmation-badge-label">Unvetted</span>
753
818
  </button>
754
819
  </div>
@@ -38,9 +38,11 @@ export const Tabs: React.FC = () => {
38
38
  Notifications
39
39
  </button>
40
40
  </div>
41
- <div>
41
+ {/* Same shape as the app's EntityTabs: the panel itself carries no qt-* class. */}
42
+ <div className="qt-tab-divider" />
43
+ <div role="tabpanel" style={{ padding: '1rem 0' }}>
42
44
  {activeTab === 'tab1' && (
43
- <div className="qt-tab-content">
45
+ <div>
44
46
  <h4 style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Account Settings</h4>
45
47
  <p style={{ color: 'var(--color-muted-foreground)' }}>
46
48
  Manage your account settings and preferences.
@@ -48,7 +50,7 @@ export const Tabs: React.FC = () => {
48
50
  </div>
49
51
  )}
50
52
  {activeTab === 'tab2' && (
51
- <div className="qt-tab-content">
53
+ <div>
52
54
  <h4 style={{ fontWeight: 600, marginBottom: '0.5rem' }}>General Settings</h4>
53
55
  <p style={{ color: 'var(--color-muted-foreground)' }}>
54
56
  Configure general application settings.
@@ -56,7 +58,7 @@ export const Tabs: React.FC = () => {
56
58
  </div>
57
59
  )}
58
60
  {activeTab === 'tab3' && (
59
- <div className="qt-tab-content">
61
+ <div>
60
62
  <h4 style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Notification Preferences</h4>
61
63
  <p style={{ color: 'var(--color-muted-foreground)' }}>
62
64
  Choose how you want to receive notifications.
@@ -99,26 +101,58 @@ export const Tabs: React.FC = () => {
99
101
  <h3 style={{ fontSize: '1.125rem', fontWeight: 700, marginBottom: '1rem', borderBottom: '1px solid var(--color-border)', paddingBottom: '0.5rem' }}>
100
102
  Navigation Bar
101
103
  </h3>
102
- <div className="qt-navbar">
103
- <div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
104
- <div style={{ fontWeight: 700, fontSize: '1.125rem' }}>Quilltap</div>
105
- <nav style={{ display: 'flex', gap: '0.25rem' }}>
106
- <a href="#" className="qt-navbar-link qt-navbar-link-active">Dashboard</a>
107
- <a href="#" className="qt-navbar-link">Characters</a>
108
- <a href="#" className="qt-navbar-link">Chats</a>
109
- <a href="#" className="qt-navbar-link">Settings</a>
110
- </nav>
111
- </div>
112
- <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
113
- <button className="qt-button-icon" aria-label="Notifications">
114
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
115
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
116
- </svg>
117
- </button>
118
- <div className="qt-avatar qt-avatar-sm">
119
- <div className="qt-avatar-fallback">U</div>
104
+ <p style={{ color: 'var(--color-muted-foreground)', marginBottom: '1rem' }}>
105
+ The <code>qt-navbar</code> family from <code>_layout.css</code>: the bar holds a{' '}
106
+ <code>qt-navbar-container</code>, which lays out <code>qt-navbar-section</code> groups end to end.
107
+ Links are <code>qt-navbar-link</code> / <code>qt-navbar-link-active</code>; icon buttons are{' '}
108
+ <code>qt-navbar-toggle</code> (with <code>qt-navbar-toggle-active</code> when pressed), as in the
109
+ app&apos;s content-width toggle.
110
+ </p>
111
+ {/* Quilltap runs on Tailwind's preflight, which strips the browser's link
112
+ underline and button chrome before any qt-* rule applies. Storybook has
113
+ no preflight, so this demo restores just that much — in the base layer,
114
+ beneath the qt-* component rules, so they still win. */}
115
+ <style>{`
116
+ @layer base {
117
+ .sb-navbar-demo a { color: inherit; text-decoration: inherit; }
118
+ .sb-navbar-demo button { background: transparent; border: 0; padding: 0; font: inherit; color: inherit; }
119
+ }
120
+ `}</style>
121
+ <div className="sb-navbar-demo">
122
+ <header className="qt-navbar">
123
+ <div className="qt-navbar-container">
124
+ <div className="qt-navbar-section">
125
+ <span className="qt-font-brand">Quilltap</span>
126
+ <nav aria-label="Main" className="qt-navbar-section" style={{ gap: '0.25rem' }}>
127
+ <a href="#" className="qt-navbar-link-active" aria-current="page">Salon</a>
128
+ <a href="#" className="qt-navbar-link">Aurora</a>
129
+ <a href="#" className="qt-navbar-link">Scriptorium</a>
130
+ <a href="#" className="qt-navbar-link">Settings</a>
131
+ </nav>
132
+ </div>
133
+ <div className="qt-navbar-section" style={{ gap: '0.5rem' }}>
134
+ <button type="button" className="qt-navbar-toggle" aria-label="Notifications" title="Notifications">
135
+ <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
136
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
137
+ </svg>
138
+ </button>
139
+ <button
140
+ type="button"
141
+ className="qt-navbar-toggle qt-navbar-toggle-active"
142
+ aria-label="Switch to narrow layout"
143
+ title="Switch to narrow layout"
144
+ aria-pressed={true}
145
+ >
146
+ <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
147
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 9L4 4m0 0v4m0-4h4m6 5l5-5m0 0v4m0-4h-4M9 15l-5 5m0 0v-4m0 4h4m6-5l5 5m0 0v-4m0 4h-4" />
148
+ </svg>
149
+ </button>
150
+ <div className="qt-avatar qt-avatar-sm">
151
+ <div className="qt-avatar-fallback">U</div>
152
+ </div>
153
+ </div>
120
154
  </div>
121
- </div>
155
+ </header>
122
156
  </div>
123
157
  </section>
124
158
  </div>
@@ -183,9 +183,9 @@ export const ThemeComparison: React.FC = () => {
183
183
  <div style={{ marginBottom: '1.5rem' }}>
184
184
  <h4 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.75rem', color: 'var(--color-muted-foreground)' }}>Badges</h4>
185
185
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
186
- <span className="qt-badge qt-badge-default">Default</span>
187
186
  <span className="qt-badge qt-badge-primary">Primary</span>
188
187
  <span className="qt-badge qt-badge-secondary">Secondary</span>
188
+ <span className="qt-badge qt-badge-info">Info</span>
189
189
  <span className="qt-badge qt-badge-success">Success</span>
190
190
  <span className="qt-badge qt-badge-warning">Warning</span>
191
191
  <span className="qt-badge qt-badge-destructive">Destructive</span>