dr-widget 0.1.3__py3-none-any.whl
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.
- dr_widget/__init__.py +5 -0
- dr_widget/py.typed +0 -0
- dr_widget/widgets/__init__.py +5 -0
- dr_widget/widgets/config_file_manager/.gitignore +24 -0
- dr_widget/widgets/config_file_manager/.vscode/extensions.json +3 -0
- dr_widget/widgets/config_file_manager/README.md +89 -0
- dr_widget/widgets/config_file_manager/__init__.py +283 -0
- dr_widget/widgets/config_file_manager/components.json +16 -0
- dr_widget/widgets/config_file_manager/index.html +12 -0
- dr_widget/widgets/config_file_manager/jsrepo.json +18 -0
- dr_widget/widgets/config_file_manager/package.json +49 -0
- dr_widget/widgets/config_file_manager/postcss.config.js +6 -0
- dr_widget/widgets/config_file_manager/public/fonts/Inter-roman.var.woff2 +0 -0
- dr_widget/widgets/config_file_manager/public/vite.svg +1 -0
- dr_widget/widgets/config_file_manager/src/App.svelte +62 -0
- dr_widget/widgets/config_file_manager/src/ConfigFileManager.svelte +605 -0
- dr_widget/widgets/config_file_manager/src/app.css +134 -0
- dr_widget/widgets/config_file_manager/src/index.js +5 -0
- dr_widget/widgets/config_file_manager/src/lib/@test_state.json +20 -0
- dr_widget/widgets/config_file_manager/src/lib/Counter.svelte +10 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/BrowseConfigsPanel.svelte +137 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/ComplexJsonViewer.svelte +94 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/ConfigViewerPanel.svelte +282 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/LoadedConfigPreview.svelte +74 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SaveConfigPanel.svelte +449 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SelectedFileRow.svelte +38 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SelectedFilesList.svelte +30 -0
- dr_widget/widgets/config_file_manager/src/lib/components/file-drop/SimpleJsonViewer.svelte +405 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/badge/badge.svelte +50 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/badge/index.ts +2 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/button/button.svelte +128 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/button/index.ts +27 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-action.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-content.svelte +15 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-description.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-footer.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-header.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/card.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/card/index.ts +25 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-close.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-content.svelte +47 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-description.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-footer.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-header.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-overlay.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-title.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/dialog/index.ts +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-close.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-content.svelte +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-description.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-footer.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-header.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-nested.svelte +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-overlay.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-title.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer-trigger.svelte +11 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/drawer.svelte +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/drawer/index.ts +45 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-content.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-description.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-header.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-media.svelte +41 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/empty.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/empty/index.ts +22 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-content.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-description.svelte +25 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-error.svelte +58 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-group.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-label.svelte +26 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-legend.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-separator.svelte +38 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-set.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field-title.svelte +23 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/field.svelte +53 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/field/index.ts +33 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/file-drop-zone.svelte +178 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/index.ts +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/file-drop-zone/types.ts +51 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/index.ts +34 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-actions.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-content.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-description.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-footer.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-group.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-header.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-media.svelte +42 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-separator.svelte +19 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/item/item.svelte +60 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/label/index.ts +7 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/label/label.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/index.ts +13 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-content.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-description.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-footer.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-header.svelte +29 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-title.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal-trigger.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal.svelte +24 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/modal/modal.svelte.ts +32 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/separator/index.ts +7 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/separator/separator.svelte +21 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/index.ts +16 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-list.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs-trigger.svelte +20 -0
- dr_widget/widgets/config_file_manager/src/lib/components/ui/tabs/tabs.svelte +19 -0
- dr_widget/widgets/config_file_manager/src/lib/hooks/use-file-bindings.ts +189 -0
- dr_widget/widgets/config_file_manager/src/lib/react/JsonTreeCanvas.tsx +207 -0
- dr_widget/widgets/config_file_manager/src/lib/utils/config-format.ts +113 -0
- dr_widget/widgets/config_file_manager/src/lib/utils/utils.ts +21 -0
- dr_widget/widgets/config_file_manager/src/lib/utils.ts +17 -0
- dr_widget/widgets/config_file_manager/src/main.js +7 -0
- dr_widget/widgets/config_file_manager/static/fonts/Inter-roman.var.woff2 +0 -0
- dr_widget/widgets/config_file_manager/static/index.js +9719 -0
- dr_widget/widgets/config_file_manager/static/style.css +1 -0
- dr_widget/widgets/config_file_manager/static/vite.svg +1 -0
- dr_widget/widgets/config_file_manager/svelte.config.js +8 -0
- dr_widget/widgets/config_file_manager/tailwind.config.js +12 -0
- dr_widget/widgets/config_file_manager/tsconfig.json +28 -0
- dr_widget/widgets/config_file_manager/vite.config.js +36 -0
- dr_widget-0.1.3.dist-info/METADATA +62 -0
- dr_widget-0.1.3.dist-info/RECORD +127 -0
- dr_widget-0.1.3.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import SimpleJsonViewer from './SimpleJsonViewer.svelte';
|
|
3
|
+
|
|
4
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
5
|
+
type DiffStatus = "added" | "removed" | "changed" | "unchanged";
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
data,
|
|
9
|
+
baseline,
|
|
10
|
+
dirty = false,
|
|
11
|
+
diffContext = "unchanged",
|
|
12
|
+
depth = 2,
|
|
13
|
+
currentDepth = 0,
|
|
14
|
+
isLast = true,
|
|
15
|
+
preserveKeyOrder = false,
|
|
16
|
+
} = $props<{
|
|
17
|
+
data?: unknown;
|
|
18
|
+
baseline?: unknown;
|
|
19
|
+
dirty?: boolean;
|
|
20
|
+
diffContext?: DiffStatus;
|
|
21
|
+
depth?: number;
|
|
22
|
+
currentDepth?: number;
|
|
23
|
+
isLast?: boolean;
|
|
24
|
+
preserveKeyOrder?: boolean;
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
let items = $state<string[]>([]);
|
|
28
|
+
let isArray = $state(false);
|
|
29
|
+
let brackets = $state<[string, string]>(["{", "}"]);
|
|
30
|
+
let collapsed = $state(false);
|
|
31
|
+
let diffMeta = $state<
|
|
32
|
+
Record<
|
|
33
|
+
string,
|
|
34
|
+
{
|
|
35
|
+
status: DiffStatus;
|
|
36
|
+
hasCurrent: boolean;
|
|
37
|
+
hasPrevious: boolean;
|
|
38
|
+
currentValue: unknown;
|
|
39
|
+
previousValue: unknown;
|
|
40
|
+
}
|
|
41
|
+
>
|
|
42
|
+
>({});
|
|
43
|
+
|
|
44
|
+
const getType = (value: unknown): string => {
|
|
45
|
+
if (value === null) return "null";
|
|
46
|
+
return typeof value;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const isObjectLike = (value: unknown): value is Record<string, unknown> =>
|
|
50
|
+
typeof value === "object" && value !== null;
|
|
51
|
+
|
|
52
|
+
const deepEqual = (a: unknown, b: unknown): boolean => {
|
|
53
|
+
if (a === b) return true;
|
|
54
|
+
|
|
55
|
+
if (typeof a !== typeof b) return false;
|
|
56
|
+
|
|
57
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
58
|
+
if (a.length !== b.length) return false;
|
|
59
|
+
return a.every((item, index) => deepEqual(item, b[index]));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (isObjectLike(a) && isObjectLike(b)) {
|
|
63
|
+
const keysA = Object.keys(a);
|
|
64
|
+
const keysB = Object.keys(b);
|
|
65
|
+
if (keysA.length !== keysB.length) return false;
|
|
66
|
+
return keysA.every((key) => deepEqual(a[key], b[key]));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const hasRemainingCurrentValues = (startIndex: number): boolean => {
|
|
73
|
+
for (let i = startIndex + 1; i < items.length; i += 1) {
|
|
74
|
+
if (diffMeta[items[i]]?.hasCurrent) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const stringify = (value: unknown): string => JSON.stringify(value);
|
|
82
|
+
|
|
83
|
+
const formatPrimitive = (value: Primitive): string => {
|
|
84
|
+
const type = getType(value);
|
|
85
|
+
if (type === "string") return stringify(value);
|
|
86
|
+
if (type === "number" || type === "bigint") return String(value);
|
|
87
|
+
if (type === "boolean") return value ? "true" : "false";
|
|
88
|
+
if (value === null) return "null";
|
|
89
|
+
if (value === undefined) return "undefined";
|
|
90
|
+
return String(value);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const toggleCollapsed = () => {
|
|
94
|
+
collapsed = !collapsed;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleKeyPress = (event: KeyboardEvent) => {
|
|
98
|
+
if (["Enter", " "].includes(event.key)) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
toggleCollapsed();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
$effect(() => {
|
|
105
|
+
const source = data ?? baseline;
|
|
106
|
+
const type = getType(source);
|
|
107
|
+
|
|
108
|
+
if (type === "object") {
|
|
109
|
+
const currentObj = isObjectLike(data) ? data : undefined;
|
|
110
|
+
const baselineObj = isObjectLike(baseline) ? baseline : undefined;
|
|
111
|
+
|
|
112
|
+
const currentKeys = currentObj ? Object.keys(currentObj) : [];
|
|
113
|
+
const baselineKeys = baselineObj ? Object.keys(baselineObj) : [];
|
|
114
|
+
|
|
115
|
+
const keys: string[] = [];
|
|
116
|
+
const seen = new Set<string>();
|
|
117
|
+
const appendKeys = (list: string[]) => {
|
|
118
|
+
for (const key of list) {
|
|
119
|
+
if (!seen.has(key)) {
|
|
120
|
+
keys.push(key);
|
|
121
|
+
seen.add(key);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
appendKeys(currentKeys);
|
|
127
|
+
if (dirty && baselineObj) {
|
|
128
|
+
appendKeys(baselineKeys);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!preserveKeyOrder) {
|
|
132
|
+
const sortKeys = Array.isArray(source)
|
|
133
|
+
? (keysToSort: string[]) =>
|
|
134
|
+
keysToSort.sort((a, b) => Number(a) - Number(b))
|
|
135
|
+
: (keysToSort: string[]) =>
|
|
136
|
+
keysToSort.sort((a, b) =>
|
|
137
|
+
a.localeCompare(b, undefined, { numeric: true }),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
sortKeys(keys);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const meta: typeof diffMeta = {};
|
|
144
|
+
|
|
145
|
+
for (const key of keys) {
|
|
146
|
+
const hasCurrent = Boolean(currentObj && key in currentObj);
|
|
147
|
+
const hasPrevious = Boolean(baselineObj && key in baselineObj);
|
|
148
|
+
const currentValue = hasCurrent ? currentObj?.[key] : undefined;
|
|
149
|
+
const previousValue = hasPrevious ? baselineObj?.[key] : undefined;
|
|
150
|
+
|
|
151
|
+
let status: DiffStatus = "unchanged";
|
|
152
|
+
|
|
153
|
+
if (dirty && baselineObj) {
|
|
154
|
+
if (hasCurrent && !hasPrevious) {
|
|
155
|
+
status = "added";
|
|
156
|
+
} else if (!hasCurrent && hasPrevious) {
|
|
157
|
+
status = "removed";
|
|
158
|
+
} else if (!deepEqual(currentValue, previousValue)) {
|
|
159
|
+
status = "changed";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
meta[key] = {
|
|
164
|
+
status,
|
|
165
|
+
hasCurrent,
|
|
166
|
+
hasPrevious,
|
|
167
|
+
currentValue,
|
|
168
|
+
previousValue,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
items = keys;
|
|
173
|
+
diffMeta = meta;
|
|
174
|
+
isArray = Array.isArray(source);
|
|
175
|
+
brackets = isArray ? ["[", "]"] : ["{", "}"];
|
|
176
|
+
} else {
|
|
177
|
+
items = [];
|
|
178
|
+
isArray = false;
|
|
179
|
+
brackets = ["{", "}"];
|
|
180
|
+
diffMeta = {};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
$effect(() => {
|
|
185
|
+
collapsed = depth < currentDepth;
|
|
186
|
+
});
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
{#if data === undefined && (!dirty || baseline === undefined)}
|
|
190
|
+
<div
|
|
191
|
+
class="flex h-80 w-full items-center justify-center rounded-md border border-dashed border-zinc-200 bg-zinc-50 text-sm text-zinc-500 dark:border-zinc-800 dark:bg-zinc-900 dark:text-zinc-400"
|
|
192
|
+
>
|
|
193
|
+
<p>No JSON selected.</p>
|
|
194
|
+
</div>
|
|
195
|
+
{:else}
|
|
196
|
+
{#if !items.length}
|
|
197
|
+
<span class="_jsonBkt empty" class:isArray={isArray}>
|
|
198
|
+
{brackets[0]}{brackets[1]}
|
|
199
|
+
</span>{#if !isLast}<span class="_jsonSep">,</span>{/if}
|
|
200
|
+
{:else if collapsed}
|
|
201
|
+
<span
|
|
202
|
+
class="_jsonBkt"
|
|
203
|
+
class:isArray={isArray}
|
|
204
|
+
role="button"
|
|
205
|
+
tabindex="0"
|
|
206
|
+
onclick={toggleCollapsed}
|
|
207
|
+
onkeydown={handleKeyPress}
|
|
208
|
+
>{brackets[0]}...{brackets[1]}</span>{#if !isLast && collapsed}<span class="_jsonSep">,</span>{/if}
|
|
209
|
+
{:else}
|
|
210
|
+
<span
|
|
211
|
+
class="_jsonBkt"
|
|
212
|
+
class:isArray={isArray}
|
|
213
|
+
class:diff-block-added={diffContext === "added"}
|
|
214
|
+
class:diff-block-removed={diffContext === "removed"}
|
|
215
|
+
role="button"
|
|
216
|
+
tabindex="0"
|
|
217
|
+
onclick={toggleCollapsed}
|
|
218
|
+
onkeydown={handleKeyPress}
|
|
219
|
+
>{brackets[0]}</span>
|
|
220
|
+
<ul class="_jsonList">
|
|
221
|
+
{#each items as key, idx}
|
|
222
|
+
{@const meta = diffMeta[key]}
|
|
223
|
+
{#if meta}
|
|
224
|
+
{@const currentValue = meta.currentValue}
|
|
225
|
+
{@const previousValue = meta.previousValue}
|
|
226
|
+
{@const valueType = getType(
|
|
227
|
+
meta.hasCurrent ? currentValue : previousValue,
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
<li class:diff-removed-row={meta.status === "removed"}>
|
|
231
|
+
{#if !isArray}
|
|
232
|
+
<span
|
|
233
|
+
class="_jsonKey"
|
|
234
|
+
class:diff-removed-text={meta.status === "removed"}
|
|
235
|
+
>
|
|
236
|
+
{stringify(key)}
|
|
237
|
+
</span>
|
|
238
|
+
<span class="_jsonSep">:</span>
|
|
239
|
+
{/if}
|
|
240
|
+
|
|
241
|
+
{#if (meta.hasCurrent ? getType(currentValue) : getType(previousValue)) ===
|
|
242
|
+
"object"}
|
|
243
|
+
<SimpleJsonViewer
|
|
244
|
+
data={meta.hasCurrent ? currentValue : undefined}
|
|
245
|
+
baseline={meta.hasPrevious ? previousValue : undefined}
|
|
246
|
+
{depth}
|
|
247
|
+
dirty={dirty}
|
|
248
|
+
diffContext={meta.status}
|
|
249
|
+
currentDepth={currentDepth + 1}
|
|
250
|
+
isLast={!hasRemainingCurrentValues(idx)}
|
|
251
|
+
/>
|
|
252
|
+
{:else if meta.hasCurrent}
|
|
253
|
+
<span
|
|
254
|
+
class="_jsonVal {getType(currentValue)}"
|
|
255
|
+
class:diff-added={meta.status === "added" || meta.status === "changed"}
|
|
256
|
+
>
|
|
257
|
+
{formatPrimitive(currentValue as Primitive)}
|
|
258
|
+
</span><!--
|
|
259
|
+
-->{#if meta.status === "changed" && meta.hasPrevious}
|
|
260
|
+
<span class="diff-previous-label">Updated from</span>
|
|
261
|
+
<span class="_jsonVal {getType(previousValue)} diff-removed">
|
|
262
|
+
{formatPrimitive(previousValue as Primitive)}
|
|
263
|
+
</span>
|
|
264
|
+
{/if}<!--
|
|
265
|
+
-->{#if meta.hasCurrent && hasRemainingCurrentValues(idx)}<span class="_jsonSep">,</span>{/if}
|
|
266
|
+
{:else if meta.hasPrevious}
|
|
267
|
+
<span class="_jsonVal {valueType} diff-removed">
|
|
268
|
+
{formatPrimitive(previousValue as Primitive)}
|
|
269
|
+
</span>
|
|
270
|
+
<span class="diff-previous-label">Removed</span>
|
|
271
|
+
{/if}
|
|
272
|
+
</li>
|
|
273
|
+
{/if}
|
|
274
|
+
{/each}
|
|
275
|
+
</ul>
|
|
276
|
+
<span
|
|
277
|
+
class="_jsonBkt"
|
|
278
|
+
class:isArray={isArray}
|
|
279
|
+
class:diff-block-added={diffContext === "added"}
|
|
280
|
+
class:diff-block-removed={diffContext === "removed"}
|
|
281
|
+
role="button"
|
|
282
|
+
tabindex="0"
|
|
283
|
+
onclick={toggleCollapsed}
|
|
284
|
+
onkeydown={handleKeyPress}
|
|
285
|
+
>{brackets[1]}</span>{#if !isLast && diffContext !== "removed"}<span class="_jsonSep">,</span>{/if}
|
|
286
|
+
{/if}
|
|
287
|
+
{/if}
|
|
288
|
+
|
|
289
|
+
<style>
|
|
290
|
+
:global(.dark) {
|
|
291
|
+
--jsonBracketHoverBackground: rgba(63, 63, 70, 0.4);
|
|
292
|
+
--jsonBorderLeft: 1px dashed rgba(63, 63, 70, 0.6);
|
|
293
|
+
--jsonValColor: rgba(228, 228, 231, 0.8);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
:where(._jsonList) {
|
|
297
|
+
list-style: none;
|
|
298
|
+
margin: 0;
|
|
299
|
+
padding: 0;
|
|
300
|
+
padding-left: var(--jsonPaddingLeft, 1rem);
|
|
301
|
+
border-left: var(--jsonBorderLeft, 1px dotted);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
:where(._jsonBkt) {
|
|
305
|
+
color: var(--jsonBracketColor, currentcolor);
|
|
306
|
+
border-radius: 0.25rem;
|
|
307
|
+
padding: 0.1rem 0.25rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
:where(._jsonBkt):not(.empty):hover,
|
|
311
|
+
:where(._jsonBkt):focus-visible {
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
outline: none;
|
|
314
|
+
background: var(--jsonBracketHoverBackground, #e5e7eb);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
:where(._jsonSep) {
|
|
318
|
+
color: var(--jsonSeparatorColor, currentcolor);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
:where(._jsonKey) {
|
|
322
|
+
color: var(--jsonKeyColor, currentcolor);
|
|
323
|
+
margin-right: 0.35rem;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
:where(._jsonVal) {
|
|
327
|
+
color: var(--jsonValColor, #9ca3af);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
:where(._jsonVal).string {
|
|
331
|
+
color: var(--jsonValStringColor, #059669);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
:where(._jsonVal).number {
|
|
335
|
+
color: var(--jsonValNumberColor, #d97706);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
:where(._jsonVal).boolean {
|
|
339
|
+
color: var(--jsonValBooleanColor, #2563eb);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
:where(.diff-added) {
|
|
343
|
+
background: var(--jsonDiffAddedBg, rgba(34, 197, 94, 0.14));
|
|
344
|
+
color: var(--jsonDiffAddedColor, #166534);
|
|
345
|
+
padding: 0.125rem 0.25rem;
|
|
346
|
+
border-radius: 0.25rem;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
:where(.diff-removed) {
|
|
350
|
+
background: var(--jsonDiffRemovedBg, rgba(239, 68, 68, 0.16));
|
|
351
|
+
color: var(--jsonDiffRemovedColor, #b91c1c);
|
|
352
|
+
padding: 0.125rem 0.25rem;
|
|
353
|
+
border-radius: 0.25rem;
|
|
354
|
+
text-decoration: line-through;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.diff-removed-text {
|
|
358
|
+
color: var(--jsonDiffRemovedColor, #b91c1c);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.diff-removed-row {
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: baseline;
|
|
364
|
+
gap: 0.35rem;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.diff-previous-label {
|
|
368
|
+
margin-left: 0.35rem;
|
|
369
|
+
margin-right: 0.25rem;
|
|
370
|
+
font-size: 0.65rem;
|
|
371
|
+
text-transform: uppercase;
|
|
372
|
+
letter-spacing: 0.05em;
|
|
373
|
+
color: var(--jsonDiffLabelColor, #52525b);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.diff-block-added {
|
|
377
|
+
background: var(--jsonDiffAddedBg, rgba(34, 197, 94, 0.08));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.diff-block-removed {
|
|
381
|
+
background: var(--jsonDiffRemovedBg, rgba(239, 68, 68, 0.08));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
:global(.dark) :where(.diff-added) {
|
|
385
|
+
background: rgba(34, 197, 94, 0.24);
|
|
386
|
+
color: #bbf7d0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
:global(.dark) :where(.diff-removed) {
|
|
390
|
+
background: rgba(239, 68, 68, 0.22);
|
|
391
|
+
color: #fecaca;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
:global(.dark) .diff-previous-label {
|
|
395
|
+
color: #a1a1aa;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
:global(.dark) .diff-block-added {
|
|
399
|
+
background: rgba(34, 197, 94, 0.18);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
:global(.dark) .diff-block-removed {
|
|
403
|
+
background: rgba(239, 68, 68, 0.18);
|
|
404
|
+
}
|
|
405
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
3
|
+
|
|
4
|
+
export const badgeVariants = tv({
|
|
5
|
+
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-full border px-2 py-0.5 text-xs font-medium transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default:
|
|
9
|
+
"bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent",
|
|
10
|
+
secondary:
|
|
11
|
+
"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent",
|
|
12
|
+
destructive:
|
|
13
|
+
"bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white",
|
|
14
|
+
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: "default",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
import type { HTMLAnchorAttributes } from "svelte/elements";
|
|
27
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
ref = $bindable(null),
|
|
31
|
+
href,
|
|
32
|
+
class: className,
|
|
33
|
+
variant = "default",
|
|
34
|
+
children,
|
|
35
|
+
...restProps
|
|
36
|
+
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
37
|
+
variant?: BadgeVariant;
|
|
38
|
+
} = $props();
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<svelte:element
|
|
42
|
+
this={href ? "a" : "span"}
|
|
43
|
+
bind:this={ref}
|
|
44
|
+
data-slot="badge"
|
|
45
|
+
{href}
|
|
46
|
+
class={cn(badgeVariants({ variant }), className)}
|
|
47
|
+
{...restProps}
|
|
48
|
+
>
|
|
49
|
+
{@render children?.()}
|
|
50
|
+
</svelte:element>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Installed from @ieedan/shadcn-svelte-extras
|
|
3
|
+
-->
|
|
4
|
+
|
|
5
|
+
<script lang="ts" module>
|
|
6
|
+
import type { WithChildren, WithoutChildren } from 'bits-ui';
|
|
7
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
|
8
|
+
import { type VariantProps, tv } from 'tailwind-variants';
|
|
9
|
+
|
|
10
|
+
export const buttonVariants = tv({
|
|
11
|
+
base: "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive focus-visible:border-ring focus-visible:ring-ring/50 relative inline-flex shrink-0 items-center justify-center gap-2 overflow-hidden rounded-md text-sm font-medium whitespace-nowrap outline-hidden transition-all select-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow-2xs',
|
|
15
|
+
destructive:
|
|
16
|
+
'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 text-white shadow-2xs',
|
|
17
|
+
outline:
|
|
18
|
+
'bg-background hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 border shadow-2xs',
|
|
19
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-2xs',
|
|
20
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
|
21
|
+
link: 'text-primary underline-offset-4 hover:underline'
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
25
|
+
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
|
|
26
|
+
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
|
27
|
+
icon: 'size-9'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: 'default',
|
|
32
|
+
size: 'default'
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
|
37
|
+
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
|
38
|
+
|
|
39
|
+
export type ButtonPropsWithoutHTML = WithChildren<{
|
|
40
|
+
ref?: HTMLElement | null;
|
|
41
|
+
variant?: ButtonVariant;
|
|
42
|
+
size?: ButtonSize;
|
|
43
|
+
loading?: boolean;
|
|
44
|
+
onClickPromise?: (
|
|
45
|
+
e: MouseEvent & {
|
|
46
|
+
currentTarget: EventTarget & HTMLButtonElement;
|
|
47
|
+
}
|
|
48
|
+
) => Promise<void>;
|
|
49
|
+
}>;
|
|
50
|
+
|
|
51
|
+
export type AnchorElementProps = ButtonPropsWithoutHTML &
|
|
52
|
+
WithoutChildren<Omit<HTMLAnchorAttributes, 'href' | 'type'>> & {
|
|
53
|
+
href: HTMLAnchorAttributes['href'];
|
|
54
|
+
type?: never;
|
|
55
|
+
disabled?: HTMLButtonAttributes['disabled'];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type ButtonElementProps = ButtonPropsWithoutHTML &
|
|
59
|
+
WithoutChildren<Omit<HTMLButtonAttributes, 'type' | 'href'>> & {
|
|
60
|
+
type?: HTMLButtonAttributes['type'];
|
|
61
|
+
href?: never;
|
|
62
|
+
disabled?: HTMLButtonAttributes['disabled'];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type ButtonProps = AnchorElementProps | ButtonElementProps;
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<script lang="ts">
|
|
69
|
+
import { cn } from '../../../utils/utils.js';
|
|
70
|
+
import LoaderCircleIcon from '@lucide/svelte/icons/loader-circle';
|
|
71
|
+
|
|
72
|
+
let {
|
|
73
|
+
ref = $bindable(null),
|
|
74
|
+
variant = 'default',
|
|
75
|
+
size = 'default',
|
|
76
|
+
href = undefined,
|
|
77
|
+
type = 'button',
|
|
78
|
+
loading = false,
|
|
79
|
+
disabled = false,
|
|
80
|
+
tabindex = 0,
|
|
81
|
+
onclick,
|
|
82
|
+
onClickPromise,
|
|
83
|
+
class: className,
|
|
84
|
+
children,
|
|
85
|
+
...rest
|
|
86
|
+
}: ButtonProps = $props();
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<!-- This approach to disabled links is inspired by bits-ui see: https://github.com/huntabyte/bits-ui/pull/1055 -->
|
|
90
|
+
<svelte:element
|
|
91
|
+
this={href ? 'a' : 'button'}
|
|
92
|
+
{...rest}
|
|
93
|
+
data-slot="button"
|
|
94
|
+
type={href ? undefined : type}
|
|
95
|
+
href={href && !disabled ? href : undefined}
|
|
96
|
+
disabled={href ? undefined : disabled || loading}
|
|
97
|
+
aria-disabled={href ? disabled : undefined}
|
|
98
|
+
role={href && disabled ? 'link' : undefined}
|
|
99
|
+
tabindex={href && disabled ? -1 : tabindex}
|
|
100
|
+
class={cn(buttonVariants({ variant, size }), className)}
|
|
101
|
+
bind:this={ref}
|
|
102
|
+
onclick={async (
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
104
|
+
e: any
|
|
105
|
+
) => {
|
|
106
|
+
onclick?.(e);
|
|
107
|
+
|
|
108
|
+
if (type === undefined) return;
|
|
109
|
+
|
|
110
|
+
if (onClickPromise) {
|
|
111
|
+
loading = true;
|
|
112
|
+
|
|
113
|
+
await onClickPromise(e);
|
|
114
|
+
|
|
115
|
+
loading = false;
|
|
116
|
+
}
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{#if type !== undefined && loading}
|
|
120
|
+
<div class="absolute flex size-full place-items-center justify-center bg-inherit">
|
|
121
|
+
<div class="flex animate-spin place-items-center justify-center">
|
|
122
|
+
<LoaderCircleIcon class="size-4" />
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<span class="sr-only">Loading</span>
|
|
126
|
+
{/if}
|
|
127
|
+
{@render children?.()}
|
|
128
|
+
</svelte:element>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Installed from @ieedan/shadcn-svelte-extras
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Root, {
|
|
6
|
+
type ButtonProps,
|
|
7
|
+
type ButtonSize,
|
|
8
|
+
type ButtonVariant,
|
|
9
|
+
type AnchorElementProps,
|
|
10
|
+
type ButtonElementProps,
|
|
11
|
+
type ButtonPropsWithoutHTML,
|
|
12
|
+
buttonVariants,
|
|
13
|
+
} from "./button.svelte";
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
Root,
|
|
17
|
+
type ButtonProps as Props,
|
|
18
|
+
//
|
|
19
|
+
Root as Button,
|
|
20
|
+
buttonVariants,
|
|
21
|
+
type ButtonProps,
|
|
22
|
+
type ButtonSize,
|
|
23
|
+
type ButtonVariant,
|
|
24
|
+
type AnchorElementProps,
|
|
25
|
+
type ButtonElementProps,
|
|
26
|
+
type ButtonPropsWithoutHTML,
|
|
27
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-action"
|
|
16
|
+
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}>
|
|
14
|
+
{@render children?.()}
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<p
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-description"
|
|
16
|
+
class={cn("text-muted-foreground text-sm", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</p>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-footer"
|
|
16
|
+
class={cn("[.border-t]:pt-6 flex items-center px-6", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|