@happyvertical/smrt-playground 0.38.26 → 0.39.0

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/dist/index.js CHANGED
@@ -40,7 +40,7 @@ import { playgroundModules } from 'virtual:smrt-playground/modules';
40
40
  <\/script>
41
41
 
42
42
  <PlaygroundHost
43
- title="SMRT Playground"
43
+ title="s-m-r-t playground"
44
44
  subtitle="Package previews and local app overrides"
45
45
  modules={playgroundModules}
46
46
  />
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import {\n displayNameForScopedPackage,\n displayNameForSmrtPackage,\n} from './utils.js';\n\nexport function createPackagePlaygroundTemplate(packageName: string): string {\n const displayName = displayNameForSmrtPackage(packageName);\n\n return `/**\n * ${displayName} playground definitions\n *\n * Export preview entries for the shared SMRT playground host here.\n */\n\nexport default {\n packageName: '${packageName}',\n displayName: '${displayName}',\n entries: [],\n};\n`;\n}\n\nexport function createAppPlaygroundTemplate(packageName: string): string {\n const displayName = displayNameForScopedPackage(packageName);\n\n return `/**\n * Local app playground overrides\n *\n * Add app-specific previews here, or override installed package entries by\n * exporting an additional module with the same packageName + entry id.\n */\n\nexport default [\n {\n packageName: '${packageName}',\n displayName: '${displayName}',\n entries: [],\n },\n];\n`;\n}\n\nexport function createAppPlaygroundRouteTemplate(): string {\n return `<script lang=\"ts\">\nimport { PlaygroundHost } from '@happyvertical/smrt-playground/svelte';\nimport { playgroundModules } from 'virtual:smrt-playground/modules';\n</script>\n\n<PlaygroundHost\n title=\"SMRT Playground\"\n subtitle=\"Package previews and local app overrides\"\n modules={playgroundModules}\n/>\n`;\n}\n"],"mappings":";;;AAKO,SAAS,gCAAgC,aAA6B;CAC3E,MAAM,cAAc,0BAA0B,WAAW;CAEzD,OAAO;KACJ,YAAW;;;;;;kBAME,YAAW;kBACX,YAAW;;;;AAI7B;AAEO,SAAS,4BAA4B,aAA6B;CAGvE,OAAO;;;;;;;;;oBASW,YAAW;oBAXT,4BAA4B,WAY9B,EAAW;;;;;AAK/B;AAEO,SAAS,mCAA2C;CACzD,OAAO;;;;;;;;;;;AAWT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import {\n displayNameForScopedPackage,\n displayNameForSmrtPackage,\n} from './utils.js';\n\nexport function createPackagePlaygroundTemplate(packageName: string): string {\n const displayName = displayNameForSmrtPackage(packageName);\n\n return `/**\n * ${displayName} playground definitions\n *\n * Export preview entries for the shared SMRT playground host here.\n */\n\nexport default {\n packageName: '${packageName}',\n displayName: '${displayName}',\n entries: [],\n};\n`;\n}\n\nexport function createAppPlaygroundTemplate(packageName: string): string {\n const displayName = displayNameForScopedPackage(packageName);\n\n return `/**\n * Local app playground overrides\n *\n * Add app-specific previews here, or override installed package entries by\n * exporting an additional module with the same packageName + entry id.\n */\n\nexport default [\n {\n packageName: '${packageName}',\n displayName: '${displayName}',\n entries: [],\n },\n];\n`;\n}\n\nexport function createAppPlaygroundRouteTemplate(): string {\n return `<script lang=\"ts\">\nimport { PlaygroundHost } from '@happyvertical/smrt-playground/svelte';\nimport { playgroundModules } from 'virtual:smrt-playground/modules';\n</script>\n\n<PlaygroundHost\n title=\"s-m-r-t playground\"\n subtitle=\"Package previews and local app overrides\"\n modules={playgroundModules}\n/>\n`;\n}\n"],"mappings":";;;AAKO,SAAS,gCAAgC,aAA6B;CAC3E,MAAM,cAAc,0BAA0B,WAAW;CAEzD,OAAO;KACJ,YAAW;;;;;;kBAME,YAAW;kBACX,YAAW;;;;AAI7B;AAEO,SAAS,4BAA4B,aAA6B;CAGvE,OAAO;;;;;;;;;oBASW,YAAW;oBAXT,4BAA4B,WAY9B,EAAW;;;;;AAK/B;AAEO,SAAS,mCAA2C;CACzD,OAAO;;;;;;;;;;;AAWT"}
@@ -22,41 +22,110 @@ export interface Props {
22
22
 
23
23
  let {
24
24
  modules = [],
25
- title = 'SMRT Playground',
25
+ title = 's-m-r-t playground',
26
26
  subtitle = 'Shared package previews with app-local overrides',
27
27
  }: Props = $props();
28
28
 
29
- const normalizedModules = $derived(mergePlaygroundModules(modules));
30
- const filteredModules = $derived(
31
- normalizedModules.filter((module) => {
32
- if (!searchTerm) {
33
- return true;
34
- }
35
-
36
- const query = searchTerm.toLowerCase();
37
- return (
38
- module.displayName.toLowerCase().includes(query) ||
39
- module.entries.some(
40
- (entry) =>
41
- entry.title.toLowerCase().includes(query) ||
42
- entry.id.toLowerCase().includes(query),
43
- )
44
- );
45
- }),
46
- );
29
+ const foundationPackageName = '@happyvertical/smrt-ui';
30
+ const foundationGroupOrder = [
31
+ 'Inputs & controls',
32
+ 'Feedback & overlays',
33
+ 'Collections & data',
34
+ ] as const;
47
35
 
36
+ let searchTerm = $state('');
48
37
  let selectedModuleName = $state<string | null>(null);
49
38
  let selectedEntryId = $state<string | null>(null);
50
39
  let selectedMode = $state<SmrtPlaygroundMode>('mock');
51
- let searchTerm = $state('');
52
40
  let isHydrated = $state(false);
53
41
 
54
42
  onMount(() => {
55
43
  isHydrated = true;
56
44
  });
57
45
 
46
+ const normalizedModules = $derived(mergePlaygroundModules(modules));
47
+ const orderedModules = $derived([
48
+ ...normalizedModules.filter(
49
+ (module) => module.packageName === foundationPackageName,
50
+ ),
51
+ ...normalizedModules.filter(
52
+ (module) => module.packageName !== foundationPackageName,
53
+ ),
54
+ ]);
55
+
56
+ const filteredModules = $derived.by(() => {
57
+ const query = searchTerm.trim().toLowerCase();
58
+ if (!query) {
59
+ return orderedModules;
60
+ }
61
+
62
+ return orderedModules
63
+ .map((module) => {
64
+ const moduleMatches = [
65
+ module.displayName,
66
+ module.packageName,
67
+ module.description,
68
+ ].some((value) => value?.toLowerCase().includes(query));
69
+
70
+ if (moduleMatches) {
71
+ return module;
72
+ }
73
+
74
+ return {
75
+ ...module,
76
+ entries: module.entries.filter((entry) =>
77
+ [
78
+ entry.title,
79
+ entry.id,
80
+ entry.description,
81
+ ...(entry.tags ?? []),
82
+ ].some((value) => value?.toLowerCase().includes(query)),
83
+ ),
84
+ };
85
+ })
86
+ .filter((module) => module.entries.length > 0);
87
+ });
88
+
89
+ const filteredFoundation = $derived(
90
+ filteredModules.find(
91
+ (module) => module.packageName === foundationPackageName,
92
+ ) ?? null,
93
+ );
94
+ const filteredPackageModules = $derived(
95
+ filteredModules.filter(
96
+ (module) => module.packageName !== foundationPackageName,
97
+ ),
98
+ );
99
+
100
+ function foundationGroup(entry: ResolvedSmrtPlaygroundEntry) {
101
+ const tags = entry.tags ?? [];
102
+ if (tags.includes('feedback') || tags.includes('overlays')) {
103
+ return 'Feedback & overlays';
104
+ }
105
+ if (
106
+ tags.some((tag) =>
107
+ ['content', 'data', 'grid', 'list', 'table'].includes(tag),
108
+ )
109
+ ) {
110
+ return 'Collections & data';
111
+ }
112
+ return 'Inputs & controls';
113
+ }
114
+
115
+ const foundationGroups = $derived.by(() => {
116
+ const grouped = new Map<string, ResolvedSmrtPlaygroundEntry[]>();
117
+ for (const entry of filteredFoundation?.entries ?? []) {
118
+ const group = foundationGroup(entry);
119
+ grouped.set(group, [...(grouped.get(group) ?? []), entry]);
120
+ }
121
+
122
+ return foundationGroupOrder
123
+ .map((group) => [group, grouped.get(group) ?? []] as const)
124
+ .filter(([, entries]) => entries.length > 0);
125
+ });
126
+
58
127
  $effect(() => {
59
- const firstModule = filteredModules[0] ?? normalizedModules[0] ?? null;
128
+ const firstModule = orderedModules[0] ?? null;
60
129
  if (!firstModule) {
61
130
  selectedModuleName = null;
62
131
  selectedEntryId = null;
@@ -64,67 +133,66 @@ $effect(() => {
64
133
  }
65
134
 
66
135
  const activeModule =
67
- filteredModules.find(
136
+ orderedModules.find(
68
137
  (module) => module.packageName === selectedModuleName,
69
- ) ||
70
- normalizedModules.find(
71
- (module) => module.packageName === selectedModuleName,
72
- ) ||
73
- firstModule;
138
+ ) ?? firstModule;
74
139
 
75
140
  if (activeModule.packageName !== selectedModuleName) {
76
141
  selectedModuleName = activeModule.packageName;
142
+ selectedEntryId =
143
+ activeModule.packageName === foundationPackageName
144
+ ? (activeModule.entries[0]?.qualifiedId ?? null)
145
+ : null;
146
+ selectedMode = activeModule.entries[0]?.availableModes[0] ?? 'mock';
147
+ return;
148
+ }
149
+
150
+ if (selectedEntryId === null) {
151
+ return;
77
152
  }
78
153
 
79
154
  const activeEntry =
80
155
  activeModule.entries.find(
81
156
  (entry) => entry.qualifiedId === selectedEntryId,
82
- ) ||
83
- activeModule.entries[0] ||
84
- null;
157
+ ) ?? null;
85
158
 
86
- if (activeEntry && activeEntry.qualifiedId !== selectedEntryId) {
87
- selectedEntryId = activeEntry.qualifiedId;
159
+ if (!activeEntry) {
160
+ selectedEntryId = null;
161
+ return;
88
162
  }
89
163
 
90
- if (activeEntry && !activeEntry.availableModes.includes(selectedMode)) {
164
+ selectedEntryId = activeEntry.qualifiedId;
165
+ if (!activeEntry.availableModes.includes(selectedMode)) {
91
166
  selectedMode = activeEntry.availableModes[0] ?? 'mock';
92
167
  }
93
168
  });
94
169
 
95
170
  const selectedModule = $derived(
96
- normalizedModules.find(
97
- (module) => module.packageName === selectedModuleName,
98
- ) ||
99
- normalizedModules[0] ||
171
+ orderedModules.find((module) => module.packageName === selectedModuleName) ??
172
+ orderedModules[0] ??
100
173
  null,
101
174
  );
102
-
103
175
  const selectedEntry = $derived(
104
176
  selectedModule?.entries.find(
105
177
  (entry) => entry.qualifiedId === selectedEntryId,
106
- ) ||
107
- selectedModule?.entries[0] ||
108
- null,
178
+ ) ?? null,
179
+ );
180
+ const selectedModeConfig = $derived(
181
+ selectedEntry ? (selectedEntry.modes[selectedMode] ?? null) : null,
109
182
  );
110
-
111
- let PreviewComponent = $state<Component<Record<string, unknown>> | null>(null);
112
- let previewComponentEntryId = $state<string | null>(null);
113
- let previewLoadError = $state<string | null>(null);
114
- let previewIsLoading = $state(false);
115
-
116
183
  const selectedProps = $derived(
117
184
  selectedEntry
118
185
  ? {
119
- ...(selectedEntry.props || {}),
120
- ...(selectedEntry.modes[selectedMode]?.props || {}),
186
+ ...(selectedEntry.props ?? {}),
187
+ ...(selectedModeConfig?.props ?? {}),
121
188
  }
122
189
  : {},
123
190
  );
124
191
 
125
- const selectedModeConfig = $derived(
126
- selectedEntry ? (selectedEntry.modes[selectedMode] ?? null) : null,
127
- );
192
+ let PreviewComponent = $state<Component<Record<string, unknown>> | null>(null);
193
+ let previewComponentEntryId = $state<string | null>(null);
194
+ let previewLoadError = $state<string | null>(null);
195
+ let previewIsLoading = $state(false);
128
196
 
129
197
  function resolvePreviewComponent(
130
198
  loaded: PlaygroundComponentModule,
@@ -146,7 +214,6 @@ $effect(() => {
146
214
  let cancelled = false;
147
215
 
148
216
  previewLoadError = null;
149
-
150
217
  if (!entry) {
151
218
  PreviewComponent = null;
152
219
  previewComponentEntryId = null;
@@ -179,7 +246,6 @@ $effect(() => {
179
246
  if (cancelled) {
180
247
  return;
181
248
  }
182
-
183
249
  PreviewComponent = resolvePreviewComponent(loaded);
184
250
  previewComponentEntryId = entry.qualifiedId;
185
251
  previewIsLoading = false;
@@ -188,7 +254,6 @@ $effect(() => {
188
254
  if (cancelled) {
189
255
  return;
190
256
  }
191
-
192
257
  PreviewComponent = null;
193
258
  previewComponentEntryId = null;
194
259
  previewLoadError =
@@ -202,8 +267,15 @@ $effect(() => {
202
267
  });
203
268
 
204
269
  function selectModule(packageName: string) {
270
+ const module = orderedModules.find(
271
+ (candidate) => candidate.packageName === packageName,
272
+ );
205
273
  selectedModuleName = packageName;
206
- selectedEntryId = null;
274
+ selectedEntryId =
275
+ packageName === foundationPackageName
276
+ ? (module?.entries[0]?.qualifiedId ?? null)
277
+ : null;
278
+ selectedMode = module?.entries[0]?.availableModes[0] ?? 'mock';
207
279
  }
208
280
 
209
281
  function selectEntry(entry: ResolvedSmrtPlaygroundEntry) {
@@ -213,137 +285,188 @@ function selectEntry(entry: ResolvedSmrtPlaygroundEntry) {
213
285
  }
214
286
  </script>
215
287
 
288
+ {#snippet entryButton(entry: ResolvedSmrtPlaygroundEntry)}
289
+ <button
290
+ type="button"
291
+ class:active={entry.qualifiedId === selectedEntry?.qualifiedId}
292
+ aria-pressed={entry.qualifiedId === selectedEntry?.qualifiedId}
293
+ data-playground-entry={entry.qualifiedId}
294
+ onclick={() => selectEntry(entry)}
295
+ >
296
+ {entry.title}
297
+ </button>
298
+ {/snippet}
299
+
216
300
  <ThemeProvider colorScheme="system" persist={true}>
217
301
  <div class="playground-shell" data-hydrated={isHydrated ? 'true' : 'false'}>
218
- <aside class="sidebar">
219
- <div class="sidebar__header">
220
- <p class="eyebrow">Shared Host</p>
221
- <h1>{title}</h1>
222
- <p>{subtitle}</p>
223
- </div>
224
-
225
- <div class="theme-controls" data-testid="playground-theme-controls">
226
- <ThemeSwitcher variant="select" label="Theme" />
227
- <ColorSchemeToggle />
228
- </div>
302
+ <aside class="catalog-nav">
303
+ <header class="brand">
304
+ <span class="brand__mark" aria-hidden="true">S</span>
305
+ <div>
306
+ <h1>{title}</h1>
307
+ <p>{subtitle}</p>
308
+ </div>
309
+ </header>
229
310
 
230
- <label class="search">
231
- <span>Filter previews</span>
232
- <input bind:value={searchTerm} placeholder="Search packages or entries" />
311
+ <label class="search-field">
312
+ <span>Find a component</span>
313
+ <input bind:value={searchTerm} placeholder="Search" />
233
314
  </label>
234
315
 
235
- <div class="module-list" data-testid="playground-modules">
316
+ <div class="catalog-scroll" data-testid="playground-modules">
236
317
  {#if filteredModules.length === 0}
237
- <div class="empty-card">
238
- <strong>No previews found</strong>
239
- <p>Add a `src/svelte/playground.ts` file to a package, or scaffold `src/playground.ts` in your app.</p>
318
+ <div class="empty-state">
319
+ <strong>No components found</strong>
320
+ <p>Try another component, package, or tag.</p>
240
321
  </div>
241
322
  {:else}
242
- {#each filteredModules as module (module.packageName)}
243
- <button
244
- type="button"
245
- class:selected={module.packageName === selectedModule?.packageName}
246
- data-playground-module={module.packageName}
247
- onclick={() => selectModule(module.packageName)}
248
- >
249
- <strong>{module.displayName}</strong>
250
- <span>{module.entries.length} preview{module.entries.length === 1 ? '' : 's'}</span>
251
- </button>
252
- {/each}
323
+ <nav aria-label="Component catalog" data-testid="playground-entries">
324
+ {#if filteredFoundation}
325
+ <section class="foundation-catalog">
326
+ <button
327
+ type="button"
328
+ class="foundation-heading"
329
+ data-playground-module={filteredFoundation.packageName}
330
+ onclick={() => selectModule(filteredFoundation.packageName)}
331
+ >
332
+ <span>Foundation</span>
333
+ <small>{filteredFoundation.entries.length}</small>
334
+ </button>
335
+
336
+ {#each foundationGroups as [group, entries]}
337
+ <section class="nav-group">
338
+ <h2>{group}</h2>
339
+ {#each entries as entry (entry.qualifiedId)}
340
+ {@render entryButton(entry)}
341
+ {/each}
342
+ </section>
343
+ {/each}
344
+ </section>
345
+ {/if}
346
+
347
+ {#if filteredPackageModules.length > 0}
348
+ <section class="package-groups">
349
+ <h2>Package components</h2>
350
+ {#each filteredPackageModules as module (module.packageName)}
351
+ <details open={module.packageName === selectedModule?.packageName}>
352
+ <summary
353
+ data-playground-module={module.packageName}
354
+ onclick={(event) => {
355
+ event.preventDefault();
356
+ selectModule(module.packageName);
357
+ }}
358
+ >
359
+ <span>{module.displayName}</span>
360
+ <small>{module.entries.length}</small>
361
+ </summary>
362
+ <div>
363
+ {#each module.entries as entry (entry.qualifiedId)}
364
+ {@render entryButton(entry)}
365
+ {/each}
366
+ </div>
367
+ </details>
368
+ {/each}
369
+ </section>
370
+ {/if}
371
+ </nav>
253
372
  {/if}
254
373
  </div>
374
+
375
+ <footer class="theme-controls" data-testid="playground-theme-controls">
376
+ <ThemeSwitcher variant="select" label="Theme" />
377
+ <ColorSchemeToggle />
378
+ </footer>
255
379
  </aside>
256
380
 
257
- <main class="content">
381
+ <main class="workspace">
258
382
  {#if selectedModule}
259
- <header class="content__header">
260
- <div>
261
- <p class="eyebrow" data-testid="playground-selected-package">{selectedModule.packageName}</p>
262
- <h2 data-testid="playground-selected-module">{selectedModule.displayName}</h2>
263
- {#if selectedModule.description}
264
- <p>{selectedModule.description}</p>
265
- {/if}
383
+ <header class="workspace-header">
384
+ <div class="breadcrumbs">
385
+ <span data-testid="playground-selected-module">{selectedModule.displayName}</span>
386
+ <i aria-hidden="true">/</i>
387
+ <strong>{selectedEntry?.title ?? 'Overview'}</strong>
266
388
  </div>
389
+
390
+ {#if selectedEntry && selectedEntry.availableModes.length > 1}
391
+ <div class="mode-control" role="group" aria-label="Preview mode">
392
+ {#each selectedEntry.availableModes as mode}
393
+ <button
394
+ type="button"
395
+ class:active={mode === selectedMode}
396
+ aria-pressed={mode === selectedMode}
397
+ data-playground-mode={mode}
398
+ onclick={() => (selectedMode = mode)}
399
+ >
400
+ {mode}
401
+ </button>
402
+ {/each}
403
+ </div>
404
+ {/if}
267
405
  </header>
268
406
 
269
- <div class="content__body">
270
- <section class="entry-list" data-testid="playground-entries">
271
- {#each selectedModule.entries as entry (entry.qualifiedId)}
272
- <button
273
- type="button"
274
- class:selected={entry.qualifiedId === selectedEntry?.qualifiedId}
275
- aria-pressed={entry.qualifiedId === selectedEntry?.qualifiedId}
276
- data-playground-entry={entry.qualifiedId}
277
- onclick={() => selectEntry(entry)}
278
- >
279
- <div>
280
- <strong>{entry.title}</strong>
281
- <span>{entry.id}</span>
282
- </div>
283
- <div class="mode-pills">
284
- {#each entry.availableModes as mode}
285
- <span>{mode}</span>
286
- {/each}
287
- </div>
288
- </button>
289
- {/each}
290
- </section>
407
+ {#if selectedEntry}
408
+ <div class="preview-heading">
409
+ <p data-testid="playground-selected-package">{selectedModule.packageName}</p>
410
+ <h2 data-testid="playground-preview-title">{selectedEntry.title}</h2>
411
+ {#if selectedEntry.description}
412
+ <span>{selectedEntry.description}</span>
413
+ {/if}
414
+ {#if selectedModeConfig?.description}
415
+ <span class="mode-description">{selectedModeConfig.description}</span>
416
+ {/if}
417
+ </div>
291
418
 
292
419
  <section class="preview-panel" data-testid="playground-preview-panel">
293
- {#if selectedEntry && PreviewComponent && previewComponentEntryId === selectedEntry.qualifiedId}
294
- <div class="preview-panel__meta">
295
- <div>
296
- <h3 data-testid="playground-preview-title">{selectedEntry.title}</h3>
297
- {#if selectedEntry.description}
298
- <p>{selectedEntry.description}</p>
299
- {/if}
300
- {#if selectedModeConfig?.description}
301
- <p class="mode-description">{selectedModeConfig.description}</p>
302
- {/if}
303
- </div>
304
-
305
- {#if selectedEntry.availableModes.length > 1}
306
- <div class="mode-toggle" role="group" aria-label="Preview mode">
307
- {#each selectedEntry.availableModes as mode}
308
- <button
309
- type="button"
310
- class:active={mode === selectedMode}
311
- aria-pressed={mode === selectedMode}
312
- data-playground-mode={mode}
313
- onclick={() => (selectedMode = mode)}
314
- >
315
- {mode}
316
- </button>
317
- {/each}
318
- </div>
319
- {/if}
320
- </div>
321
-
322
- <div class="preview-stage" data-testid="playground-preview-stage">
420
+ <div class="preview-stage" data-testid="playground-preview-stage">
421
+ {#if PreviewComponent && previewComponentEntryId === selectedEntry.qualifiedId}
323
422
  <PreviewComponent {...selectedProps} />
324
- </div>
325
- {:else if selectedEntry && previewIsLoading}
326
- <div class="empty-card">
327
- <strong>Loading preview...</strong>
328
- <p>{selectedEntry.title} is being loaded for the selected mode.</p>
329
- </div>
330
- {:else if selectedEntry && previewLoadError}
331
- <div class="empty-card">
332
- <strong>Preview failed to load</strong>
333
- <p>{previewLoadError}</p>
334
- </div>
335
- {:else}
336
- <div class="empty-card">
337
- <strong>No preview selected</strong>
338
- <p>Select a preview entry from the left to render it.</p>
339
- </div>
340
- {/if}
423
+ {:else if previewIsLoading}
424
+ <div class="preview-state">
425
+ <span class="loader" aria-hidden="true"></span>
426
+ <p>Loading {selectedEntry.title}…</p>
427
+ </div>
428
+ {:else}
429
+ <div class="preview-state">
430
+ <strong>Preview unavailable</strong>
431
+ <p>{previewLoadError ?? 'Select a component to begin.'}</p>
432
+ </div>
433
+ {/if}
434
+ </div>
341
435
  </section>
342
- </div>
436
+ {:else}
437
+ <section class="package-landing" data-testid="playground-package-landing">
438
+ <header>
439
+ <p data-testid="playground-selected-package">{selectedModule.packageName}</p>
440
+ <h2>{selectedModule.displayName}</h2>
441
+ {#if selectedModule.description}
442
+ <span>{selectedModule.description}</span>
443
+ {/if}
444
+ <small>{selectedModule.entries.length} component{selectedModule.entries.length === 1 ? '' : 's'}</small>
445
+ </header>
446
+
447
+ <div class="package-index">
448
+ <h3>Components</h3>
449
+ {#each selectedModule.entries as entry (entry.qualifiedId)}
450
+ <button
451
+ type="button"
452
+ data-playground-landing-entry={entry.qualifiedId}
453
+ onclick={() => selectEntry(entry)}
454
+ >
455
+ <span class="package-index__copy">
456
+ <strong>{entry.title}</strong>
457
+ <small>{entry.description ?? entry.id}</small>
458
+ </span>
459
+ <span class="package-index__modes">{entry.availableModes.join(' · ')}</span>
460
+ <span class="package-index__arrow" aria-hidden="true">→</span>
461
+ </button>
462
+ {/each}
463
+ </div>
464
+ </section>
465
+ {/if}
343
466
  {:else}
344
- <div class="empty-card full-height">
467
+ <div class="preview-state full-height">
345
468
  <strong>No playground modules discovered</strong>
346
- <p>The shared host is ready, but nothing is exporting playground entries yet.</p>
469
+ <p>The shared host is ready, but nothing exports playground entries yet.</p>
347
470
  </div>
348
471
  {/if}
349
472
  </main>
@@ -356,234 +479,536 @@ function selectEntry(entry: ResolvedSmrtPlaygroundEntry) {
356
479
  min-height: 100vh;
357
480
  }
358
481
 
482
+ :global(*) {
483
+ box-sizing: border-box;
484
+ }
485
+
359
486
  .playground-shell {
360
487
  display: grid;
361
- grid-template-columns: 20rem minmax(0, 1fr);
488
+ grid-template-columns: 17rem minmax(0, 1fr);
362
489
  min-height: 100vh;
363
490
  background: var(--smrt-color-background);
364
491
  color: var(--smrt-color-on-background);
365
492
  font-family: var(--smrt-font-family);
366
493
  }
367
494
 
368
- .sidebar {
369
- display: grid;
370
- align-content: start;
371
- gap: 1.25rem;
372
- padding: 1.5rem;
495
+ button,
496
+ input {
497
+ font: inherit;
498
+ }
499
+
500
+ button {
501
+ color: inherit;
502
+ }
503
+
504
+ .catalog-nav {
505
+ position: sticky;
506
+ top: 0;
507
+ display: flex;
508
+ height: 100vh;
509
+ flex-direction: column;
510
+ gap: 1.4rem;
511
+ padding: 1.25rem;
512
+ overflow: hidden;
373
513
  border-right: 1px solid var(--smrt-color-outline-variant);
374
- background: var(--smrt-color-surface-container);
514
+ background: var(--smrt-color-surface-container-low);
375
515
  color: var(--smrt-color-on-surface);
376
- backdrop-filter: blur(18px);
377
516
  }
378
517
 
379
- .sidebar__header h1,
380
- .content__header h2,
381
- .preview-panel__meta h3 {
382
- margin: 0;
518
+ .brand {
519
+ display: flex;
520
+ min-width: 0;
521
+ align-items: center;
522
+ gap: 0.75rem;
383
523
  }
384
524
 
385
- .sidebar__header,
386
- .search,
387
- .module-list,
388
- .content,
389
- .content__body,
390
- .entry-list,
391
- .preview-panel {
525
+ .brand__mark {
392
526
  display: grid;
393
- gap: 0.9rem;
527
+ width: 2rem;
528
+ height: 2rem;
529
+ flex: 0 0 auto;
530
+ place-items: center;
531
+ border-radius: 0.55rem;
532
+ background: var(--smrt-color-primary);
533
+ color: var(--smrt-color-on-primary);
534
+ font-weight: 700;
394
535
  }
395
536
 
396
- .eyebrow {
397
- margin: 0 0 0.35rem;
398
- font-size: 0.75rem;
399
- letter-spacing: 0.12em;
400
- text-transform: uppercase;
401
- color: var(--smrt-color-primary);
537
+ .brand > div {
538
+ min-width: 0;
402
539
  }
403
540
 
404
- .sidebar__header p:last-child,
405
- .content__header p,
406
- .preview-panel__meta p,
407
- .empty-card p {
541
+ .brand h1 {
408
542
  margin: 0;
409
- line-height: 1.55;
543
+ overflow: hidden;
544
+ font-size: 0.9rem;
545
+ letter-spacing: 0.035em;
546
+ text-overflow: ellipsis;
547
+ white-space: nowrap;
410
548
  }
411
549
 
412
- .theme-controls {
413
- display: flex;
414
- align-items: flex-end;
415
- gap: 0.6rem;
416
- flex-wrap: wrap;
417
- }
418
-
419
- .theme-controls :global(.smrt-theme-switcher) {
420
- flex: 1;
550
+ .brand p {
551
+ margin: 0.12rem 0 0;
552
+ overflow: hidden;
553
+ color: var(--smrt-color-on-surface-variant);
554
+ font-size: 0.72rem;
555
+ text-overflow: ellipsis;
556
+ white-space: nowrap;
421
557
  }
422
558
 
423
- .theme-controls :global(.smrt-theme-switcher__select) {
424
- width: 100%;
559
+ .search-field {
560
+ display: grid;
561
+ gap: 0.4rem;
425
562
  }
426
563
 
427
- .search span {
428
- font-size: 0.82rem;
564
+ .search-field span {
429
565
  color: var(--smrt-color-on-surface-variant);
566
+ font-size: 0.72rem;
430
567
  }
431
568
 
432
- .search input {
569
+ .search-field input {
433
570
  width: 100%;
434
- box-sizing: border-box;
571
+ padding: 0.65rem 0.75rem;
435
572
  border: 1px solid var(--smrt-color-outline-variant);
436
- border-radius: 0.9rem;
437
- background: var(--smrt-color-surface-container-lowest);
573
+ border-radius: 0.55rem;
574
+ background: var(--smrt-color-surface);
438
575
  color: var(--smrt-color-on-surface);
439
- padding: 0.8rem 0.95rem;
440
576
  }
441
577
 
442
- .search input::placeholder {
443
- color: var(--smrt-color-on-surface-variant);
444
- opacity: 0.72;
578
+ .catalog-scroll {
579
+ flex: 1;
580
+ min-height: 0;
581
+ overflow-y: auto;
582
+ scrollbar-width: thin;
583
+ }
584
+
585
+ .catalog-scroll nav,
586
+ .foundation-catalog,
587
+ .nav-group,
588
+ .package-groups {
589
+ display: grid;
590
+ }
591
+
592
+ .catalog-scroll nav {
593
+ gap: 1.15rem;
445
594
  }
446
595
 
447
- .module-list button,
448
- .entry-list button,
449
- .mode-toggle button {
596
+ .foundation-catalog {
597
+ gap: 0.95rem;
598
+ }
599
+
600
+ .foundation-heading,
601
+ .nav-group button,
602
+ details button {
603
+ width: 100%;
450
604
  border: 0;
605
+ background: transparent;
606
+ text-align: left;
451
607
  cursor: pointer;
452
- transition:
453
- transform 140ms ease,
454
- background 140ms ease,
455
- border-color 140ms ease;
456
608
  }
457
609
 
458
- .module-list button,
459
- .entry-list button {
460
- display: grid;
461
- gap: 0.25rem;
462
- text-align: left;
463
- padding: 0.9rem 1rem;
464
- border-radius: 1rem;
610
+ .foundation-heading {
611
+ display: flex;
612
+ align-items: center;
613
+ justify-content: space-between;
614
+ padding: 0 0 0.7rem;
615
+ border-bottom: 1px solid var(--smrt-color-outline-variant);
616
+ font-size: 0.82rem;
617
+ font-weight: 650;
465
618
  }
466
619
 
467
- .module-list button {
468
- background: var(--smrt-color-surface-container-low);
469
- color: var(--smrt-color-on-surface);
470
- border: 1px solid transparent;
620
+ .foundation-heading small,
621
+ details summary small {
622
+ color: var(--smrt-color-on-surface-variant);
623
+ font-size: 0.7rem;
624
+ font-weight: 500;
471
625
  }
472
626
 
473
- .module-list button.selected,
474
- .module-list button:hover {
475
- border-color: var(--smrt-color-primary);
476
- background: var(--smrt-color-primary-container);
477
- color: var(--smrt-color-on-primary-container);
627
+ .nav-group {
628
+ gap: 0.12rem;
478
629
  }
479
630
 
480
- .module-list button span,
481
- .entry-list button span {
631
+ .nav-group h2,
632
+ .package-groups > h2 {
633
+ margin: 0 0 0.35rem;
634
+ color: var(--smrt-color-on-surface-variant);
635
+ font-size: 0.66rem;
636
+ font-weight: 600;
637
+ letter-spacing: 0.08em;
638
+ text-transform: uppercase;
639
+ }
640
+
641
+ .nav-group button,
642
+ details button {
643
+ padding: 0.52rem 0.6rem;
644
+ border-radius: 0.45rem;
482
645
  font-size: 0.82rem;
483
- opacity: 0.78;
484
646
  }
485
647
 
486
- .content {
487
- padding: 1.75rem;
488
- align-content: start;
648
+ .nav-group button:hover,
649
+ .nav-group button.active,
650
+ details button:hover,
651
+ details button.active {
652
+ background: var(--smrt-color-secondary-container);
653
+ color: var(--smrt-color-on-secondary-container);
489
654
  }
490
655
 
491
- .content__body {
492
- grid-template-columns: 18rem minmax(0, 1fr);
493
- align-items: start;
656
+ .package-groups {
657
+ gap: 0.15rem;
658
+ padding-top: 0.4rem;
494
659
  }
495
660
 
496
- .entry-list button {
497
- border: 1px solid var(--smrt-color-outline-variant);
498
- background: var(--smrt-color-surface);
499
- color: var(--smrt-color-on-surface);
500
- box-shadow: 0 18px 45px color-mix(in srgb, var(--smrt-color-shadow) 5%, transparent);
661
+ details summary {
662
+ display: flex;
663
+ align-items: center;
664
+ justify-content: space-between;
665
+ padding: 0.58rem 0.3rem;
666
+ cursor: pointer;
667
+ font-size: 0.82rem;
668
+ list-style: none;
501
669
  }
502
670
 
503
- .entry-list button.selected,
504
- .entry-list button:hover {
505
- transform: translateY(-1px);
506
- border-color: var(--smrt-color-primary);
507
- background: var(--smrt-color-primary-container);
508
- color: var(--smrt-color-on-primary-container);
509
- box-shadow: 0 18px 45px color-mix(in srgb, var(--smrt-color-primary) 14%, transparent);
671
+ details summary::-webkit-details-marker {
672
+ display: none;
510
673
  }
511
674
 
512
- .mode-pills {
675
+ details > div {
676
+ padding-left: 0.45rem;
677
+ }
678
+
679
+ .theme-controls {
513
680
  display: flex;
514
- flex-wrap: wrap;
515
- gap: 0.35rem;
681
+ align-items: end;
682
+ gap: 0.5rem;
683
+ padding-top: 0.8rem;
684
+ border-top: 1px solid var(--smrt-color-outline-variant);
516
685
  }
517
686
 
518
- .mode-pills span,
519
- .mode-toggle button {
520
- border-radius: 999px;
521
- padding: 0.25rem 0.6rem;
522
- font-size: 0.75rem;
523
- text-transform: uppercase;
524
- letter-spacing: 0.08em;
687
+ .theme-controls :global(.smrt-theme-switcher__label) {
688
+ display: none;
525
689
  }
526
690
 
527
- .mode-pills span {
528
- background: var(--smrt-color-primary-container);
529
- color: var(--smrt-color-on-primary-container);
691
+ .theme-controls :global(.smrt-theme-switcher__select) {
692
+ min-width: 7rem;
693
+ padding-block: 0.48rem;
530
694
  }
531
695
 
532
- .preview-panel,
533
- .empty-card {
534
- padding: 1.1rem;
535
- border-radius: 1.25rem;
536
- border: 1px solid var(--smrt-color-outline-variant);
537
- background: var(--smrt-color-surface);
538
- color: var(--smrt-color-on-surface);
539
- box-shadow: 0 24px 60px color-mix(in srgb, var(--smrt-color-shadow) 8%, transparent);
540
- backdrop-filter: blur(18px);
696
+ .workspace {
697
+ display: grid;
698
+ min-width: 0;
699
+ align-content: start;
700
+ gap: 1.25rem;
701
+ padding: 1.25rem clamp(1.25rem, 3vw, 3rem) 3rem;
541
702
  }
542
703
 
543
- .preview-panel__meta {
704
+ .workspace-header {
544
705
  display: flex;
545
- flex-wrap: wrap;
706
+ min-height: 2.75rem;
707
+ align-items: center;
546
708
  justify-content: space-between;
547
709
  gap: 1rem;
548
- align-items: start;
710
+ border-bottom: 1px solid var(--smrt-color-outline-variant);
711
+ }
712
+
713
+ .breadcrumbs {
714
+ display: flex;
715
+ min-width: 0;
716
+ gap: 0.55rem;
717
+ color: var(--smrt-color-on-surface-variant);
718
+ font-size: 0.76rem;
719
+ }
720
+
721
+ .breadcrumbs span,
722
+ .breadcrumbs strong {
723
+ overflow: hidden;
724
+ text-overflow: ellipsis;
725
+ white-space: nowrap;
549
726
  }
550
727
 
551
- .mode-toggle {
552
- display: inline-flex;
553
- gap: 0.45rem;
728
+ .breadcrumbs i {
729
+ opacity: 0.5;
730
+ }
731
+
732
+ .breadcrumbs strong {
733
+ color: var(--smrt-color-on-surface);
734
+ }
735
+
736
+ .mode-control {
737
+ display: flex;
738
+ flex: 0 0 auto;
739
+ gap: 0.25rem;
554
740
  padding: 0.2rem;
555
741
  border-radius: 999px;
556
- background: var(--smrt-color-surface-container-high);
742
+ background: var(--smrt-color-surface-container);
557
743
  }
558
744
 
559
- .mode-toggle button {
745
+ .mode-control button {
746
+ padding: 0.4rem 0.7rem;
747
+ border: 0;
748
+ border-radius: 999px;
560
749
  background: transparent;
561
750
  color: var(--smrt-color-on-surface-variant);
751
+ cursor: pointer;
752
+ font-size: 0.72rem;
753
+ text-transform: capitalize;
562
754
  }
563
755
 
564
- .mode-toggle button.active {
565
- background: var(--smrt-color-primary);
566
- color: var(--smrt-color-on-primary);
756
+ .mode-control button.active {
757
+ background: var(--smrt-color-surface);
758
+ color: var(--smrt-color-on-surface);
759
+ box-shadow: var(--smrt-elevation-1);
760
+ }
761
+
762
+ .package-landing {
763
+ width: min(68rem, 100%);
764
+ padding: clamp(1.5rem, 4vw, 3.5rem) 0 2rem;
765
+ }
766
+
767
+ .package-landing > header {
768
+ position: relative;
769
+ display: grid;
770
+ gap: 0.55rem;
771
+ padding-bottom: clamp(1.75rem, 4vw, 3rem);
772
+ border-bottom: 1px solid var(--smrt-color-outline-variant);
773
+ }
774
+
775
+ .package-landing > header p {
776
+ margin: 0;
777
+ color: var(--smrt-color-primary);
778
+ font-size: 0.68rem;
779
+ letter-spacing: 0.08em;
780
+ text-transform: none;
781
+ }
782
+
783
+ .package-landing > header h2 {
784
+ max-width: 48rem;
785
+ margin: 0;
786
+ font-size: clamp(2rem, 5vw, 4rem);
787
+ font-weight: 520;
788
+ letter-spacing: -0.045em;
789
+ line-height: 1;
790
+ }
791
+
792
+ .package-landing > header span {
793
+ max-width: 42rem;
794
+ color: var(--smrt-color-on-surface-variant);
795
+ line-height: 1.55;
796
+ }
797
+
798
+ .package-landing > header > small {
799
+ position: absolute;
800
+ right: 0;
801
+ bottom: 1rem;
802
+ color: var(--smrt-color-on-surface-variant);
803
+ font-size: 0.75rem;
804
+ }
805
+
806
+ .package-index {
807
+ padding-top: 2rem;
808
+ }
809
+
810
+ .package-index > h3 {
811
+ margin: 0 0 0.65rem;
812
+ color: var(--smrt-color-on-surface-variant);
813
+ font-size: 0.68rem;
814
+ font-weight: 600;
815
+ letter-spacing: 0.08em;
816
+ text-transform: uppercase;
817
+ }
818
+
819
+ .package-index > button {
820
+ display: grid;
821
+ width: 100%;
822
+ grid-template-columns: minmax(0, 1fr) auto 1.5rem;
823
+ align-items: center;
824
+ gap: 1.25rem;
825
+ padding: 1.15rem 0;
826
+ border: 0;
827
+ border-bottom: 1px solid var(--smrt-color-outline-variant);
828
+ background: transparent;
829
+ text-align: left;
830
+ cursor: pointer;
831
+ }
832
+
833
+ .package-index__copy {
834
+ display: grid;
835
+ min-width: 0;
836
+ gap: 0.25rem;
837
+ }
838
+
839
+ .package-index__copy strong {
840
+ font-size: 0.95rem;
841
+ font-weight: 600;
842
+ }
843
+
844
+ .package-index__copy small,
845
+ .package-index__modes {
846
+ color: var(--smrt-color-on-surface-variant);
847
+ font-size: 0.76rem;
848
+ line-height: 1.4;
849
+ }
850
+
851
+ .package-index__modes {
852
+ text-transform: capitalize;
853
+ }
854
+
855
+ .package-index__arrow {
856
+ color: var(--smrt-color-primary);
857
+ transition: transform 140ms ease;
858
+ }
859
+
860
+ .package-index > button:hover .package-index__copy strong,
861
+ .package-index > button:focus-visible .package-index__copy strong {
862
+ color: var(--smrt-color-primary);
863
+ }
864
+
865
+ .package-index > button:hover .package-index__arrow,
866
+ .package-index > button:focus-visible .package-index__arrow {
867
+ transform: translateX(0.2rem);
868
+ }
869
+
870
+ .preview-heading {
871
+ padding: 1rem 0 0.25rem;
872
+ }
873
+
874
+ .preview-heading p {
875
+ margin: 0 0 0.35rem;
876
+ color: var(--smrt-color-primary);
877
+ font-size: 0.68rem;
878
+ letter-spacing: 0.08em;
879
+ text-transform: none;
880
+ }
881
+
882
+ .preview-heading h2 {
883
+ margin: 0;
884
+ font-size: clamp(1.45rem, 2.6vw, 2.15rem);
885
+ font-weight: 550;
886
+ letter-spacing: -0.025em;
887
+ }
888
+
889
+ .preview-heading span {
890
+ display: block;
891
+ max-width: 48rem;
892
+ margin-top: 0.45rem;
893
+ color: var(--smrt-color-on-surface-variant);
894
+ line-height: 1.5;
895
+ }
896
+
897
+ .preview-heading .mode-description {
898
+ font-size: 0.85rem;
899
+ }
900
+
901
+ .preview-panel {
902
+ min-width: 0;
567
903
  }
568
904
 
569
905
  .preview-stage {
570
- padding: 1rem;
571
- border-radius: 1rem;
572
- border: 1px solid var(--smrt-color-outline-variant);
573
- background: var(--smrt-color-surface-container-lowest);
906
+ min-width: 0;
907
+ min-height: 30rem;
908
+ padding: clamp(1rem, 2.2vw, 2rem);
574
909
  overflow: auto;
575
- min-height: 24rem;
910
+ border: 1px solid var(--smrt-color-outline-variant);
911
+ border-radius: 1rem;
912
+ background: var(--smrt-color-surface);
913
+ color: var(--smrt-color-on-surface);
914
+ }
915
+
916
+ .preview-state {
917
+ display: grid;
918
+ min-height: 25rem;
919
+ place-content: center;
920
+ justify-items: center;
921
+ padding: 1rem;
922
+ color: var(--smrt-color-on-surface-variant);
923
+ text-align: center;
924
+ }
925
+
926
+ .preview-state p,
927
+ .empty-state p {
928
+ margin: 0.4rem 0 0;
929
+ line-height: 1.5;
576
930
  }
577
931
 
578
932
  .full-height {
579
933
  min-height: 60vh;
580
- align-content: center;
581
934
  }
582
935
 
583
- @media (max-width: 1040px) {
584
- .playground-shell,
585
- .content__body {
936
+ .empty-state {
937
+ padding: 1rem 0.25rem;
938
+ color: var(--smrt-color-on-surface-variant);
939
+ }
940
+
941
+ .loader {
942
+ width: 1.25rem;
943
+ height: 1.25rem;
944
+ border: 2px solid var(--smrt-color-outline-variant);
945
+ border-top-color: var(--smrt-color-primary);
946
+ border-radius: 50%;
947
+ animation: spin 0.8s linear infinite;
948
+ }
949
+
950
+ @keyframes spin {
951
+ to {
952
+ transform: rotate(360deg);
953
+ }
954
+ }
955
+
956
+ @media (max-width: 900px) {
957
+ .playground-shell {
586
958
  grid-template-columns: 1fr;
587
959
  }
960
+
961
+ .catalog-nav {
962
+ position: static;
963
+ height: auto;
964
+ border-right: 0;
965
+ border-bottom: 1px solid var(--smrt-color-outline-variant);
966
+ }
967
+
968
+ .catalog-scroll {
969
+ max-height: 18rem;
970
+ }
971
+
972
+ .catalog-scroll nav {
973
+ grid-template-columns: 1fr;
974
+ }
975
+ }
976
+
977
+ @media (max-width: 600px) {
978
+ .theme-controls :global(.smrt-theme-switcher) {
979
+ display: none;
980
+ }
981
+
982
+ .workspace-header {
983
+ align-items: flex-start;
984
+ }
985
+
986
+ .breadcrumbs {
987
+ display: grid;
988
+ gap: 0.15rem;
989
+ }
990
+
991
+ .breadcrumbs i {
992
+ display: none;
993
+ }
994
+
995
+ .package-landing > header > small {
996
+ position: static;
997
+ margin-top: 0.35rem;
998
+ }
999
+
1000
+ .package-index > button {
1001
+ grid-template-columns: minmax(0, 1fr) 1.5rem;
1002
+ }
1003
+
1004
+ .package-index__modes {
1005
+ display: none;
1006
+ }
1007
+ }
1008
+
1009
+ @media (prefers-reduced-motion: reduce) {
1010
+ .loader {
1011
+ animation: none;
1012
+ }
588
1013
  }
589
1014
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"PlaygroundHost.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/PlaygroundHost.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,KAAK,EAIV,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,KAAK;IACpB,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAsUD,QAAA,MAAM,cAAc,0BAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"PlaygroundHost.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/PlaygroundHost.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGxC,OAAO,KAAK,EAIV,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,KAAK;IACpB,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0bD,QAAA,MAAM,cAAc,0BAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-playground",
3
- "version": "0.38.26",
3
+ "version": "0.39.0",
4
4
  "description": "Shared playground discovery, runtime, and host components for SMRT UI packages",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,10 +26,21 @@
26
26
  "import": "./dist/svelte/index.js"
27
27
  }
28
28
  },
29
+ "scripts": {
30
+ "build": "vite build --mode library && svelte-package -i src/svelte -o dist/svelte --tsconfig tsconfig.svelte.json",
31
+ "build:watch": "vite build --mode library --watch",
32
+ "clean": "rm -rf dist",
33
+ "dev": "npm run build:watch",
34
+ "check": "pnpm exec svelte-check --tsconfig ./tsconfig.svelte.json",
35
+ "typecheck": "tsc --noEmit && node ../../scripts/svelte-check-a11y.mjs --tsconfig ./tsconfig.svelte.json",
36
+ "prepack": "node ../../scripts/prepack-package.js",
37
+ "test": "vitest run",
38
+ "verify:pack": "node ../../scripts/verify-package-types-exports.js ."
39
+ },
29
40
  "dependencies": {
41
+ "@happyvertical/smrt-ui": "workspace:*",
30
42
  "fast-glob": "3.3.3",
31
- "tsx": "^4.23.0",
32
- "@happyvertical/smrt-ui": "0.38.26"
43
+ "tsx": "^4.23.0"
33
44
  },
34
45
  "peerDependencies": {
35
46
  "svelte": "^5.56.4",
@@ -62,15 +73,5 @@
62
73
  "type": "git",
63
74
  "url": "https://github.com/happyvertical/smrt.git",
64
75
  "directory": "packages/smrt-playground"
65
- },
66
- "scripts": {
67
- "build": "vite build --mode library && svelte-package -i src/svelte -o dist/svelte --tsconfig tsconfig.svelte.json",
68
- "build:watch": "vite build --mode library --watch",
69
- "clean": "rm -rf dist",
70
- "dev": "npm run build:watch",
71
- "check": "pnpm exec svelte-check --tsconfig ./tsconfig.svelte.json",
72
- "typecheck": "tsc --noEmit && node ../../scripts/svelte-check-a11y.mjs --tsconfig ./tsconfig.svelte.json",
73
- "test": "vitest run",
74
- "verify:pack": "node ../../scripts/verify-package-types-exports.js ."
75
76
  }
76
- }
77
+ }
package/LICENSE DELETED
@@ -1,7 +0,0 @@
1
- Copyright <2025> <Happy Vertical Corporation>
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
-
5
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
-
7
- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.