@picked/svelte 0.0.45-next.1 → 0.0.45-next.3

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.
Files changed (44) hide show
  1. package/dist/plugins/devtools/DevToolsExperience.svelte +61 -44
  2. package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +2 -1
  3. package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +22 -20
  4. package/dist/plugins/devtools/components/Tabs.svelte +20 -7
  5. package/dist/plugins/devtools/experience.js +1 -1
  6. package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +18 -18
  7. package/dist/plugins/devtools/screens/events/EventListItem.svelte +28 -42
  8. package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +10 -11
  9. package/dist/plugins/devtools/screens/events/EventsTable.svelte +31 -33
  10. package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +13 -16
  11. package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +20 -71
  12. package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +96 -232
  13. package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +14 -16
  14. package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +18 -16
  15. package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +14 -16
  16. package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +25 -63
  17. package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +43 -64
  18. package/dist/plugins/devtools/ui/actions/Button.svelte +171 -0
  19. package/dist/plugins/devtools/ui/actions/CopyButton.svelte +88 -0
  20. package/dist/plugins/devtools/ui/data/Badge.svelte +62 -0
  21. package/dist/plugins/devtools/ui/data/CodeBlock.svelte +38 -0
  22. package/dist/plugins/devtools/ui/data/DataTable.svelte +134 -0
  23. package/dist/plugins/devtools/ui/data/EmptyState.svelte +26 -0
  24. package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +66 -0
  25. package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +55 -0
  26. package/dist/plugins/devtools/ui/feedback/Banner.svelte +116 -0
  27. package/dist/plugins/devtools/ui/feedback/Spinner.svelte +79 -0
  28. package/dist/plugins/devtools/ui/forms/Checkbox.svelte +139 -0
  29. package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +218 -0
  30. package/dist/plugins/devtools/ui/forms/Select.svelte +137 -0
  31. package/dist/plugins/devtools/ui/forms/Switch.svelte +158 -0
  32. package/dist/plugins/devtools/ui/forms/TextField.svelte +125 -0
  33. package/dist/plugins/devtools/ui/index.d.ts +22 -0
  34. package/dist/plugins/devtools/ui/index.js +1 -0
  35. package/dist/plugins/devtools/ui/layout/Card.svelte +55 -0
  36. package/dist/plugins/devtools/ui/layout/Divider.svelte +34 -0
  37. package/dist/plugins/devtools/ui/layout/Spacer.svelte +11 -0
  38. package/dist/plugins/devtools/ui/layout/Stack.svelte +129 -0
  39. package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +90 -0
  40. package/dist/plugins/devtools/ui/tokens.css +144 -0
  41. package/dist/plugins/devtools/ui/tokens.css.js +1 -0
  42. package/dist/plugins/devtools/ui/typography/Heading.svelte +55 -0
  43. package/dist/plugins/devtools/ui/typography/Text.svelte +93 -0
  44. package/package.json +5 -2
@@ -1,5 +1,13 @@
1
+ <!--
2
+ @paired-component apps/dashboard/src/shared/ui/layout/page.tsx
3
+ @design-parity The devtools modal layout mirrors the Dashboard Page component.
4
+ The toolbar, main area, padding, and border radius must stay visually identical
5
+ to the Dashboard's AppShell content area. When modifying this layout, replicate
6
+ the change in the paired React component to maintain visual and behavioral parity.
7
+ -->
1
8
  <script lang="ts">
2
9
  import { getExperience } from '../../hooks'
10
+ import { Button } from './ui'
3
11
  import PickedLogo from './PickedLogo.svelte'
4
12
  import Tabs from './components/Tabs.svelte'
5
13
  import ExperiencesListScreen from './screens/experiences/ExperiencesListScreen.svelte'
@@ -91,9 +99,14 @@
91
99
  >
92
100
  <div class="backdrop"></div>
93
101
  <div class="container">
