@picked/svelte 0.0.1-dev.1789541668638 → 0.0.1-next.2

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 (132) hide show
  1. package/dist/component.d.ts +5 -5
  2. package/dist/component.js +1 -1
  3. package/dist/context.js +1 -1
  4. package/dist/form/index.d.ts +15 -3
  5. package/dist/form/index.js +1 -1
  6. package/dist/hooks/client.d.ts +1 -1
  7. package/dist/hooks/client.js +1 -1
  8. package/dist/hooks/field.d.ts +5 -16
  9. package/dist/hooks/field.js +1 -1
  10. package/dist/hooks/index.d.ts +1 -9
  11. package/dist/hooks/index.js +1 -1
  12. package/dist/hooks/step.d.ts +15 -93
  13. package/dist/hooks/step.js +1 -1
  14. package/dist/hooks/storage.d.ts +2 -2
  15. package/dist/hooks/storage.js +1 -1
  16. package/dist/hooks/stores.d.ts +1 -1
  17. package/dist/hooks/stores.js +1 -1
  18. package/dist/index.d.ts +1 -8
  19. package/dist/index.js +1 -1
  20. package/dist/plugins/devtools/DevToolsExperience.svelte +53 -355
  21. package/dist/plugins/devtools/PickedLogo.svelte +58 -0
  22. package/dist/plugins/devtools/components/ExperienceInstanceListItem.svelte +4 -17
  23. package/dist/plugins/devtools/components/ExperienceInstancesTable.svelte +19 -26
  24. package/dist/plugins/devtools/components/Tabs.svelte +12 -69
  25. package/dist/plugins/devtools/experience.d.ts +1 -0
  26. package/dist/plugins/devtools/experience.js +1 -0
  27. package/dist/plugins/devtools/index.d.ts +0 -2
  28. package/dist/plugins/devtools/index.js +1 -1
  29. package/dist/plugins/devtools/plugin.d.ts +5 -37
  30. package/dist/plugins/devtools/plugin.js +1 -1
  31. package/dist/plugins/devtools/screens/components/ComponentsListScreen.svelte +14 -18
  32. package/dist/plugins/devtools/screens/experience-instances/ExperienceInstancesListScreen.svelte +3 -1
  33. package/dist/plugins/devtools/screens/experiences/ExperienceDetailsScreen.svelte +33 -31
  34. package/dist/plugins/devtools/screens/experiences/ExperienceExperienceInstancesListScreen.svelte +3 -1
  35. package/dist/plugins/devtools/screens/experiences/ExperienceTreeScreen.svelte +103 -293
  36. package/dist/plugins/devtools/screens/experiences/ExperiencesListScreen.svelte +1 -2
  37. package/dist/plugins/devtools/screens/frameworks/FrameworksListScreen.svelte +13 -15
  38. package/dist/plugins/devtools/screens/history/HistoryEntriesTable.svelte +16 -22
  39. package/dist/plugins/devtools/screens/history/HistoryEntryListItem.svelte +1 -7
  40. package/dist/plugins/devtools/screens/predicates/PredicatesListScreen.svelte +13 -15
  41. package/dist/plugins/devtools/screens/project/ProjectScreen.svelte +1 -46
  42. package/dist/plugins/framework/index.js +1 -1
  43. package/dist/plugins/framework/plugin.d.ts +4 -4
  44. package/dist/plugins/framework/plugin.js +1 -1
  45. package/dist/render/browser.d.ts +2 -35
  46. package/dist/render/browser.js +1 -1
  47. package/dist/render/server.d.ts +8 -22
  48. package/dist/render/server.js +1 -1
  49. package/dist/router/Step.svelte +4 -6
  50. package/dist/router/StepRouter.svelte +34 -14
  51. package/dist/router/context.d.ts +3 -3
  52. package/dist/router/context.js +1 -1
  53. package/dist/router/index.js +1 -1
  54. package/dist/server/index.d.ts +2 -2
  55. package/dist/server/index.js +1 -1
  56. package/dist/utils/array.js +1 -1
  57. package/dist/vite-env.d.js +1 -0
  58. package/package.json +33 -106
  59. package/dist/.tsbuildinfo +0 -1
  60. package/dist/_virtual/_vite/preload-helper.js +0 -1
  61. package/dist/components/LazyImage.svelte +0 -192
  62. package/dist/components/index.d.ts +0 -1
  63. package/dist/components/index.js +0 -1
  64. package/dist/experience/ExperienceHost.svelte +0 -112
  65. package/dist/experience/ExperienceSlot.svelte +0 -154
  66. package/dist/experience/HtmlRender.svelte +0 -52
  67. package/dist/experience/set-picked-context.d.ts +0 -29
  68. package/dist/experience/set-picked-context.js +0 -1
  69. package/dist/form/__tests__/CreateFormHarness.svelte +0 -30
  70. package/dist/form/create-form.js +0 -1
  71. package/dist/form/create-form.svelte.d.ts +0 -40
  72. package/dist/form/create-form.svelte.js +0 -1
  73. package/dist/form/get-form.d.ts +0 -30
  74. package/dist/form/get-form.js +0 -1
  75. package/dist/hooks/action.d.ts +0 -40
  76. package/dist/hooks/action.js +0 -1
  77. package/dist/hooks/content.d.ts +0 -18
  78. package/dist/hooks/content.js +0 -1
  79. package/dist/hooks/create-query/index.d.ts +0 -145
  80. package/dist/hooks/create-query/index.js +0 -1
  81. package/dist/hooks/events.d.ts +0 -2
  82. package/dist/hooks/events.js +0 -1
  83. package/dist/hooks/feature.d.ts +0 -47
  84. package/dist/hooks/feature.js +0 -1
  85. package/dist/hooks/predicate.d.ts +0 -3
  86. package/dist/hooks/predicate.js +0 -1
  87. package/dist/hooks/reference.d.ts +0 -61
  88. package/dist/hooks/reference.js +0 -1
  89. package/dist/plugins/devtools/PickedIcon.svelte +0 -19
  90. package/dist/plugins/devtools/components/AdaptedToolHost.svelte +0 -103
  91. package/dist/plugins/devtools/customTools.d.ts +0 -27
  92. package/dist/plugins/devtools/customTools.js +0 -1
  93. package/dist/plugins/devtools/screens/config/ConfigScreen.svelte +0 -160
  94. package/dist/plugins/devtools/screens/content/ContentListScreen.svelte +0 -89
  95. package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +0 -29
  96. package/dist/plugins/devtools/screens/events/EventListItem.svelte +0 -216
  97. package/dist/plugins/devtools/screens/events/EventsListScreen.svelte +0 -18
  98. package/dist/plugins/devtools/screens/events/EventsTable.svelte +0 -76
  99. package/dist/plugins/devtools/screens/features/FeaturesListScreen.svelte +0 -239
  100. package/dist/plugins/devtools/screens/fields/FieldTableRow.svelte +0 -276
  101. package/dist/plugins/devtools/screens/fields/FieldsListScreen.svelte +0 -12
  102. package/dist/plugins/devtools/screens/fields/FieldsTable.svelte +0 -78
  103. package/dist/plugins/devtools/screens/identity/IdentityScreen.svelte +0 -66
  104. package/dist/plugins/devtools/screens/responses/ResponseTableRow.svelte +0 -280
  105. package/dist/plugins/devtools/screens/responses/ResponsesListScreen.svelte +0 -12
  106. package/dist/plugins/devtools/screens/responses/ResponsesTable.svelte +0 -88
  107. package/dist/plugins/devtools/ui/actions/Button.svelte +0 -175
  108. package/dist/plugins/devtools/ui/actions/CopyButton.svelte +0 -88
  109. package/dist/plugins/devtools/ui/data/Badge.svelte +0 -62
  110. package/dist/plugins/devtools/ui/data/CodeBlock.svelte +0 -38
  111. package/dist/plugins/devtools/ui/data/DataTable.svelte +0 -144
  112. package/dist/plugins/devtools/ui/data/EmptyState.svelte +0 -26
  113. package/dist/plugins/devtools/ui/data/ExpandableRow.svelte +0 -66
  114. package/dist/plugins/devtools/ui/data/VisualizationContainer.svelte +0 -55
  115. package/dist/plugins/devtools/ui/feedback/Banner.svelte +0 -116
  116. package/dist/plugins/devtools/ui/feedback/Spinner.svelte +0 -79
  117. package/dist/plugins/devtools/ui/forms/Checkbox.svelte +0 -139
  118. package/dist/plugins/devtools/ui/forms/RadioGroup.svelte +0 -218
  119. package/dist/plugins/devtools/ui/forms/Select.svelte +0 -137
  120. package/dist/plugins/devtools/ui/forms/Switch.svelte +0 -158
  121. package/dist/plugins/devtools/ui/forms/TextField.svelte +0 -125
  122. package/dist/plugins/devtools/ui/index.d.ts +0 -22
  123. package/dist/plugins/devtools/ui/index.js +0 -1
  124. package/dist/plugins/devtools/ui/layout/Card.svelte +0 -55
  125. package/dist/plugins/devtools/ui/layout/Divider.svelte +0 -34
  126. package/dist/plugins/devtools/ui/layout/Spacer.svelte +0 -11
  127. package/dist/plugins/devtools/ui/layout/Stack.svelte +0 -129
  128. package/dist/plugins/devtools/ui/overlays/Tooltip.svelte +0 -90
  129. package/dist/plugins/devtools/ui/tokens.css +0 -144
  130. package/dist/plugins/devtools/ui/typography/Heading.svelte +0 -55
  131. package/dist/plugins/devtools/ui/typography/Text.svelte +0 -93
  132. package/dist/utils/ErrorBoundary.svelte +0 -27
