vite-plugin-lingo 0.0.1
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/README.md +266 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/plugin/gettext-parser.d.ts +44 -0
- package/dist/plugin/index.cjs +449 -0
- package/dist/plugin/index.cjs.map +1 -0
- package/dist/plugin/index.d.cts +84 -0
- package/dist/plugin/index.d.ts +84 -0
- package/dist/plugin/index.js +413 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/middleware.d.ts +10 -0
- package/dist/plugin/middleware.js +137 -0
- package/dist/plugin/po-parser.d.ts +25 -0
- package/dist/plugin/po-parser.js +147 -0
- package/dist/plugin/types.d.ts +67 -0
- package/dist/plugin/types.js +1 -0
- package/dist/ui/App.svelte +92 -0
- package/dist/ui/App.svelte.d.ts +3 -0
- package/dist/ui/app.css +64 -0
- package/dist/ui/components/LanguageList.svelte +166 -0
- package/dist/ui/components/LanguageList.svelte.d.ts +6 -0
- package/dist/ui/components/ProgressBar.svelte +47 -0
- package/dist/ui/components/ProgressBar.svelte.d.ts +9 -0
- package/dist/ui/components/SearchBar.svelte +54 -0
- package/dist/ui/components/SearchBar.svelte.d.ts +6 -0
- package/dist/ui/components/ThemeToggle.svelte +17 -0
- package/dist/ui/components/ThemeToggle.svelte.d.ts +18 -0
- package/dist/ui/components/TranslationEditor.svelte +418 -0
- package/dist/ui/components/TranslationEditor.svelte.d.ts +8 -0
- package/dist/ui/index.html +24 -0
- package/dist/ui/main.d.ts +3 -0
- package/dist/ui/main.js +7 -0
- package/dist/ui/stores/refresh-signal.svelte.d.ts +6 -0
- package/dist/ui/stores/refresh-signal.svelte.js +11 -0
- package/dist/ui-dist/assets/index-B5dZv0sy.css +1 -0
- package/dist/ui-dist/assets/index-DsX4xzGF.js +10 -0
- package/dist/ui-dist/index.html +25 -0
- package/package.json +118 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const ThemeToggle: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type ThemeToggle = InstanceType<typeof ThemeToggle>;
|
|
18
|
+
export default ThemeToggle;
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
LoaderCircle,
|
|
4
|
+
TriangleAlert,
|
|
5
|
+
FileText,
|
|
6
|
+
Pencil,
|
|
7
|
+
Check,
|
|
8
|
+
X,
|
|
9
|
+
MapPin,
|
|
10
|
+
CircleCheck,
|
|
11
|
+
CircleAlert,
|
|
12
|
+
CircleDashed,
|
|
13
|
+
Flag,
|
|
14
|
+
FlagOff,
|
|
15
|
+
MessageSquareText,
|
|
16
|
+
Info
|
|
17
|
+
} from '@lucide/svelte';
|
|
18
|
+
import { triggerRefresh } from '../stores/refresh-signal.svelte';
|
|
19
|
+
|
|
20
|
+
interface Translation {
|
|
21
|
+
msgid: string;
|
|
22
|
+
msgstr: string;
|
|
23
|
+
context?: string;
|
|
24
|
+
fuzzy?: boolean;
|
|
25
|
+
comments?: {
|
|
26
|
+
reference?: string;
|
|
27
|
+
translator?: string;
|
|
28
|
+
extracted?: string;
|
|
29
|
+
flag?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Props {
|
|
34
|
+
language: string;
|
|
35
|
+
searchQuery: string;
|
|
36
|
+
filter: 'all' | 'translated' | 'untranslated' | 'fuzzy';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let { language, searchQuery, filter }: Props = $props();
|
|
40
|
+
|
|
41
|
+
let translations = $state<Translation[]>([]);
|
|
42
|
+
let loading = $state(true);
|
|
43
|
+
let error = $state<string | null>(null);
|
|
44
|
+
let saving = $state<string | null>(null);
|
|
45
|
+
let editingId = $state<string | null>(null);
|
|
46
|
+
let editValue = $state('');
|
|
47
|
+
let togglingFuzzy = $state<string | null>(null);
|
|
48
|
+
|
|
49
|
+
// Derived filtered translations
|
|
50
|
+
let filteredTranslations = $derived.by(() => {
|
|
51
|
+
let result = translations;
|
|
52
|
+
|
|
53
|
+
// Apply filter
|
|
54
|
+
switch (filter) {
|
|
55
|
+
case 'translated':
|
|
56
|
+
result = result.filter((t) => t.msgstr && !t.fuzzy);
|
|
57
|
+
break;
|
|
58
|
+
case 'untranslated':
|
|
59
|
+
result = result.filter((t) => !t.msgstr);
|
|
60
|
+
break;
|
|
61
|
+
case 'fuzzy':
|
|
62
|
+
result = result.filter((t) => t.fuzzy);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Apply search
|
|
67
|
+
if (searchQuery) {
|
|
68
|
+
const query = searchQuery.toLowerCase();
|
|
69
|
+
result = result.filter(
|
|
70
|
+
(t) =>
|
|
71
|
+
t.msgid.toLowerCase().includes(query) || t.msgstr.toLowerCase().includes(query)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Stats
|
|
79
|
+
let stats = $derived({
|
|
80
|
+
total: translations.length,
|
|
81
|
+
translated: translations.filter((t) => t.msgstr && !t.fuzzy).length,
|
|
82
|
+
untranslated: translations.filter((t) => !t.msgstr).length,
|
|
83
|
+
fuzzy: translations.filter((t) => t.fuzzy).length
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Load translations when language changes
|
|
87
|
+
$effect(() => {
|
|
88
|
+
if (language) {
|
|
89
|
+
loadTranslations();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
async function loadTranslations() {
|
|
94
|
+
loading = true;
|
|
95
|
+
error = null;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(`./api/translations/${language}`);
|
|
99
|
+
const result = await res.json();
|
|
100
|
+
|
|
101
|
+
if (!result.success) {
|
|
102
|
+
throw new Error(result.error || 'Failed to load translations');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
translations = result.data || [];
|
|
106
|
+
} catch (e) {
|
|
107
|
+
error = e instanceof Error ? e.message : 'Failed to load translations';
|
|
108
|
+
} finally {
|
|
109
|
+
loading = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function startEdit(t: Translation) {
|
|
114
|
+
editingId = t.msgid;
|
|
115
|
+
editValue = t.msgstr;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function cancelEdit() {
|
|
119
|
+
editingId = null;
|
|
120
|
+
editValue = '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function saveEdit(t: Translation) {
|
|
124
|
+
if (editValue === t.msgstr) {
|
|
125
|
+
cancelEdit();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
saving = t.msgid;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const res = await fetch(`./api/translations/${language}`, {
|
|
133
|
+
method: 'PUT',
|
|
134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
msgid: t.msgid,
|
|
137
|
+
msgstr: editValue,
|
|
138
|
+
context: t.context
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const result = await res.json();
|
|
143
|
+
|
|
144
|
+
if (!result.success) {
|
|
145
|
+
throw new Error(result.error || 'Failed to save');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Update local state
|
|
149
|
+
const index = translations.findIndex((tr) => tr.msgid === t.msgid);
|
|
150
|
+
if (index !== -1) {
|
|
151
|
+
translations[index] = { ...translations[index], msgstr: editValue };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Notify the language list to refresh stats
|
|
155
|
+
triggerRefresh();
|
|
156
|
+
|
|
157
|
+
cancelEdit();
|
|
158
|
+
} catch (e) {
|
|
159
|
+
alert(e instanceof Error ? e.message : 'Failed to save');
|
|
160
|
+
} finally {
|
|
161
|
+
saving = null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function handleKeydown(e: KeyboardEvent, t: Translation) {
|
|
166
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
saveEdit(t);
|
|
169
|
+
}
|
|
170
|
+
if (e.key === 'Escape') {
|
|
171
|
+
cancelEdit();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function toggleFuzzy(t: Translation) {
|
|
176
|
+
togglingFuzzy = t.msgid;
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(`./api/translations/${language}`, {
|
|
180
|
+
method: 'PUT',
|
|
181
|
+
headers: { 'Content-Type': 'application/json' },
|
|
182
|
+
body: JSON.stringify({
|
|
183
|
+
msgid: t.msgid,
|
|
184
|
+
msgstr: t.msgstr,
|
|
185
|
+
context: t.context,
|
|
186
|
+
fuzzy: !t.fuzzy
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const result = await res.json();
|
|
191
|
+
|
|
192
|
+
if (!result.success) {
|
|
193
|
+
throw new Error(result.error || 'Failed to toggle fuzzy status');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Update local state
|
|
197
|
+
const index = translations.findIndex((tr) => tr.msgid === t.msgid);
|
|
198
|
+
if (index !== -1) {
|
|
199
|
+
translations[index] = { ...translations[index], fuzzy: !t.fuzzy };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Notify the language list to refresh stats
|
|
203
|
+
triggerRefresh();
|
|
204
|
+
} catch (e) {
|
|
205
|
+
alert(e instanceof Error ? e.message : 'Failed to toggle fuzzy status');
|
|
206
|
+
} finally {
|
|
207
|
+
togglingFuzzy = null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getStatusIcon(t: Translation) {
|
|
212
|
+
if (t.fuzzy) return { icon: CircleAlert, class: 'text-amber-500' };
|
|
213
|
+
if (t.msgstr) return { icon: CircleCheck, class: 'text-emerald-500' };
|
|
214
|
+
return { icon: CircleDashed, class: 'text-red-400' };
|
|
215
|
+
}
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
<div class="flex flex-1 flex-col overflow-hidden">
|
|
219
|
+
<!-- Stats bar -->
|
|
220
|
+
<div class="flex items-center gap-6 border-b border-slate-200 bg-white px-6 py-3 dark:border-slate-700 dark:bg-slate-800">
|
|
221
|
+
<div class="flex items-center gap-2">
|
|
222
|
+
<span class="text-2xl font-semibold text-slate-900 dark:text-white">{stats.total}</span>
|
|
223
|
+
<span class="text-sm text-slate-500 dark:text-slate-400">Total</span>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="flex items-center gap-2">
|
|
226
|
+
<CircleCheck class="h-4 w-4 text-emerald-500" />
|
|
227
|
+
<span class="font-medium text-emerald-600 dark:text-emerald-400">{stats.translated}</span>
|
|
228
|
+
<span class="text-sm text-slate-500 dark:text-slate-400">Translated</span>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="flex items-center gap-2">
|
|
231
|
+
<CircleDashed class="h-4 w-4 text-red-400" />
|
|
232
|
+
<span class="font-medium text-red-600 dark:text-red-400">{stats.untranslated}</span>
|
|
233
|
+
<span class="text-sm text-slate-500 dark:text-slate-400">Untranslated</span>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="flex items-center gap-2">
|
|
236
|
+
<CircleAlert class="h-4 w-4 text-amber-500" />
|
|
237
|
+
<span class="font-medium text-amber-600 dark:text-amber-400">{stats.fuzzy}</span>
|
|
238
|
+
<span class="text-sm text-slate-500 dark:text-slate-400">Fuzzy</span>
|
|
239
|
+
</div>
|
|
240
|
+
<div class="ml-auto text-sm text-slate-500 dark:text-slate-400">
|
|
241
|
+
Showing {filteredTranslations.length} of {stats.total}
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<!-- Content -->
|
|
246
|
+
{#if loading}
|
|
247
|
+
<div class="flex flex-1 flex-col items-center justify-center gap-3 text-slate-500 dark:text-slate-400">
|
|
248
|
+
<LoaderCircle class="h-8 w-8 animate-spin text-blue-500" />
|
|
249
|
+
<span>Loading translations...</span>
|
|
250
|
+
</div>
|
|
251
|
+
{:else if error}
|
|
252
|
+
<div class="flex flex-1 flex-col items-center justify-center gap-4">
|
|
253
|
+
<TriangleAlert class="h-10 w-10 text-red-400" />
|
|
254
|
+
<p class="text-red-600 dark:text-red-400">{error}</p>
|
|
255
|
+
<button type="button" class="btn btn-secondary" onclick={loadTranslations}>
|
|
256
|
+
Retry
|
|
257
|
+
</button>
|
|
258
|
+
</div>
|
|
259
|
+
{:else if filteredTranslations.length === 0}
|
|
260
|
+
<div class="flex flex-1 flex-col items-center justify-center gap-3 text-slate-500 dark:text-slate-400">
|
|
261
|
+
<FileText class="h-10 w-10 text-slate-300 dark:text-slate-600" />
|
|
262
|
+
<p class="font-medium">No translations found</p>
|
|
263
|
+
{#if searchQuery || filter !== 'all'}
|
|
264
|
+
<p class="text-sm text-slate-400 dark:text-slate-500">Try adjusting your search or filter</p>
|
|
265
|
+
{/if}
|
|
266
|
+
</div>
|
|
267
|
+
{:else}
|
|
268
|
+
<div class="flex-1 overflow-y-auto">
|
|
269
|
+
<div class="divide-y divide-slate-100 dark:divide-slate-700">
|
|
270
|
+
{#each filteredTranslations as t (t.msgid)}
|
|
271
|
+
{@const status = getStatusIcon(t)}
|
|
272
|
+
<div
|
|
273
|
+
class="group px-6 py-4 transition-colors hover:bg-slate-50 dark:hover:bg-slate-800/50 {t.fuzzy
|
|
274
|
+
? 'bg-amber-50/50 dark:bg-amber-900/10'
|
|
275
|
+
: !t.msgstr
|
|
276
|
+
? 'bg-red-50/30 dark:bg-red-900/10'
|
|
277
|
+
: ''}"
|
|
278
|
+
>
|
|
279
|
+
<div class="flex gap-4">
|
|
280
|
+
<!-- Status icon -->
|
|
281
|
+
<div class="shrink-0 pt-0.5">
|
|
282
|
+
<status.icon class="h-5 w-5 {status.class}" />
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<!-- Content -->
|
|
286
|
+
<div class="min-w-0 flex-1 space-y-3">
|
|
287
|
+
<!-- Source -->
|
|
288
|
+
<div>
|
|
289
|
+
<div class="mb-1 flex items-center gap-2">
|
|
290
|
+
<span class="text-xs font-medium uppercase tracking-wide text-slate-400">Source</span>
|
|
291
|
+
{#if t.context}
|
|
292
|
+
<span class="badge badge-secondary text-xs">ctx: {t.context}</span>
|
|
293
|
+
{/if}
|
|
294
|
+
</div>
|
|
295
|
+
<code class="block rounded bg-slate-100 px-3 py-2 text-sm text-slate-800 dark:bg-slate-700 dark:text-slate-200">
|
|
296
|
+
{t.msgid}
|
|
297
|
+
</code>
|
|
298
|
+
{#if t.comments?.reference}
|
|
299
|
+
<div class="mt-1 flex items-center gap-1 text-xs text-slate-400 dark:text-slate-500">
|
|
300
|
+
<MapPin class="h-3 w-3" />
|
|
301
|
+
{t.comments.reference}
|
|
302
|
+
</div>
|
|
303
|
+
{/if}
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<!-- Comments (translator notes, extracted comments) -->
|
|
307
|
+
{#if t.comments?.translator || t.comments?.extracted}
|
|
308
|
+
<div class="rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-slate-600 dark:bg-slate-800/50">
|
|
309
|
+
{#if t.comments.translator}
|
|
310
|
+
<div class="flex items-start gap-2 text-sm">
|
|
311
|
+
<MessageSquareText class="mt-0.5 h-4 w-4 shrink-0 text-blue-500" />
|
|
312
|
+
<div>
|
|
313
|
+
<span class="font-medium text-slate-600 dark:text-slate-300">Translator note:</span>
|
|
314
|
+
<span class="ml-1 text-slate-700 dark:text-slate-200">{t.comments.translator}</span>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
{/if}
|
|
318
|
+
{#if t.comments.extracted}
|
|
319
|
+
<div class="flex items-start gap-2 text-sm {t.comments.translator ? 'mt-2' : ''}">
|
|
320
|
+
<Info class="mt-0.5 h-4 w-4 shrink-0 text-slate-400" />
|
|
321
|
+
<div>
|
|
322
|
+
<span class="font-medium text-slate-600 dark:text-slate-300">Context:</span>
|
|
323
|
+
<span class="ml-1 text-slate-700 dark:text-slate-200">{t.comments.extracted}</span>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
{/if}
|
|
327
|
+
</div>
|
|
328
|
+
{/if}
|
|
329
|
+
|
|
330
|
+
<!-- Translation -->
|
|
331
|
+
<div>
|
|
332
|
+
<div class="mb-1 flex items-center gap-2">
|
|
333
|
+
<span class="text-xs font-medium uppercase tracking-wide text-slate-400">Translation</span>
|
|
334
|
+
{#if t.fuzzy}
|
|
335
|
+
<span class="badge badge-warning">Fuzzy</span>
|
|
336
|
+
{/if}
|
|
337
|
+
<!-- Fuzzy toggle button -->
|
|
338
|
+
{#if t.msgstr}
|
|
339
|
+
<button
|
|
340
|
+
type="button"
|
|
341
|
+
class="ml-auto flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors {t.fuzzy
|
|
342
|
+
? 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:hover:bg-amber-900/50'
|
|
343
|
+
: 'bg-slate-100 text-slate-600 hover:bg-slate-200 dark:bg-slate-700 dark:text-slate-400 dark:hover:bg-slate-600'}"
|
|
344
|
+
onclick={() => toggleFuzzy(t)}
|
|
345
|
+
disabled={togglingFuzzy === t.msgid}
|
|
346
|
+
title={t.fuzzy ? 'Mark as not fuzzy' : 'Mark as fuzzy (needs review)'}
|
|
347
|
+
>
|
|
348
|
+
{#if togglingFuzzy === t.msgid}
|
|
349
|
+
<LoaderCircle class="h-3 w-3 animate-spin" />
|
|
350
|
+
{:else if t.fuzzy}
|
|
351
|
+
<FlagOff class="h-3 w-3" />
|
|
352
|
+
<span>Unflag</span>
|
|
353
|
+
{:else}
|
|
354
|
+
<Flag class="h-3 w-3" />
|
|
355
|
+
<span>Mark fuzzy</span>
|
|
356
|
+
{/if}
|
|
357
|
+
</button>
|
|
358
|
+
{/if}
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
{#if editingId === t.msgid}
|
|
362
|
+
<!-- Edit mode -->
|
|
363
|
+
<div class="space-y-2">
|
|
364
|
+
<textarea
|
|
365
|
+
bind:value={editValue}
|
|
366
|
+
onkeydown={(e) => handleKeydown(e, t)}
|
|
367
|
+
rows="2"
|
|
368
|
+
class="input w-full resize-none font-mono text-sm"
|
|
369
|
+
placeholder="Enter translation..."
|
|
370
|
+
></textarea>
|
|
371
|
+
<div class="flex items-center gap-2">
|
|
372
|
+
<button
|
|
373
|
+
type="button"
|
|
374
|
+
class="btn btn-primary"
|
|
375
|
+
onclick={() => saveEdit(t)}
|
|
376
|
+
disabled={saving === t.msgid}
|
|
377
|
+
>
|
|
378
|
+
{#if saving === t.msgid}
|
|
379
|
+
<LoaderCircle class="h-4 w-4 animate-spin" />
|
|
380
|
+
Saving...
|
|
381
|
+
{:else}
|
|
382
|
+
<Check class="h-4 w-4" />
|
|
383
|
+
Save
|
|
384
|
+
{/if}
|
|
385
|
+
</button>
|
|
386
|
+
<button type="button" class="btn btn-ghost" onclick={cancelEdit}>
|
|
387
|
+
<X class="h-4 w-4" />
|
|
388
|
+
Cancel
|
|
389
|
+
</button>
|
|
390
|
+
<span class="ml-2 text-xs text-slate-400 dark:text-slate-500">
|
|
391
|
+
Enter to save, Esc to cancel
|
|
392
|
+
</span>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
{:else}
|
|
396
|
+
<!-- Display mode -->
|
|
397
|
+
<button
|
|
398
|
+
type="button"
|
|
399
|
+
class="group/edit flex w-full items-start gap-2 rounded-lg px-3 py-2 text-left transition-colors hover:bg-slate-100 dark:hover:bg-slate-700"
|
|
400
|
+
onclick={() => startEdit(t)}
|
|
401
|
+
>
|
|
402
|
+
{#if t.msgstr}
|
|
403
|
+
<span class="flex-1 text-sm text-slate-800 dark:text-slate-200">{t.msgstr}</span>
|
|
404
|
+
{:else}
|
|
405
|
+
<span class="flex-1 text-sm italic text-slate-400 dark:text-slate-500">Click to add translation</span>
|
|
406
|
+
{/if}
|
|
407
|
+
<Pencil class="h-4 w-4 shrink-0 text-slate-400 opacity-0 transition-opacity group-hover/edit:opacity-100" />
|
|
408
|
+
</button>
|
|
409
|
+
{/if}
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
{/each}
|
|
415
|
+
</div>
|
|
416
|
+
</div>
|
|
417
|
+
{/if}
|
|
418
|
+
</div>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
language: string;
|
|
3
|
+
searchQuery: string;
|
|
4
|
+
filter: 'all' | 'translated' | 'untranslated' | 'fuzzy';
|
|
5
|
+
}
|
|
6
|
+
declare const TranslationEditor: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type TranslationEditor = ReturnType<typeof TranslationEditor>;
|
|
8
|
+
export default TranslationEditor;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Lingo Translation Editor</title>
|
|
7
|
+
<script>
|
|
8
|
+
// Prevent FOUC by setting theme before page renders
|
|
9
|
+
(function() {
|
|
10
|
+
const stored = localStorage.getItem('mode-watcher-mode');
|
|
11
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
12
|
+
const theme = stored ? JSON.parse(stored) : (prefersDark ? 'dark' : 'light');
|
|
13
|
+
if (theme === 'dark' || (theme === 'system' && prefersDark)) {
|
|
14
|
+
document.documentElement.classList.add('dark');
|
|
15
|
+
}
|
|
16
|
+
document.documentElement.style.colorScheme = theme === 'dark' || (theme === 'system' && prefersDark) ? 'dark' : 'light';
|
|
17
|
+
})();
|
|
18
|
+
</script>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div id="app"></div>
|
|
22
|
+
<script type="module" src="./main.ts"></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
package/dist/ui/main.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple reactive signal to notify the LanguageList when translations are updated.
|
|
3
|
+
* Using Svelte 5 runes for fine-grained reactivity.
|
|
4
|
+
*/
|
|
5
|
+
let refreshCount = $state(0);
|
|
6
|
+
export function getRefreshCount() {
|
|
7
|
+
return refreshCount;
|
|
8
|
+
}
|
|
9
|
+
export function triggerRefresh() {
|
|
10
|
+
refreshCount++;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-900:oklch(39.6% .141 25.723);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-900:oklch(41.4% .112 45.904);--color-emerald-100:oklch(95% .052 163.051);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-emerald-700:oklch(50.8% .118 165.612);--color-emerald-900:oklch(37.8% .077 168.94);--color-blue-50:oklch(97% .014 254.604);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-900:oklch(37.9% .146 265.522);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}body{background-color:var(--color-slate-50);color:var(--color-slate-900);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body:where(.dark,.dark *){background-color:var(--color-slate-900);color:var(--color-slate-100)}}@layer components{.btn{justify-content:center;align-items:center;gap:calc(var(--spacing)*2);border-radius:var(--radius-md);padding-inline:calc(var(--spacing)*3);padding-block:calc(var(--spacing)*1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:inline-flex}.btn:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color);--tw-outline-style:none;outline-style:none}.btn:disabled{pointer-events:none;opacity:.5}.btn:where(.dark,.dark *):focus-visible{--tw-ring-offset-color:var(--color-slate-900)}.btn-primary{background-color:var(--color-blue-600);color:var(--color-white)}@media(hover:hover){.btn-primary:hover{background-color:var(--color-blue-700)}}.btn-primary:where(.dark,.dark *){background-color:var(--color-blue-500)}@media(hover:hover){.btn-primary:where(.dark,.dark *):hover{background-color:var(--color-blue-600)}}.btn-secondary{background-color:var(--color-slate-100);color:var(--color-slate-900)}@media(hover:hover){.btn-secondary:hover{background-color:var(--color-slate-200)}}.btn-secondary:where(.dark,.dark *){background-color:var(--color-slate-800);color:var(--color-slate-100)}@media(hover:hover){.btn-secondary:where(.dark,.dark *):hover{background-color:var(--color-slate-700)}.btn-ghost:hover{background-color:var(--color-slate-100)}.btn-ghost:where(.dark,.dark *):hover{background-color:var(--color-slate-800)}}.input{height:calc(var(--spacing)*9);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-300);background-color:var(--color-white);width:100%;padding-inline:calc(var(--spacing)*3);padding-block:calc(var(--spacing)*1);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.input::placeholder{color:var(--color-slate-400)}.input:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:var(--color-blue-500);--tw-outline-style:none;border-color:#0000;outline-style:none}.input:disabled{cursor:not-allowed;opacity:.5}.input:where(.dark,.dark *){border-color:var(--color-slate-600);background-color:var(--color-slate-800);color:var(--color-slate-100)}.input:where(.dark,.dark *)::placeholder{color:var(--color-slate-500)}.badge{padding-inline:calc(var(--spacing)*2);padding-block:calc(var(--spacing)*.5);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);border-radius:3.40282e38px;align-items:center;display:inline-flex}.badge-success{background-color:var(--color-emerald-100);color:var(--color-emerald-700)}.badge-success:where(.dark,.dark *){background-color:#004e3b80}@supports (color:color-mix(in lab,red,red)){.badge-success:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-emerald-900)50%,transparent)}}.badge-success:where(.dark,.dark *){color:var(--color-emerald-400)}.badge-warning{background-color:var(--color-amber-100);color:var(--color-amber-700)}.badge-warning:where(.dark,.dark *){background-color:#7b330680}@supports (color:color-mix(in lab,red,red)){.badge-warning:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-amber-900)50%,transparent)}}.badge-warning:where(.dark,.dark *){color:var(--color-amber-400)}.badge-danger{background-color:var(--color-red-100);color:var(--color-red-700)}.badge-danger:where(.dark,.dark *){background-color:#82181a80}@supports (color:color-mix(in lab,red,red)){.badge-danger:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-900)50%,transparent)}}.badge-danger:where(.dark,.dark *){color:var(--color-red-400)}.card{border-radius:var(--radius-lg);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-slate-200);background-color:var(--color-white);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.card:where(.dark,.dark *){border-color:var(--color-slate-700);background-color:var(--color-slate-800)}}@layer utilities{.absolute{position:absolute}.relative{position:relative}.right-3{right:calc(var(--spacing)*3)}.left-3{left:calc(var(--spacing)*3)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-9{height:calc(var(--spacing)*9)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-full{height:100%}.h-screen{height:100vh}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-72{width:calc(var(--spacing)*72)}.w-full{width:100%}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-slate-100>:not(:last-child)){border-color:var(--color-slate-100)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-300{border-color:var(--color-slate-300)}.bg-amber-50\/50{background-color:#fffbeb80}@supports (color:color-mix(in lab,red,red)){.bg-amber-50\/50{background-color:color-mix(in oklab,var(--color-amber-50)50%,transparent)}}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-emerald-500{background-color:var(--color-emerald-500)}.bg-red-50\/30{background-color:#fef2f24d}@supports (color:color-mix(in lab,red,red)){.bg-red-50\/30{background-color:color-mix(in oklab,var(--color-red-50)30%,transparent)}}.bg-red-500{background-color:var(--color-red-500)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-white{background-color:var(--color-white)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.pt-0\.5{padding-top:calc(var(--spacing)*.5)}.pr-16{padding-right:calc(var(--spacing)*16)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-amber-500{color:var(--color-amber-500)}.text-amber-600{color:var(--color-amber-600)}.text-amber-700{color:var(--color-amber-700)}.text-blue-500{color:var(--color-blue-500)}.text-blue-600{color:var(--color-blue-600)}.text-blue-900{color:var(--color-blue-900)}.text-emerald-500{color:var(--color-emerald-500)}.text-emerald-600{color:var(--color-emerald-600)}.text-red-400{color:var(--color-red-400)}.text-red-600{color:var(--color-red-600)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.opacity-0{opacity:0}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-blue-200{--tw-ring-color:var(--color-blue-200)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}@media(hover:hover){.group-hover\/edit\:opacity-100:is(:where(.group\/edit):hover *){opacity:1}}.placeholder\:text-slate-400::placeholder{color:var(--color-slate-400)}@media(hover:hover){.hover\:bg-amber-200:hover{background-color:var(--color-amber-200)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500\/20:focus{--tw-ring-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.focus\:ring-blue-500\/20:focus{--tw-ring-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}:where(.dark\:divide-slate-700:where(.dark,.dark *)>:not(:last-child)){border-color:var(--color-slate-700)}.dark\:border-slate-600:where(.dark,.dark *){border-color:var(--color-slate-600)}.dark\:border-slate-700:where(.dark,.dark *){border-color:var(--color-slate-700)}.dark\:bg-amber-900\/10:where(.dark,.dark *){background-color:#7b33061a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-900\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-amber-900)10%,transparent)}}.dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:#7b33064d}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-amber-900)30%,transparent)}}.dark\:bg-blue-500:where(.dark,.dark *){background-color:var(--color-blue-500)}.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:#1c398e4d}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-900\/30:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-900)30%,transparent)}}.dark\:bg-red-900\/10:where(.dark,.dark *){background-color:#82181a1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-900\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-900)10%,transparent)}}.dark\:bg-slate-700:where(.dark,.dark *){background-color:var(--color-slate-700)}.dark\:bg-slate-800:where(.dark,.dark *){background-color:var(--color-slate-800)}.dark\:bg-slate-800\/50:where(.dark,.dark *){background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.dark\:bg-slate-800\/50:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.dark\:bg-slate-900:where(.dark,.dark *){background-color:var(--color-slate-900)}.dark\:text-amber-400:where(.dark,.dark *){color:var(--color-amber-400)}.dark\:text-blue-300:where(.dark,.dark *){color:var(--color-blue-300)}.dark\:text-blue-400:where(.dark,.dark *){color:var(--color-blue-400)}.dark\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.dark\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.dark\:text-slate-100:where(.dark,.dark *){color:var(--color-slate-100)}.dark\:text-slate-200:where(.dark,.dark *){color:var(--color-slate-200)}.dark\:text-slate-300:where(.dark,.dark *){color:var(--color-slate-300)}.dark\:text-slate-400:where(.dark,.dark *){color:var(--color-slate-400)}.dark\:text-slate-500:where(.dark,.dark *){color:var(--color-slate-500)}.dark\:text-slate-600:where(.dark,.dark *){color:var(--color-slate-600)}.dark\:text-white:where(.dark,.dark *){color:var(--color-white)}.dark\:ring-blue-700:where(.dark,.dark *){--tw-ring-color:var(--color-blue-700)}.dark\:placeholder\:text-slate-500:where(.dark,.dark *)::placeholder{color:var(--color-slate-500)}@media(hover:hover){.dark\:hover\:bg-amber-900\/50:where(.dark,.dark *):hover{background-color:#7b330680}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-amber-900\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-amber-900)50%,transparent)}}.dark\:hover\:bg-slate-600:where(.dark,.dark *):hover{background-color:var(--color-slate-600)}.dark\:hover\:bg-slate-700:where(.dark,.dark *):hover{background-color:var(--color-slate-700)}.dark\:hover\:bg-slate-700\/50:where(.dark,.dark *):hover{background-color:#31415880}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-slate-700\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-slate-700)50%,transparent)}}.dark\:hover\:bg-slate-800:where(.dark,.dark *):hover{background-color:var(--color-slate-800)}.dark\:hover\:bg-slate-800\/50:where(.dark,.dark *):hover{background-color:#1d293d80}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-slate-800\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-slate-800)50%,transparent)}}.dark\:hover\:text-slate-200:where(.dark,.dark *):hover{color:var(--color-slate-200)}.dark\:hover\:text-slate-300:where(.dark,.dark *):hover{color:var(--color-slate-300)}}.dark\:focus\:border-blue-400:where(.dark,.dark *):focus{border-color:var(--color-blue-400)}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
|