94
- <header class="header" id="devtools-header">
95
- <PickedLogo />
96
- <button on:click={() => experience.close()}>Close</button>
102
+ <header class="toolbar" id="devtools-header">
103
+ <div class="toolbar-left">
104
+ <PickedLogo width={80} height={28} />
105
+ </div>
106
+ <div class="toolbar-center"></div>
107
+ <div class="toolbar-right">
108
+ <Button variant="ghost" size="small" onclick={() => experience.close()}>Close</Button>
109
+ </div>
97
110
  </header>
98
111
  <section class="main">
99
112
  <Tabs {tabs} initialTab={1} />
@@ -103,7 +116,7 @@
103
116
  {:else}
104
117
  <button
105
118
  class="floating-button"
106
- on:click={() => experience.open()}
119
+ onclick={() => experience.open()}
107
120
  aria-label="Open Picked Devtools"
108
121
  title="Open Picked Devtools (or press .)"
109
122
  >
@@ -114,71 +127,74 @@
114
127
  <style>
115
128
  *,
116
129
  .devtools * {
117
- box-sizing: inherit;
130
+ box-sizing: border-box;
118
131
  }
119
132
 
120
133
  .devtools {
121
134
  position: fixed;
122
- top: 0;
123
- left: 0;
124
- width: 100%;
125
- height: 100%;
135
+ inset: 0;
126
136
  z-index: 999999999;
127
- padding: 2em;
137
+ padding: 48px;
128
138
  display: grid;
129
139
  place-items: center;
130
140
  overflow: hidden;
131
- font-size: 13px;
132
- color: black;
133
- line-height: 1;
134
- }
135
-
136
- .devtools button {
137
- font-size: 13px;
138
- color: black;
139
- line-height: 1;
140
141
  }
141
142
 
142
143
  .backdrop {
143
144
  position: fixed;
144
- top: 0;
145
- left: 0;
146
- width: 100%;
147
- height: 100%;
145
+ inset: 0;
148
146
  background-color: rgba(0, 0, 0, 0.5);
149
147
  z-index: 1;
150
148
  }
151
149
 
152
150
  .container {
153
- background-color: white;
151
+ background-color: var(--color-white);
152
+ border-radius: var(--radius-xl);
153
+ box-shadow: var(--shadow-lg);
154
154
  display: flex;
155
155
  flex-direction: column;
156
- padding: 0.5rem;
157
- border-radius: 4px;
158
- box-shadow: 4px 4px 4px #00000029;
159
- border: 1px solid lightgray;
156
+ overflow: hidden;
160
157
  z-index: 2;
161
- overflow: auto;
162
158
  width: 100%;
163
159
  height: 100%;
164
160
  }
165
161
 