@@ -1,280 +0,0 @@
1
- <script lang="ts">
2
- /** @file Editable table row for a single field response in the devtools responses screen. */
3
- import type { ResponseListItem, Field, FieldValue, JsonValue } from '@picked/client'
4
- import { getPicked } from '../../../../hooks'
5
- import { Button, Text, CodeBlock, Banner, Stack } from '../../ui'
6
-
7
- export let field: Field
8
- export let response: ResponseListItem | null
9
-
10
- const client = getPicked()
11
-
12
- let isEditing = false
13
- let editValue = ''
14
- let editError = ''
15
- // eslint-disable-next-line svelte/prefer-svelte-reactivity -- Set is reassigned for Svelte reactivity via `selectedOptions = selectedOptions`
16
- let selectedOptions: Set<JsonValue> = new Set()
17
-
18
- $: hasOptions = Array.isArray(field?.options) && field.options.length > 0
19
- $: staticOptions = Array.isArray(field?.options) ? field.options : null
20
- $: currentValue = response?.value ?? null
21
-
22
- /**
23
- * Formats a response value for display, handling arrays, objects, and primitives.
24
- *
25
- * @param {unknown} value - The response value to format.
26
- * @returns {string} The display string for the value.
27
- * @example
28
- * ```typescript
29
- * formatValue(['a', 'b']) // 'a, b'
30
- * formatValue(null) // '-'
31
- * ```
32
- */
33
- function formatValue(value: unknown): string {
34
- if (value === null || value === undefined) {
35
- return '-'
36
- }
37
-
38
- if (Array.isArray(value) && value.every((item) => typeof item === 'string')) {
39
- return value.join(', ')
40
- }
41
-
42
- if (typeof value === 'object') {
43
- return JSON.stringify(value, null, 2)
44
- }
45
-
46
- return String(value)
47
- }
48
-
49
- /**
50
- * Enters edit mode for this field, initializing the edit state from the current value.
51
- *
52
- * @example
53
- * ```typescript
54
- * startEdit() // opens the inline editor
55
- * ```
56
- */
57
- function startEdit() {
58
- isEditing = true
59
- editError = ''
60
-
61
- if (hasOptions) {
62
- selectedOptions = new Set()
63
- if (Array.isArray(currentValue)) {
64
- currentValue.forEach((v) => selectedOptions.add(v))
65
- } else if (currentValue !== null && currentValue !== undefined) {
66
- selectedOptions.add(currentValue)
67
- }
68
- } else {
69
- editValue = formatValue(currentValue)
70
- }
71
- }
72
-
73
- /**
74
- * Cancels the current edit, discarding changes and resetting edit state.
75
- *
76
- * @example
77
- * ```typescript
78
- * cancelEdit() // closes editor without saving
79
- * ```
80
- */
81
- function cancelEdit() {
82
- isEditing = false
83
- editValue = ''
84
- editError = ''
85
- selectedOptions = new Set()
86
- }
87
-
88
- /**
89
- * Toggles selection of a field option, supporting both single and multi-select modes.
90
- *
91
- * @param {JsonValue} optionValue - The option value to toggle.
92
- * @example
93
- * ```typescript
94
- * toggleOption('red') // selects or deselects the 'red' option
95
- * ```
96
- */
97
- function toggleOption(optionValue: JsonValue) {
98
- if (field?.multiple) {
99
- if (selectedOptions.has(optionValue)) {
100
- selectedOptions.delete(optionValue)
101
- } else {
102
- selectedOptions.add(optionValue)
103
- }
104
-
105
- selectedOptions = selectedOptions
106
- } else {
107
- selectedOptions = new Set([optionValue])
108
- }
109
- }
110
-
111
- /**
112
- * Parses a string input into a typed value based on the field's type.
113
- *
114
- * @param {string} input - The raw string input to parse.
115
- * @returns {FieldValue} The parsed value (null, number, boolean, JSON, or string).
116
- * @example
117
- * ```typescript
118
- * parseValue('42') // 42 (when field.type is 'number')
119
- * parseValue('true') // true (when field.type is 'boolean')
120
- * ```
121
- */
122
- function parseValue(input: string): FieldValue {
123
- const trimmed = input.trim()
124
-
125
- if (trimmed === '' || trimmed === '-') {
126
- return null
127
- }
128
-
129
- if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
130
- try {
131
- return JSON.parse(trimmed)
132
- } catch {
133
- throw new Error('Invalid JSON format')
134
- }
135
- }
136
-
137
- if (field.type === 'boolean') {
138
- if (trimmed === 'true') return true
139
- if (trimmed === 'false') return false
140
- throw new Error('Boolean must be "true" or "false"')
141
- }
142
-
143
- if (field.type === 'number') {
144
- const num = Number(trimmed)
145
- if (isNaN(num)) {
146
- throw new Error('Invalid number format')
147
- }
148
-
149
- return num
150
- }
151
-
152
- return trimmed
153
- }
154
-
155
- /**
156
- * Commits the edited value via the client responses API and exits edit mode.
157
- *
158
- * @example
159
- * ```typescript
160
- * await saveEdit() // saves the current edit value
161
- * ```
162
- */
163
- async function saveEdit() {
164
- try {
165
- let newValue: FieldValue
166
-
167
- if (hasOptions) {
168
- const values = Array.from(selectedOptions)
169
- if (field?.multiple) {
170
- newValue = values
171
- } else {
172
- newValue = values.at(0) ?? null
173
- }
174
- } else {
175
- newValue = parseValue(editValue)
176
- }
177
-
178
- const result = await client.responses.commit({ [field.key]: newValue })
179
-
180
- if (result.errors.length > 0) {
181
- editError = result.errors[0].userMessage
182
- return
183
- }
184
-
185
- isEditing = false
186
- editError = ''
187
- selectedOptions = new Set()
188
- } catch (error) {
189
- editError = error instanceof Error ? error.message : 'Failed to save'
190
- }
191
- }
192
-
193
- /**
194
- * Clears the response value for this field via the client responses API.
195
- *
196
- * @example
197
- * ```typescript
198
- * handleClear() // removes the response for this field
199
- * ```
200
- */
201
- function handleClear() {
202
- client.responses.clear(field.key)
203
- }
204
- </script>
205
-
206
- <tr>
207
- <td>
208
- <CodeBlock code={field.key} inline />
209
- </td>
210
- <td>
211
- <Text size="small" weight="semibold">{field.label}</Text>
212
- </td>
213
- <td>
214
- {#if isEditing}
215
- <Stack direction="vertical" gap="small">
216
- {#if hasOptions && staticOptions}
217
- <Stack direction="vertical" gap="small">
218
- {#each staticOptions as option (option.id)}
219
- <label class="option-label">
220
- {#if field.multiple}
221
- <input
222
- type="checkbox"
223
- checked={selectedOptions.has(option.value)}
224
- on:change={() => toggleOption(option.value)}
225
- />
226
- {:else}
227
- <input
228
- type="radio"
229
- name="field-{field.id}"
230
- checked={selectedOptions.has(option.value)}
231
- on:change={() => toggleOption(option.value)}
232
- />
233
- {/if}
234
- <Text size="caption" inline>
235
- {option.label}
236
- {#if option.description}
237
- <Text size="caption" color="subdued" inline>({option.description})</Text>
238
- {/if}
239
- </Text>
240
- </label>
241
- {/each}
242
- </Stack>
243
- {:else}
244
- <textarea
245
- bind:value={editValue}
246
- rows="3"
247
- placeholder="Enter {field.type} value..."
248
- ></textarea>
249
- {/if}
250
- {#if editError}
251
- <Banner status="error">{editError}</Banner>
252
- {/if}
253
- </Stack>
254
- {:else if response === null}
255
- <Text size="caption" color="subdued">No value set</Text>
256
- {:else}
257
- <CodeBlock code={formatValue(currentValue)} />
258
- {/if}
259
- </td>
260
- <td>
261
- <CodeBlock code="{field.type}{field.multiple ? '[]' : ''}" inline />
262
- </td>
263
- <td>
264
- {#if isEditing}
265
- <Stack direction="horizontal" gap="none" align="end">
266
- <Button variant="primary" size="small" onclick={saveEdit}>Save</Button>
267
- <Button variant="destructive" size="small" onclick={cancelEdit}>Cancel</Button>
268
- </Stack>
269
- {:else}
270
- <Stack direction="horizontal" gap="none" align="end">
271
- <Button variant="secondary" size="small" onclick={startEdit}>
272
- {response === null ? 'Set' : 'Edit'}
273
- </Button>
274
- {#if response !== null}
275
- <Button variant="destructive" size="small" onclick={handleClear}>Clear</Button>
276
- {/if}
277
- </Stack>
278
- {/if}
279
- </td>
280
- </tr>
@@ -1,12 +0,0 @@
1
- <script lang="ts">
2
- import { getPicked } from '../../../../hooks'
3
- import ResponsesTable from './ResponsesTable.svelte'
4
-
5
- const client = getPicked()
6
-
7
- $: responsesStore = client.responses.listStore
8
- </script>
9
-
10
- <div>
11
- <ResponsesTable responses={$responsesStore} />
12
- </div>
@@ -1,88 +0,0 @@
1
- <script lang="ts">
2
- /** @file Table displaying all field responses with filtering and clear-all functionality in the devtools panel. */
3
- import type { ResponseListItem } from '@picked/client'
4
- import { getPicked } from '../../../../hooks'
5
- import { Card, DataTable, EmptyState, Button, RadioGroup, Stack } from '../../ui'
6
- import ResponseTableRow from './ResponseTableRow.svelte'
7
-
8
- export let responses: ResponseListItem[] = []
9
-
10
- const client = getPicked()
11
-
12
- let filter = 'set'
13
-
14
- const fieldsStore = client.fields.listStore
15
- $: allFields = $fieldsStore ?? []
16
-
17
- $: responseMap = new Map(responses.map((r) => [r.fieldKey, r]))
18
-
19
- $: allFieldRows = allFields.map((field) => {
20
- const response = responseMap.get(field.key)
21
- return {
22
- field,
23
- response: response || null,
24
- }
25
- })
26
-
27
- $: fieldRows = allFieldRows.filter((row) => {
28
- if (filter === 'set') return row.response !== null
29
- if (filter === 'unset') return row.response === null
30
- return true
31
- })
32
-
33
- /**
34
- * Clears all committed field responses via the client responses API.
35
- *
36
- * @example
37
- * ```typescript
38
- * handleClearAll() // removes all responses
39
- * ```
40
- */
41
- function handleClearAll() {
42
- client.responses.clearAll()
43
- }
44
-
45
- const columns = [
46
- { key: 'key', header: 'Key' },
47
- { key: 'field', header: 'Field' },
48
- { key: 'value', header: 'Value' },
49
- { key: 'type', header: 'Type' },
50
- { key: 'actions', header: 'Actions' },
51
- ]
52
-
53
- const filterOptions = [
54
- { value: 'all', label: 'All' },
55
- { value: 'set', label: 'Set (0)' },
56
- { value: 'unset', label: 'Unset (0)' },
57
- ]
58
-
59
- $: filterOptions[1] = { value: 'set', label: `Set (${responses.length})` }
60
- $: filterOptions[2] = { value: 'unset', label: `Unset (${allFields.length - responses.length})` }
61
- </script>
62
-
63
- <Stack padding="medium">
64
- <Card sectioned>
65
- <Stack direction="horizontal" gap="small" align="space-between" justify="center" padding="medium">
66
- <RadioGroup
67
- label="Show"
68
- name="filter"
69
- options={filterOptions}
70
- value={filter}
71
- onchange={(val) => { filter = val }}
72
- orientation="horizontal"
73
- />
74
- {#if responses.length > 0}
75
- <Button variant="destructive" size="small" onclick={handleClearAll}>Clear All</Button>
76
- {/if}
77
- </Stack>
78
- <DataTable columns={columns} rows={fieldRows}>
79
- {#snippet row(item)}
80
- {@const rowData = item}
81
- <ResponseTableRow field={rowData.field} response={rowData.response} />
82
- {/snippet}
83
- {#snippet empty()}
84
- <EmptyState message="No fields registered yet" />
85
- {/snippet}
86
- </DataTable>
87
- </Card>
88
- </Stack>
@@ -1,175 +0,0 @@
1
- <!--
2
- @paired-component apps/dashboard/src/shared/ui/actions/button.tsx
3
- @design-parity Props, variants, sizes, and visual output must stay in sync
4
- with the Dashboard React counterpart. When modifying this component, replicate
5
- the change in the paired React component to maintain visual and behavioral parity.
6
- -->
7
- <script lang="ts">
8
- import type { Snippet } from 'svelte'
9
- import Spinner from '../feedback/Spinner.svelte'
10
-
11
- interface Props {
12
- children: Snippet
13
- icon?: Snippet
14
- variant?: 'primary' | 'secondary' | 'destructive' | 'ghost'
15
- size?: 'small' | 'medium' | 'large'
16
- disabled?: boolean
17
- loading?: boolean
18
- type?: 'button' | 'submit' | 'reset'
19
- fullWidth?: boolean
20
- href?: string
21
- target?: string
22
- onclick?: (event: MouseEvent) => void
23
- }
24
-
25
- let {
26
- children,
27
- icon,
28
- variant = 'primary',
29
- size = 'medium',
30
- disabled = false,
31
- loading = false,
32
- type = 'button',
33
- fullWidth = false,
34
- href,
35
- target,
36
- onclick,
37
- }: Props = $props()
38
-
39
- const isDisabled = $derived(disabled || loading)
40
- </script>
41
-
42
- {#if href}
43
- <a
44
- class="button variant-{variant} size-{size}"
45
- class:full-width={fullWidth}
46
- class:is-disabled={isDisabled}
47
- {href}
48
- {target}
49
- rel={target === '_blank' ? 'noopener noreferrer' : undefined}
50
- aria-disabled={isDisabled || undefined}
51
- tabindex={isDisabled ? -1 : undefined}
52
- onclick={isDisabled
53
- ? (e: MouseEvent) => e.preventDefault()
54
- : onclick}
55
- >
56
- {#if loading}
57
- <Spinner size="small" color="current" label="Loading" />
58
- {:else if icon}
59
- {@render icon()}
60
- {/if}
61
- {@render children()}
62
- </a>
63
- {:else}
64
- <button
65
- class="button variant-{variant} size-{size}"
66
- class:full-width={fullWidth}
67
- {type}
68
- disabled={isDisabled}
69
- {onclick}
70
- >
71
- {#if loading}
72
- <Spinner size="small" color="current" label="Loading" />
73
- {:else if icon}
74
- {@render icon()}
75
- {/if}
76
- {@render children()}
77
- </button>
78
- {/if}
79
-
80
- <style>
81
- .button {
82
- position: relative;
83
- display: inline-flex;
84
- cursor: pointer;
85
- align-items: center;
86
- justify-content: center;
87
- gap: var(--space-2);
88
- border-radius: var(--radius-md);
89
- font-weight: var(--font-medium);
90
- font-family: inherit;
91
- line-height: 1.5;
92
- border: none;
93
- text-decoration: none;
94
- transition: background-color 0.15s ease, color 0.15s ease;
95
- }
96
-
97
- .button:focus-visible {
98
- outline: var(--ring-width) solid var(--ring-color);
99
- outline-offset: var(--ring-offset);
100
- }
101
-
102
- /* Variants */
103
- .variant-primary {
104
- background-color: var(--color-blue-600);
105
- color: var(--color-white);
106
- }
107
- .variant-primary:hover:not(:disabled):not(.is-disabled) {
108
- background-color: var(--color-blue-700);
109
- }
110
- .variant-primary:active:not(:disabled):not(.is-disabled) {
111
- background-color: var(--color-blue-800);
112
- }
113
-
114
- .variant-secondary {
115
- background-color: var(--color-gray-100);
116
- color: var(--color-gray-900);
117
- }
118
- .variant-secondary:hover:not(:disabled):not(.is-disabled) {
119
- background-color: var(--color-gray-200);
120
- }
121
- .variant-secondary:active:not(:disabled):not(.is-disabled) {
122
- background-color: var(--color-gray-300);
123
- }
124
-
125
- .variant-destructive {
126
- background-color: var(--color-red-600);
127
- color: var(--color-white);
128
- }
129
- .variant-destructive:hover:not(:disabled):not(.is-disabled) {
130
- background-color: var(--color-red-700);
131
- }
132
- .variant-destructive:active:not(:disabled):not(.is-disabled) {
133
- background-color: var(--color-red-800);
134
- }
135
-
136
- .variant-ghost {
137
- background-color: transparent;
138
- color: var(--color-gray-700);
139
- }
140
- .variant-ghost:hover:not(:disabled):not(.is-disabled) {
141
- background-color: var(--color-gray-100);
142
- }
143
- .variant-ghost:active:not(:disabled):not(.is-disabled) {
144
- background-color: var(--color-gray-200);
145
- }
146
-
147
- /* Sizes */
148
- .size-small {
149
- padding: var(--space-1) var(--space-2-5);
150
- font-size: var(--text-sm);
151
- }
152
- .size-medium {
153
- padding: var(--space-1-5) var(--space-3);
154
- font-size: var(--text-sm);
155
- }
156
- .size-large {
157
- padding: var(--space-2) var(--space-5);
158
- font-size: var(--text-base);
159
- }
160
-
161
- /* States */
162
- .button:disabled,
163
- .is-disabled {
164
- cursor: not-allowed;
165
- opacity: 0.5;
166
- }
167
-
168
- a.is-disabled {
169
- pointer-events: none;
170
- }
171
-
172
- .full-width {
173
- width: 100%;
174
- }
175
- </style>
@@ -1,88 +0,0 @@
1
- <!--
2
- @paired-component apps/dashboard/src/shared/ui/actions/copy-button.tsx
3
- @design-parity Props, variants, sizes, and visual output must stay in sync
4
- with the Dashboard React counterpart. When modifying this component, replicate
5
- the change in the paired React component to maintain visual and behavioral parity.
6
- -->
7
- <script lang="ts">
8
- interface Props {
9
- text: string
10
- label?: string
11
- }
12
-
13
- let { text, label = 'Copy to clipboard' }: Props = $props()
14
-
15
- let copied = $state(false)
16
- let timeoutId: ReturnType<typeof setTimeout> | undefined
17
-
18
- async function handleCopy(): Promise<void> {
19
- try {
20
- await navigator.clipboard.writeText(text)
21
- copied = true
22
- clearTimeout(timeoutId)
23
- timeoutId = setTimeout(() => {
24
- copied = false
25
- }, 1500)
26
- } catch {
27
- // Clipboard API may not be available
28
- }
29
- }
30
- </script>
31
-
32
- <button
33
- class="copy-button"
34
- onclick={handleCopy}
35
- aria-label={label}
36
- >
37
- {#if copied}
38
- <svg class="icon icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
39
- <polyline points="20 6 9 17 4 12" />
40
- </svg>
41
- <span class="copied-text" aria-live="polite">Copied!</span>
42
- {:else}
43
- <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
44
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
45
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
46
- </svg>
47
- {/if}
48
- </button>
49
-
50
- <style>
51
- .copy-button {
52
- display: inline-flex;
53
- align-items: center;
54
- gap: var(--space-1);
55
- border-radius: var(--radius-sm);
56
- padding: var(--space-1) var(--space-2);
57
- font-size: var(--text-sm);
58
- font-family: inherit;
59
- color: var(--color-gray-600);
60
- background: none;
61
- border: none;
62
- cursor: pointer;
63
- transition: background-color 0.15s ease, color 0.15s ease;
64
- }
65
-
66
- .copy-button:hover {
67
- background-color: var(--color-gray-100);
68
- color: var(--color-gray-900);
69
- }
70
-
71
- .copy-button:focus-visible {
72
- outline: var(--ring-width) solid var(--ring-color);
73
- outline-offset: var(--ring-offset);
74
- }
75
-
76
- .icon {
77
- width: 1rem;
78
- height: 1rem;
79
- }
80
-
81
- .icon-check {
82
- color: var(--color-green-700);
83
- }
84
-
85
- .copied-text {
86
- color: var(--color-green-700);
87
- }
88
- </style>
@@ -1,62 +0,0 @@
1
- <!--
2
- @paired-component apps/dashboard/src/shared/ui/data/badge.tsx
3
- @design-parity Props, variants, sizes, and visual output must stay in sync
4
- with the Dashboard React counterpart. When modifying this component, replicate
5
- the change in the paired React component to maintain visual and behavioral parity.
6
- -->
7
- <script lang="ts">
8
- import type { Snippet } from 'svelte'
9
-
10
- interface Props {
11
- children: Snippet
12
- status?: 'default' | 'success' | 'warning' | 'error' | 'info'
13
- size?: 'small' | 'medium'
14
- }
15
-
16
- let { children, status = 'default', size = 'medium' }: Props = $props()
17
- </script>
18
-
19
- <span class="badge status-{status} size-{size}">
20
- {@render children()}
21
- </span>
22
-
23
- <style>
24
- .badge {
25
- display: inline-flex;
26
- align-items: center;
27
- border-radius: var(--radius-full);
28
- font-weight: var(--font-medium);
29
- }
30
-
31
- /* Sizes */
32
- .size-small {
33
- padding: 1px var(--space-1-5);
34
- font-size: 10px;
35
- }
36
- .size-medium {
37
- padding: var(--space-0-5) var(--space-2);
38
- font-size: var(--text-xs);
39
- }
40
-
41
- /* Status colors */
42
- .status-default {
43
- background-color: var(--color-gray-100);
44
- color: var(--color-gray-700);
45
- }
46
- .status-success {
47
- background-color: var(--color-green-100);
48
- color: var(--color-green-800);
49
- }
50
- .status-warning {
51
- background-color: var(--color-yellow-100);
52
- color: var(--color-yellow-800);
53
- }
54
- .status-error {
55
- background-color: #fee2e2;
56
- color: var(--color-red-800, #991b1b);
57
- }
58
- .status-info {
59
- background-color: #dbeafe;
60
- color: var(--color-blue-800, #1e40af);
61
- }
62
- </style>