@rozenite/storage-plugin 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +20 -0
- package/README.md +77 -0
- package/dist/assets/panel-C7usUotG.js +22 -0
- package/dist/assets/panel-DMhXYHH4.css +1 -0
- package/dist/panel.html +31 -0
- package/dist/react-native.cjs +1 -0
- package/dist/react-native.d.ts +4 -0
- package/dist/react-native.js +200 -0
- package/dist/rozenite.config.d.ts +7 -0
- package/dist/rozenite.json +1 -0
- package/dist/src/react-native/adapters/async-storage.d.ts +33 -0
- package/dist/src/react-native/adapters/index.d.ts +6 -0
- package/dist/src/react-native/adapters/mmkv.d.ts +13 -0
- package/dist/src/react-native/adapters/secure-storage.d.ts +18 -0
- package/dist/src/react-native/is-garbled.d.ts +1 -0
- package/dist/src/react-native/storage-view.d.ts +19 -0
- package/dist/src/react-native/useRozeniteStoragePlugin.d.ts +6 -0
- package/dist/src/shared/messaging.d.ts +29 -0
- package/dist/src/shared/types.d.ts +60 -0
- package/dist/src/ui/add-entry-dialog.d.ts +9 -0
- package/dist/src/ui/confirm-dialog.d.ts +11 -0
- package/dist/src/ui/edit-entry-dialog.d.ts +9 -0
- package/dist/src/ui/editable-table.d.ts +10 -0
- package/dist/src/ui/entry-detail-dialog.d.ts +8 -0
- package/dist/src/ui/panel.d.ts +1 -0
- package/dist/types.cjs +1 -0
- package/dist/types.js +11 -0
- package/dist/useRozeniteStoragePlugin.cjs +1 -0
- package/dist/useRozeniteStoragePlugin.js +212 -0
- package/package.json +59 -0
- package/postcss.config.js +6 -0
- package/react-native.ts +40 -0
- package/rozenite.config.ts +8 -0
- package/src/css-modules.d.ts +4 -0
- package/src/react-native/adapters/async-storage.ts +115 -0
- package/src/react-native/adapters/index.ts +19 -0
- package/src/react-native/adapters/mmkv.ts +187 -0
- package/src/react-native/adapters/secure-storage.ts +76 -0
- package/src/react-native/is-garbled.ts +17 -0
- package/src/react-native/storage-view.ts +245 -0
- package/src/react-native/useRozeniteStoragePlugin.ts +162 -0
- package/src/shared/messaging.ts +37 -0
- package/src/shared/types.ts +66 -0
- package/src/ui/add-entry-dialog.tsx +310 -0
- package/src/ui/confirm-dialog.tsx +100 -0
- package/src/ui/edit-entry-dialog.tsx +280 -0
- package/src/ui/editable-table.tsx +298 -0
- package/src/ui/entry-detail-dialog.tsx +220 -0
- package/src/ui/globals.css +123 -0
- package/src/ui/panel.tsx +442 -0
- package/tailwind.config.ts +94 -0
- package/tsconfig.json +36 -0
- package/vite.config.ts +20 -0
package/src/ui/panel.tsx
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
2
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { Search, Plus } from 'lucide-react';
|
|
4
|
+
import type {
|
|
5
|
+
StorageDeleteEntryEvent,
|
|
6
|
+
StorageEventMap,
|
|
7
|
+
StorageSetEntryEvent,
|
|
8
|
+
StorageSnapshotEvent,
|
|
9
|
+
} from '../shared/messaging';
|
|
10
|
+
import type {
|
|
11
|
+
StorageCapabilities,
|
|
12
|
+
StorageEntry,
|
|
13
|
+
StorageEntryValue,
|
|
14
|
+
StorageTarget,
|
|
15
|
+
} from '../shared/types';
|
|
16
|
+
import { getStorageViewId } from '../shared/types';
|
|
17
|
+
import { EditableTable } from './editable-table';
|
|
18
|
+
import { AddEntryDialog } from './add-entry-dialog';
|
|
19
|
+
import { EntryDetailDialog } from './entry-detail-dialog';
|
|
20
|
+
import { EditEntryDialog } from './edit-entry-dialog';
|
|
21
|
+
import './globals.css';
|
|
22
|
+
|
|
23
|
+
type StorageSnapshotState = {
|
|
24
|
+
target: StorageTarget;
|
|
25
|
+
adapterName: string;
|
|
26
|
+
storageName: string;
|
|
27
|
+
capabilities: StorageCapabilities;
|
|
28
|
+
entries: StorageEntry[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getEntryTypeFromValue = (value: StorageEntryValue): StorageEntry['type'] => {
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
return 'string';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof value === 'number') {
|
|
37
|
+
return 'number';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (typeof value === 'boolean') {
|
|
41
|
+
return 'boolean';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return 'buffer';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default function StoragePanel() {
|
|
48
|
+
const [snapshots, setSnapshots] = useState<Map<string, StorageSnapshotState>>(
|
|
49
|
+
new Map()
|
|
50
|
+
);
|
|
51
|
+
const [selectedStorageViewId, setSelectedStorageViewId] = useState<string | null>(
|
|
52
|
+
null
|
|
53
|
+
);
|
|
54
|
+
const [loading, setLoading] = useState(false);
|
|
55
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
56
|
+
const [showAddDialog, setShowAddDialog] = useState(false);
|
|
57
|
+
const [selectedEntry, setSelectedEntry] = useState<StorageEntry | null>(null);
|
|
58
|
+
const [showDetailDialog, setShowDetailDialog] = useState(false);
|
|
59
|
+
const [editingEntry, setEditingEntry] = useState<StorageEntry | null>(null);
|
|
60
|
+
const [showEditDialog, setShowEditDialog] = useState(false);
|
|
61
|
+
|
|
62
|
+
const client = useRozeniteDevToolsClient<StorageEventMap>({
|
|
63
|
+
pluginId: '@rozenite/storage-plugin',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!client) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const snapshotSubscription = client.onMessage(
|
|
72
|
+
'snapshot',
|
|
73
|
+
(event: StorageSnapshotEvent) => {
|
|
74
|
+
const viewId = getStorageViewId(event.target);
|
|
75
|
+
setSnapshots((previous) => {
|
|
76
|
+
const next = new Map(previous);
|
|
77
|
+
next.set(viewId, {
|
|
78
|
+
target: event.target,
|
|
79
|
+
adapterName: event.adapterName,
|
|
80
|
+
storageName: event.storageName,
|
|
81
|
+
capabilities: event.capabilities,
|
|
82
|
+
entries: event.entries,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (previous.size === 0 && !selectedStorageViewId) {
|
|
86
|
+
setSelectedStorageViewId(viewId);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return next;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (viewId === selectedStorageViewId) {
|
|
93
|
+
setLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const setEntrySubscription = client.onMessage(
|
|
99
|
+
'set-entry',
|
|
100
|
+
(event: StorageSetEntryEvent) => {
|
|
101
|
+
const viewId = getStorageViewId(event.target);
|
|
102
|
+
setSnapshots((previous) => {
|
|
103
|
+
const next = new Map(previous);
|
|
104
|
+
const current = next.get(viewId);
|
|
105
|
+
|
|
106
|
+
if (!current) {
|
|
107
|
+
return previous;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const existingIndex = current.entries.findIndex(
|
|
111
|
+
(entry) => entry.key === event.entry.key
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const entries =
|
|
115
|
+
existingIndex >= 0
|
|
116
|
+
? current.entries.map((entry) =>
|
|
117
|
+
entry.key === event.entry.key ? event.entry : entry
|
|
118
|
+
)
|
|
119
|
+
: [...current.entries, event.entry];
|
|
120
|
+
|
|
121
|
+
next.set(viewId, {
|
|
122
|
+
...current,
|
|
123
|
+
entries,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return next;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const deleteEntrySubscription = client.onMessage(
|
|
132
|
+
'delete-entry',
|
|
133
|
+
(event: StorageDeleteEntryEvent) => {
|
|
134
|
+
const viewId = getStorageViewId(event.target);
|
|
135
|
+
|
|
136
|
+
setSnapshots((previous) => {
|
|
137
|
+
const next = new Map(previous);
|
|
138
|
+
const current = next.get(viewId);
|
|
139
|
+
|
|
140
|
+
if (!current) {
|
|
141
|
+
return previous;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
next.set(viewId, {
|
|
145
|
+
...current,
|
|
146
|
+
entries: current.entries.filter((entry) => entry.key !== event.key),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return next;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
client.send('get-snapshot', {
|
|
155
|
+
type: 'get-snapshot',
|
|
156
|
+
target: 'all',
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return () => {
|
|
160
|
+
snapshotSubscription.remove();
|
|
161
|
+
setEntrySubscription.remove();
|
|
162
|
+
deleteEntrySubscription.remove();
|
|
163
|
+
};
|
|
164
|
+
}, [client, selectedStorageViewId]);
|
|
165
|
+
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (!client || !selectedStorageViewId) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const selectedSnapshot = snapshots.get(selectedStorageViewId);
|
|
172
|
+
|
|
173
|
+
if (selectedSnapshot) {
|
|
174
|
+
setLoading(false);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const separatorIndex = selectedStorageViewId.indexOf(':');
|
|
179
|
+
if (separatorIndex < 0) {
|
|
180
|
+
console.warn(
|
|
181
|
+
`[Rozenite] Storage Plugin: Invalid storage view id "${selectedStorageViewId}".`
|
|
182
|
+
);
|
|
183
|
+
setLoading(false);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const adapterId = selectedStorageViewId.slice(0, separatorIndex);
|
|
188
|
+
const storageId = selectedStorageViewId.slice(separatorIndex + 1);
|
|
189
|
+
|
|
190
|
+
setLoading(true);
|
|
191
|
+
client.send('get-snapshot', {
|
|
192
|
+
type: 'get-snapshot',
|
|
193
|
+
target: {
|
|
194
|
+
adapterId,
|
|
195
|
+
storageId,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}, [client, selectedStorageViewId, snapshots]);
|
|
199
|
+
|
|
200
|
+
const selectedStorage = selectedStorageViewId
|
|
201
|
+
? snapshots.get(selectedStorageViewId) ?? null
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
const entries = selectedStorage?.entries ?? [];
|
|
205
|
+
|
|
206
|
+
const filteredEntries = useMemo(
|
|
207
|
+
() =>
|
|
208
|
+
entries.filter((entry) =>
|
|
209
|
+
entry.key.toLowerCase().includes(searchTerm.toLowerCase())
|
|
210
|
+
),
|
|
211
|
+
[entries, searchTerm]
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const supportedTypes = selectedStorage?.capabilities.supportedTypes ?? [];
|
|
215
|
+
|
|
216
|
+
const updateEntriesForSelectedStorage = (
|
|
217
|
+
mutate: (entries: StorageEntry[]) => StorageEntry[]
|
|
218
|
+
) => {
|
|
219
|
+
if (!selectedStorageViewId) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
setSnapshots((previous) => {
|
|
224
|
+
const next = new Map(previous);
|
|
225
|
+
const current = next.get(selectedStorageViewId);
|
|
226
|
+
|
|
227
|
+
if (!current) {
|
|
228
|
+
return previous;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
next.set(selectedStorageViewId, {
|
|
232
|
+
...current,
|
|
233
|
+
entries: mutate(current.entries),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return next;
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const handleValueChange = (key: string, newValue: StorageEntryValue) => {
|
|
241
|
+
if (!client || !selectedStorage) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const type = getEntryTypeFromValue(newValue);
|
|
246
|
+
|
|
247
|
+
if (!selectedStorage.capabilities.supportedTypes.includes(type)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let updatedEntry: StorageEntry;
|
|
252
|
+
if (type === 'string') {
|
|
253
|
+
updatedEntry = { key, type: 'string', value: newValue as string };
|
|
254
|
+
} else if (type === 'number') {
|
|
255
|
+
updatedEntry = { key, type: 'number', value: newValue as number };
|
|
256
|
+
} else if (type === 'boolean') {
|
|
257
|
+
updatedEntry = { key, type: 'boolean', value: newValue as boolean };
|
|
258
|
+
} else {
|
|
259
|
+
updatedEntry = { key, type: 'buffer', value: newValue as number[] };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
client.send('set-entry', {
|
|
263
|
+
type: 'set-entry',
|
|
264
|
+
target: selectedStorage.target,
|
|
265
|
+
entry: updatedEntry,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
updateEntriesForSelectedStorage((currentEntries) =>
|
|
269
|
+
currentEntries.map((entry) => (entry.key === key ? updatedEntry : entry))
|
|
270
|
+
);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const handleDeleteEntry = (key: string) => {
|
|
274
|
+
if (!client || !selectedStorage) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
client.send('delete-entry', {
|
|
279
|
+
type: 'delete-entry',
|
|
280
|
+
target: selectedStorage.target,
|
|
281
|
+
key,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
updateEntriesForSelectedStorage((currentEntries) =>
|
|
285
|
+
currentEntries.filter((entry) => entry.key !== key)
|
|
286
|
+
);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const handleAddEntry = (entry: StorageEntry) => {
|
|
290
|
+
if (!client || !selectedStorage) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
client.send('set-entry', {
|
|
295
|
+
type: 'set-entry',
|
|
296
|
+
target: selectedStorage.target,
|
|
297
|
+
entry,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
updateEntriesForSelectedStorage((currentEntries) => [...currentEntries, entry]);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const storageOptions = [...snapshots.entries()].map(([viewId, snapshot]) => ({
|
|
304
|
+
viewId,
|
|
305
|
+
label: `${snapshot.adapterName} / ${snapshot.storageName}`,
|
|
306
|
+
}));
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
<div className="h-screen bg-gray-900 text-gray-100 flex flex-col">
|
|
310
|
+
<div className="flex items-center gap-2 p-2 border-b border-gray-700 bg-gray-800">
|
|
311
|
+
<div className="flex items-center gap-2">
|
|
312
|
+
<span className="text-sm font-medium text-gray-200">Storage</span>
|
|
313
|
+
</div>
|
|
314
|
+
<div className="flex-1" />
|
|
315
|
+
<div className="flex items-center gap-2">
|
|
316
|
+
<label htmlFor="storage-select" className="text-xs text-gray-400">
|
|
317
|
+
Storage:
|
|
318
|
+
</label>
|
|
319
|
+
<select
|
|
320
|
+
id="storage-select"
|
|
321
|
+
value={selectedStorageViewId ?? ''}
|
|
322
|
+
onChange={(event) => setSelectedStorageViewId(event.target.value)}
|
|
323
|
+
disabled={snapshots.size === 0}
|
|
324
|
+
className="h-8 px-2 text-xs bg-gray-700 border border-gray-600 rounded text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
325
|
+
>
|
|
326
|
+
{snapshots.size === 0 ? (
|
|
327
|
+
<option>No storages found</option>
|
|
328
|
+
) : (
|
|
329
|
+
storageOptions.map((option) => (
|
|
330
|
+
<option key={option.viewId} value={option.viewId}>
|
|
331
|
+
{option.label}
|
|
332
|
+
</option>
|
|
333
|
+
))
|
|
334
|
+
)}
|
|
335
|
+
</select>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<div className="flex items-center gap-2 p-2 border-b border-gray-700 bg-gray-800">
|
|
340
|
+
<button
|
|
341
|
+
onClick={() => setShowAddDialog(true)}
|
|
342
|
+
disabled={!selectedStorage}
|
|
343
|
+
className="flex items-center gap-1 px-3 h-8 text-xs bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded transition-colors"
|
|
344
|
+
title="Add new entry"
|
|
345
|
+
>
|
|
346
|
+
<Plus className="h-3 w-3" />
|
|
347
|
+
Add Entry
|
|
348
|
+
</button>
|
|
349
|
+
<div className="flex-1">
|
|
350
|
+
<div className="relative">
|
|
351
|
+
<Search className="absolute left-2 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
|
352
|
+
<input
|
|
353
|
+
type="text"
|
|
354
|
+
placeholder="Search keys..."
|
|
355
|
+
value={searchTerm}
|
|
356
|
+
onChange={(event) => setSearchTerm(event.target.value)}
|
|
357
|
+
className="h-8 w-full pl-8 pr-3 text-sm bg-gray-700 border border-gray-600 rounded text-gray-100 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
358
|
+
/>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
<div className="flex items-center gap-2 text-xs text-gray-400">
|
|
362
|
+
{filteredEntries.length} of {entries.length} entries
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<main className="flex flex-1 min-h-0 overflow-auto">
|
|
367
|
+
{selectedStorage ? (
|
|
368
|
+
filteredEntries.length === 0 ? (
|
|
369
|
+
<div className="flex flex-col items-center justify-center h-full text-center w-full">
|
|
370
|
+
<h3 className="text-lg font-semibold text-gray-200 mb-2">
|
|
371
|
+
No entries found
|
|
372
|
+
</h3>
|
|
373
|
+
<p className="text-gray-400 text-sm">
|
|
374
|
+
{searchTerm
|
|
375
|
+
? 'Try adjusting your search terms'
|
|
376
|
+
: 'This storage appears to be empty'}
|
|
377
|
+
</p>
|
|
378
|
+
</div>
|
|
379
|
+
) : (
|
|
380
|
+
<EditableTable
|
|
381
|
+
data={filteredEntries}
|
|
382
|
+
supportedTypes={supportedTypes}
|
|
383
|
+
onValueChange={handleValueChange}
|
|
384
|
+
onDeleteEntry={handleDeleteEntry}
|
|
385
|
+
onRowClick={(entry) => {
|
|
386
|
+
setSelectedEntry(entry);
|
|
387
|
+
setShowDetailDialog(true);
|
|
388
|
+
}}
|
|
389
|
+
loading={loading}
|
|
390
|
+
/>
|
|
391
|
+
)
|
|
392
|
+
) : (
|
|
393
|
+
<div className="flex flex-col items-center justify-center h-full text-center w-full">
|
|
394
|
+
<h2 className="text-xl font-semibold text-gray-200 mb-2">
|
|
395
|
+
Welcome to Storage Inspector
|
|
396
|
+
</h2>
|
|
397
|
+
<p className="text-gray-400 text-sm">
|
|
398
|
+
Select a storage from the dropdown above to inspect data
|
|
399
|
+
</p>
|
|
400
|
+
</div>
|
|
401
|
+
)}
|
|
402
|
+
</main>
|
|
403
|
+
|
|
404
|
+
<AddEntryDialog
|
|
405
|
+
isOpen={showAddDialog}
|
|
406
|
+
onClose={() => setShowAddDialog(false)}
|
|
407
|
+
onAddEntry={handleAddEntry}
|
|
408
|
+
existingKeys={entries.map((entry) => entry.key)}
|
|
409
|
+
supportedTypes={supportedTypes}
|
|
410
|
+
/>
|
|
411
|
+
|
|
412
|
+
<EntryDetailDialog
|
|
413
|
+
isOpen={showDetailDialog}
|
|
414
|
+
onClose={() => {
|
|
415
|
+
setShowDetailDialog(false);
|
|
416
|
+
setSelectedEntry(null);
|
|
417
|
+
}}
|
|
418
|
+
onEdit={(entry) => {
|
|
419
|
+
setShowDetailDialog(false);
|
|
420
|
+
setEditingEntry(entry);
|
|
421
|
+
setShowEditDialog(true);
|
|
422
|
+
}}
|
|
423
|
+
entry={selectedEntry}
|
|
424
|
+
/>
|
|
425
|
+
|
|
426
|
+
<EditEntryDialog
|
|
427
|
+
isOpen={showEditDialog}
|
|
428
|
+
onClose={() => {
|
|
429
|
+
setShowEditDialog(false);
|
|
430
|
+
setEditingEntry(null);
|
|
431
|
+
}}
|
|
432
|
+
onEditEntry={(key, newValue) => {
|
|
433
|
+
handleValueChange(key, newValue);
|
|
434
|
+
setShowEditDialog(false);
|
|
435
|
+
setEditingEntry(null);
|
|
436
|
+
}}
|
|
437
|
+
supportedTypes={supportedTypes}
|
|
438
|
+
entry={editingEntry}
|
|
439
|
+
/>
|
|
440
|
+
</div>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { Config } from 'tailwindcss';
|
|
2
|
+
|
|
3
|
+
const config: Config = {
|
|
4
|
+
darkMode: ['class'],
|
|
5
|
+
content: ['./src/ui/**/*.{js,ts,jsx,tsx,mdx}'],
|
|
6
|
+
theme: {
|
|
7
|
+
extend: {
|
|
8
|
+
translate: {
|
|
9
|
+
'0.75': '0.1875rem',
|
|
10
|
+
},
|
|
11
|
+
colors: {
|
|
12
|
+
background: 'hsl(var(--background))',
|
|
13
|
+
foreground: 'hsl(var(--foreground))',
|
|
14
|
+
card: {
|
|
15
|
+
DEFAULT: 'hsl(var(--card))',
|
|
16
|
+
foreground: 'hsl(var(--card-foreground))',
|
|
17
|
+
},
|
|
18
|
+
popover: {
|
|
19
|
+
DEFAULT: 'hsl(var(--popover))',
|
|
20
|
+
foreground: 'hsl(var(--popover-foreground))',
|
|
21
|
+
},
|
|
22
|
+
primary: {
|
|
23
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
24
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
25
|
+
},
|
|
26
|
+
secondary: {
|
|
27
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
28
|
+
foreground: 'hsl(var(--secondary-foreground))',
|
|
29
|
+
},
|
|
30
|
+
muted: {
|
|
31
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
32
|
+
foreground: 'hsl(var(--muted-foreground))',
|
|
33
|
+
},
|
|
34
|
+
accent: {
|
|
35
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
36
|
+
foreground: 'hsl(var(--accent-foreground))',
|
|
37
|
+
},
|
|
38
|
+
destructive: {
|
|
39
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
40
|
+
foreground: 'hsl(var(--destructive-foreground))',
|
|
41
|
+
},
|
|
42
|
+
border: 'hsl(var(--border))',
|
|
43
|
+
input: 'hsl(var(--input))',
|
|
44
|
+
ring: 'hsl(var(--ring))',
|
|
45
|
+
chart: {
|
|
46
|
+
'1': 'hsl(var(--chart-1))',
|
|
47
|
+
'2': 'hsl(var(--chart-2))',
|
|
48
|
+
'3': 'hsl(var(--chart-3))',
|
|
49
|
+
'4': 'hsl(var(--chart-4))',
|
|
50
|
+
'5': 'hsl(var(--chart-5))',
|
|
51
|
+
},
|
|
52
|
+
sidebar: {
|
|
53
|
+
DEFAULT: 'hsl(var(--sidebar-background))',
|
|
54
|
+
foreground: 'hsl(var(--sidebar-foreground))',
|
|
55
|
+
primary: 'hsl(var(--sidebar-primary))',
|
|
56
|
+
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
|
57
|
+
accent: 'hsl(var(--sidebar-accent))',
|
|
58
|
+
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
|
59
|
+
border: 'hsl(var(--sidebar-border))',
|
|
60
|
+
ring: 'hsl(var(--sidebar-ring))',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
borderRadius: {
|
|
64
|
+
lg: 'var(--radius)',
|
|
65
|
+
md: 'calc(var(--radius) - 2px)',
|
|
66
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
67
|
+
},
|
|
68
|
+
keyframes: {
|
|
69
|
+
'accordion-down': {
|
|
70
|
+
from: {
|
|
71
|
+
height: '0',
|
|
72
|
+
},
|
|
73
|
+
to: {
|
|
74
|
+
height: 'var(--radix-accordion-content-height)',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
'accordion-up': {
|
|
78
|
+
from: {
|
|
79
|
+
height: 'var(--radix-accordion-content-height)',
|
|
80
|
+
},
|
|
81
|
+
to: {
|
|
82
|
+
height: '0',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
animation: {
|
|
87
|
+
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
88
|
+
'accordion-up': 'accordion-up 0.2s ease-out',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
plugins: [require('tailwindcss-animate')],
|
|
93
|
+
};
|
|
94
|
+
export default config;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"noFallthroughCasesInSwitch": true,
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"moduleResolution": "bundler",
|
|
14
|
+
"baseUrl": ".",
|
|
15
|
+
"paths": {
|
|
16
|
+
"@rozenite/plugin-bridge": ["../plugin-bridge/src/index.ts"]
|
|
17
|
+
},
|
|
18
|
+
"resolveJsonModule": true,
|
|
19
|
+
"isolatedModules": true,
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
"jsx": "react-jsx"
|
|
22
|
+
},
|
|
23
|
+
"include": ["src/**/*", "react-native.ts", "rozenite.config.ts"],
|
|
24
|
+
"exclude": ["node_modules", "dist", "build"],
|
|
25
|
+
"references": [
|
|
26
|
+
{
|
|
27
|
+
"path": "../plugin-bridge"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"path": "../cli"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"path": "../vite-plugin"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types='vitest' />
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import { rozenitePlugin } from '@rozenite/vite-plugin';
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
root: __dirname,
|
|
7
|
+
plugins: [rozenitePlugin()],
|
|
8
|
+
base: './',
|
|
9
|
+
build: {
|
|
10
|
+
outDir: './dist',
|
|
11
|
+
emptyOutDir: false,
|
|
12
|
+
reportCompressedSize: false,
|
|
13
|
+
minify: true,
|
|
14
|
+
sourcemap: false,
|
|
15
|
+
},
|
|
16
|
+
server: {
|
|
17
|
+
port: 3000,
|
|
18
|
+
open: true,
|
|
19
|
+
},
|
|
20
|
+
});
|