166
- .header {
167
- height: 50px;
168
- border-bottom: 1px solid lightgray;
162
+ .toolbar {
163
+ height: 40px;
164
+ background-color: var(--color-white);
165
+ border-bottom: 1px solid var(--color-gray-200);
166
+ padding: 0 var(--space-4);
169
167
  display: flex;
170
168
  align-items: center;
171
- padding: 0.5em;
172
169
  justify-content: space-between;
170
+ flex-shrink: 0;
171
+ }
172
+
173
+ .toolbar-left {
174
+ display: flex;
175
+ align-items: center;
176
+ }
177
+
178
+ .toolbar-center {
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: center;
182
+ }
183
+
184
+ .toolbar-right {
185
+ display: flex;
186
+ align-items: center;
173
187
  }
174
188
 
175
189
  .main {
176
- flex: 1 1 auto;
177
- display: grid;
190
+ flex: 1;
191
+ background-color: var(--color-gray-50);
192
+ overflow: auto;
193
+ display: flex;
178
194
  flex-direction: column;
179
- height: 100%;
180
195
  }
181
196
 
197
+ /* Global table styles for screens that still use raw tables */
182
198
  .devtools :global(table) {
183
199
  width: 100%;
184
200
  border-collapse: collapse;
@@ -186,15 +202,16 @@
186
202
 
187
203
  .devtools :global(th),
188
204
  .devtools :global(td) {
189
- padding: 0.5rem;
205
+ padding: var(--space-2);
190
206
  text-align: left;
191
- border-bottom: 1px solid lightgray;
207
+ border-bottom: 1px solid var(--color-gray-200);
208
+ font-size: var(--text-sm);
209
+ color: var(--color-gray-900);
192
210
  }
193
211
 
194
212
  .devtools :global(th) {
195
- padding: 0.5rem;
196
- font-weight: normal;
197
- background-color: #f0f0f0;
213
+ font-weight: var(--font-medium);
214
+ background-color: var(--color-gray-50);
198
215
  }
199
216
 
200
217
  .floating-button {
@@ -203,9 +220,9 @@
203
220
  right: 1.5rem;
204
221
  width: 4.5rem;
205
222
  height: 4.5rem;
206
- border-radius: 50%;
207
- background-color: white;
208
- border: 1px solid lightgray;
223
+ border-radius: var(--radius-full);
224
+ background-color: var(--color-white);
225
+ border: 1px solid var(--color-gray-200);
209
226
  box-shadow: 0 4px 12px rgba(21, 73, 255, 0.2);
210
227
  display: flex;
211
228
  align-items: center;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { ExperienceInstance } from '@picked/client'
3
+ import { Button } from '../ui'
3
4
 
4
5
  export let experienceInstance: ExperienceInstance
5
6
 
@@ -15,7 +16,7 @@
15
16
 
16
17
  <tr>
17
18
  <td>{experienceInstance.experienceId}</td>
18
- <td><button onclick={() => experienceInstance.toggleOpen()}>{$isOpen}</button></td>
19
+ <td><Button variant="ghost" size="small" onclick={() => experienceInstance.toggleOpen()}>{$isOpen}</Button></td>
19
20
  <td>{$currentNode?.id || ''}</td>
20
21
  <td>{$currentNode?.type || ''}</td>
21
22
  <td>{$currentStep?.id || ''}</td>
@@ -1,27 +1,29 @@
1
1
  <script lang="ts">
2
2
  import type { ExperienceInstance } from '@picked/client'
3
3
  import ExperienceInstanceListItem from './ExperienceInstanceListItem.svelte'
4
+ import { DataTable, EmptyState } from '../ui'
4
5
 
5
6
  export let experienceInstances: ExperienceInstance[]
7
+
8
+ const columns = [
9
+ { key: 'experience', header: 'Experience' },
10
+ { key: 'isOpen', header: 'Is Open' },
11
+ { key: 'currentNodeId', header: 'Current node ID' },
12
+ { key: 'currentNodeType', header: 'Current node Type' },
13
+ { key: 'currentStepId', header: 'Current step ID' },
14
+ { key: 'currentStepTitle', header: 'Current step Title' },
15
+ { key: 'nextSteps', header: 'Next reachable steps amount' },
16
+ { key: 'stepsToOrigin', header: 'Steps to origin' },
17
+ { key: 'stepsToEnd', header: 'Steps to end' },
18
+ ]
6
19
  </script>
7
20
 
8
- <table>
9
- <thead>
10
- <tr>
11
- <th>Experience</th>
12
- <th>Is Open</th>
13
- <th>Current node ID</th>
14
- <th>Current node Type</th>
15
- <th>Current step ID</th>
16
- <th>Current step Title</th>
17
- <th>Next reachable steps amount</th>
18
- <th>Steps to origin</th>
19
- <th>Steps to end</th>
20
- </tr>
21
- </thead>
22
- <tbody>
23
- {#each experienceInstances as experienceInstance (experienceInstance.id)}
24
- <ExperienceInstanceListItem {experienceInstance} />
25
- {/each}
26
- </tbody>
27
- </table>
21
+ <DataTable columns={columns} rows={experienceInstances}>
22
+ {#snippet row(item)}
23
+ {@const experienceInstance = item}
24
+ <ExperienceInstanceListItem {experienceInstance} />
25
+ {/snippet}
26
+ {#snippet empty()}
27
+ <EmptyState message="No experience instances" />
28
+ {/snippet}
29
+ </DataTable>
@@ -82,37 +82,50 @@
82
82
  width: 100%;
83
83
  display: flex;
84
84
  flex-direction: column;
85
+ flex: 1;
85
86
  }
86
87
 
87
88
  .tab-list {
88
89
  display: flex;
89
- border-bottom: 1px solid #ccc;
90
+ border-bottom: 1px solid var(--color-gray-200);
91
+ flex-shrink: 0;
92
+ overflow-x: auto;
90
93
  }
91
94
 
92
95
  .tab-button {
93
- padding: 0.5rem 1rem;
96
+ padding: var(--space-2) var(--space-4);
94
97
  border: none;
95
98
  background: none;
96
99
  cursor: pointer;
97
100
  border-bottom: 2px solid transparent;
101
+ font-size: var(--text-sm);
102
+ color: var(--color-gray-600);
103
+ font-family: inherit;
104
+ white-space: nowrap;
105
+ transition: color 0.15s ease, border-color 0.15s ease;
98
106
  }
99
107
 
100
108
  .tab-button:hover {
101
- background-color: #f0f0f0;
109
+ background-color: var(--color-gray-100);
110
+ color: var(--color-gray-900);
102
111
  }
103
112
 
104
113
  .tab-button.active {
105
- border-bottom: 2px solid #000;
114
+ border-bottom-color: var(--color-gray-900);
115
+ color: var(--color-gray-900);
116
+ font-weight: var(--font-medium);
106
117
  }
107
118
 
108
- .tab-button:focus {
109
- outline: 2px solid #0066cc;
110
- outline-offset: -2px;
119
+ .tab-button:focus-visible {
120
+ outline: var(--ring-width) solid var(--ring-color);
121
+ outline-offset: calc(-1 * var(--ring-width));
111
122
  }
112
123
 
113
124
  .tab-panel {
114
125
  flex: 1 1 auto;
115
126
  display: grid;
127
+ align-content: start;
128
+ overflow: auto;
116
129
  }
117
130
 
118
131
  .tab-panel:not(.active) {
@@ -1 +1 @@
1
- import{createExperience as e}from"@picked/client/plugins/create-experience";import{adaptSvelteComponent as o}from"../../component.js";import t from"./DevToolsExperience.svelte";const p=e({id:"svelte-devtools",name:"Picked Devtools",decide:{},targets:[{selector:"body",placement:"append"}],component:o(t)});p.addStep({label:"Tools"});export{p as devtoolsExperience};
1
+ import{createExperience as e}from"@picked/client/plugins/create-experience";import{adaptSvelteComponent as o}from"../../component.js";import t from"./DevToolsExperience.svelte";import p from"./ui/tokens.css.js";const r=e({id:"svelte-devtools",name:"Picked Devtools",decide:{},targets:[{selector:"body",placement:"append"}],component:o(t,{css:[p]})});r.addStep({label:"Tools"});export{r as devtoolsExperience};
@@ -1,26 +1,26 @@
1
1
  <script lang="ts">
2
2
  import { getPicked } from '../../../../hooks'
3
+ import { DataTable, EmptyState } from '../../ui'
3
4
 
4
5
  const client = getPicked()
5
6
 
6
7
  $: components = client.components.listStore
8
+
9
+ const columns = [
10
+ { key: 'name', header: 'Name' },
11
+ { key: 'framework', header: 'Framework' },
12
+ ]
7
13
  </script>
8
14
 
9
- <div>
10
- <table>
11
- <thead>
12
- <tr>
13
- <th>Name</th>
14
- <th>Framework</th>
15
- </tr>
16
- </thead>
17
- <tbody>
18
- {#each $components as component (component.name)}
19
- <tr>
20
- <td>{component.name}</td>
21
- <td>{component.component.frameworkName}</td>
22
- </tr>
23
- {/each}
24
- </tbody>
25
- </table>
26
- </div>
15
+ <DataTable columns={columns} rows={$components}>
16
+ {#snippet row(item)}
17
+ {@const component = item}
18
+ <tr>
19
+ <td>{component.name}</td>
20
+ <td>{component.component.frameworkName}</td>
21
+ </tr>
22
+ {/snippet}
23
+ {#snippet empty()}
24
+ <EmptyState message="No components" />
25
+ {/snippet}
26
+ </DataTable>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { EventBusPayload, EventHistoryEntry } from '@picked/client'
3
+ import { Badge, Card, CodeBlock, Stack, Text } from '../../ui'
3
4
 
4
5
  export let event: EventHistoryEntry
5
6
  export let isExpanded: boolean
@@ -23,15 +24,12 @@
23
24
  function getPayloadFields(payload: EventBusPayload): PayloadField[] {
24
25
  const fields: PayloadField[] = []
25
26
 
26
- // Add type
27
27
  fields.push({ label: 'Type', value: payload.type })
28
28
 
29
- // Add optional value field if present
30
29
  if ('value' in payload && payload.value !== undefined) {
31
30
  fields.push({ label: 'Value', value: payload.value })
32
31
  }
33
32
 
34
- // Add experience identifiers if present
35
33
  if ('experience' in payload && payload.experience) {
36
34
  const exp = payload.experience
37
35
  if (exp && typeof exp === 'object') {
@@ -44,7 +42,6 @@
44
42
  }
45
43
  }
46
44
 
47
- // Add step information if present
48
45
  if ('step' in payload && payload.step) {
49
46
  const step = payload.step
50
47
  if (step && typeof step === 'object') {
@@ -57,27 +54,22 @@
57
54
  }
58
55
  }
59
56
 
60
- // Add form information if present
61
57
  if ('form' in payload && payload.form) {
62
58
  fields.push({ label: 'Form', value: JSON.stringify(payload.form) })
63
59
  }
64
60
 
65
- // Add feature information if present
66
61
  if ('feature' in payload && payload.feature) {
67
62
  fields.push({ label: 'Feature', value: JSON.stringify(payload.feature) })
68
63
  }
69
64
 
70
- // Add exception/validation errors if present
71
65
  if ('exception' in payload && payload.exception) {
72
66
  fields.push({ label: 'Exception', value: JSON.stringify(payload.exception) })
73
67
  }
74
68
 
75
- // Add version if present
76
69
  if ('version' in payload && payload.version) {
77
70
  fields.push({ label: 'Version', value: payload.version })
78
71
  }
79
72
 
80
- // Add session id if present
81
73
  if ('id' in payload && payload.id && payload.type === 'session') {
82
74
  fields.push({ label: 'Session ID', value: payload.id })
83
75
  }
@@ -94,49 +86,43 @@
94
86
  }
95
87
  </script>
96
88
 
97
- <tr on:click={onToggle} style="cursor: pointer;">
98
- <td style="font-family: monospace; font-size: 12px;">
99
- <span style="margin-right: 0.5rem; display: inline-block; width: 12px;">
89
+ <tr class="event-row" onclick={onToggle}>
90
+ <td>
91
+ <span class="expand-icon">
100
92
  {isExpanded ? '▼' : '▶'}
101
93
  </span>
102
- {formatTime(event.at)}
94
+ <CodeBlock code={formatTime(event.at)} inline />
103
95
  </td>
104
96
  <td>
105
- <code
106
- style="display: inline-block; padding: 0.25rem 0.5rem; background-color: #e8f4ff; border: 1px solid #b3d9ff; border-radius: 3px; font-size: 12px; color: #0066cc;"
107
- >
108
- {event.payload.type}
109
- </code>
97
+ <Badge status="info">{event.payload.type}</Badge>
110
98
  </td>
111
- <td style="font-size: 13px; color: #333;">
112
- {'experience' in event.payload && event.payload.experience ? event.payload.experience.experienceId : '-'}
99
+ <td>
100
+ <Text size="small">
101
+ {'experience' in event.payload && event.payload.experience ? event.payload.experience.experienceId : '-'}
102
+ </Text>
113
103
  </td>
114
- <td style="font-size: 12px; color: #666; font-style: italic;">
115
- {isExpanded ? 'Click to collapse' : 'Click to expand payload'}
104
+ <td>
105
+ <Text size="caption" color="subdued">
106
+ {isExpanded ? 'Click to collapse' : 'Click to expand payload'}
107
+ </Text>
116
108
  </td>
117
109
  </tr>
118
110
  {#if isExpanded}
119
111
  <tr>
120
- <td colspan="4" style="background-color: #f8f9fa; padding: 1rem; border-left: 3px solid #0066cc;">
121
- <div style="font-weight: 600; margin-bottom: 0.5rem; font-size: 13px; color: #333;">
122
- Event Payload
123
- </div>
124
- <div
125
- style="background-color: #fff; padding: 0.75rem; border: 1px solid #e0e0e0; border-radius: 4px;"
126
- >
127
- {#each getPayloadFields(event.payload) as field}
128
- <div style="margin-bottom: 0.5rem; font-size: 12px;">
129
- <span style="font-weight: 600; color: #555; margin-right: 0.5rem;"
130
- >{field.label}:</span
131
- >
132
- <code
133
- style="font-family: 'Monaco', 'Menlo', 'Courier New', monospace; background-color: #f0f0f0; padding: 0.125rem 0.25rem; border-radius: 2px; color: #333;"
134
- >
135
- {formatValue(field.value)}
136
- </code>
137
- </div>
138
- {/each}
139
- </div>
112
+ <td colspan="4" class="details-cell">
113
+ <Stack direction="vertical" gap="small">
114
+ <Text size="small" weight="semibold">Event Payload</Text>
115
+ <Card>
116
+ <Stack direction="vertical" gap="small">
117
+ {#each getPayloadFields(event.payload) as field}
118
+ <Text size="caption" inline>
119
+ <Text size="caption" weight="semibold" color="subdued" inline>{field.label}:</Text>
120
+ <CodeBlock code={formatValue(field.value)} inline />
121
+ </Text>
122
+ {/each}
123
+ </Stack>
124
+ </Card>
125
+ </Stack>
140
126
  </td>
141
127
  </tr>
142
128
  {/if}
@@ -1,19 +1,18 @@
1
1
  <script lang="ts">
2
2
  import { getPicked } from '../../../../hooks'
3
3
  import EventsTable from './EventsTable.svelte'
4
+ import { Divider, Spacer, Stack, Text } from '../../ui'
4
5
 
5
6
  const { events } = getPicked()
6
7
  </script>
7
8
 
8
- <div style="display: flex; flex-direction: column; height: 100%;">
9
+ <Stack direction="vertical" gap="none" fill>
9
10
  <EventsTable />
10
- <div
11
- style="margin-top: auto; padding: 0.5rem; display: flex; justify-content: space-between; align-items: center; border-top: 1px solid lightgray;"
12
- >
13
- <div style="display: flex; gap: 0.5rem; align-items: center;">
14
- <span style="font-size: 13px; color: #666;">
15
- {$events.length} events
16
- </span>
17
- </div>
18
- </div>
19
- </div>
11
+ <Spacer />
12
+ <Divider />
13
+ <Stack padding="small">
14
+ <Text size="caption" color="subdued">
15
+ {$events.length} events
16
+ </Text>
17
+ </Stack>
18
+ </Stack>
@@ -2,6 +2,7 @@
2
2
  import { combine, createStore, createEvent, sample } from 'effector'
3
3
  import { getPicked } from '../../../../hooks'
4
4
  import EventListItem from './EventListItem.svelte'
5
+ import { DataTable, EmptyState } from '../../ui'
5
6
  import type { EventHistoryEntry } from '@picked/client'
6
7
 
7
8
  const { events } = getPicked()
@@ -18,7 +19,6 @@
18
19
  expandedRowIds = newSet
19
20
  }
20
21
 
21
-
22
22
  const sortDirectionStore = createStore<'asc' | 'desc'>('desc')
23
23
  const toggleSortDirection = createEvent<void>()
24
24
  const oppositeSortDirection: Record<'asc' | 'desc', 'asc' | 'desc'> = { asc: 'desc', desc: 'asc' }
@@ -38,37 +38,35 @@
38
38
  }
39
39
 
40
40
  const sortedEvents = combine(events, sortDirectionStore, sortEvents)
41
+
42
+ const columns = [
43
+ { key: 'time', header: 'Time', sortable: true },
44
+ { key: 'eventType', header: 'Event Type' },
45
+ { key: 'experience', header: 'Experience' },
46
+ { key: 'actions', header: '' },
47
+ ]
48
+
49
+ function handleSort() {
50
+ toggleSortDirection()
51
+ }
41
52
  </script>
42
53
 
43
- <table>
44
- <thead>
45
- <tr>
46
- <th
47
- style="width: 140px; cursor: pointer; user-select: none;"
48
- on:click={() => toggleSortDirection()}
49
- >
50
- Time {$sortDirectionStore === 'asc' ? '↑' : '↓'}
51
- </th>
52
- <th style="width: 200px;">Event Type</th>
53
- <th style="width: 180px;">Experience</th>
54
- <th></th>
55
- </tr>
56
- </thead>
57
- <tbody>
58
- {#if $sortedEvents.length === 0}
59
- <tr>
60
- <td colspan="4" style="text-align: center; padding: 2rem; color: #666;">
61
- No events yet. Interact with the page to see events.
62
- </td>
63
- </tr>
64
- {:else}
65
- {#each $sortedEvents as event (event.at)}
66
- <EventListItem
67
- {event}
68
- isExpanded={expandedRowIds.has(event.id)}
69
- onToggle={() => toggleRow(event.id)}
70
- />
71
- {/each}
72
- {/if}
73
- </tbody>
74
- </table>
54
+ <DataTable
55
+ columns={columns}
56
+ rows={$sortedEvents}
57
+ sortKey="time"
58
+ sortDirection={$sortDirectionStore}
59
+ onSort={handleSort}
60
+ >
61
+ {#snippet row(item)}
62
+ {@const event = item}
63
+ <EventListItem
64
+ {event}
65
+ isExpanded={expandedRowIds.has(event.id)}
66
+ onToggle={() => toggleRow(event.id)}
67
+ />
68
+ {/snippet}
69
+ {#snippet empty()}
70
+ <EmptyState message="No events yet. Interact with the page to see events." />
71
+ {/snippet}
72
+ </DataTable>
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { ExperienceStore } from '@picked/client'
3
3
  import { getPicked } from '../../../../hooks'
4
+ import { Button, Text, CodeBlock, Stack } from '../../ui'
4
5
 
5
6
  const client = getPicked()
6
7
  export let experience: ExperienceStore
@@ -8,39 +9,35 @@
8
9
  $: config = experience.configStore
9
10
  </script>
10
11
 
11
- <div class="container">
12
+ <Stack direction="vertical" gap="medium" padding="medium">
12
13
  <div>
13
- <button on:click={() => client.launchExperienceById(experience.id)}>Launch experience</button>
14
+ <Button variant="primary" size="small" onclick={() => client.launchExperienceById(experience.id)}>
15
+ Launch experience
16
+ </Button>
14
17
  </div>
15
18
 
16
19
  <div>
17
- <p><b>Type</b></p>
18
- <p>{$config.type}</p>
20
+ <Text weight="semibold">Type</Text>
21
+ <Text>{$config.type}</Text>
19
22
  </div>
20
23
 
21
24
  <div>
22
- <p><b>Target selectors</b></p>
25
+ <Text weight="semibold">Target selectors</Text>
23
26
  <ul>
24
27
  {#if $config.targetSelectors.length > 0}
25
28
  {#each $config.targetSelectors as selector (selector)}
26
29
  <li>
27
- <pre>{selector.selector} || {selector.placement}</pre>
30
+ <CodeBlock code="{selector.selector} || {selector.placement}" />
28
31
  </li>
29
32
  {/each}
30
33
  {:else}
31
- <li>(none)</li>
34
+ <li><Text color="subdued">(none)</Text></li>
32
35
  {/if}
33
36
  </ul>
34
37
  </div>
35
38
 
36
39
  <div>
37
- <p><b>Decision logic</b></p>
38
- <pre>{JSON.stringify($config.decisionLogic, null, 2)}</pre>
40
+ <Text weight="semibold">Decision logic</Text>
41
+ <CodeBlock code={JSON.stringify($config.decisionLogic, null, 2)} />
39
42
  </div>
40
- </div>
41
-
42
- <style>
43
- .container {
44
- padding: 0.5em;
45
- }
46
- </style>
43
+ </Stack>