rune-lab 0.0.1 → 0.0.2-alpha-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/dist/components/UIShowcase.svelte +1 -2
- package/dist/components/dt/Altharun.svelte +96 -0
- package/dist/components/dt/Altharun.svelte.d.ts +3 -0
- package/dist/components/dt/Kyntharil.svelte +337 -0
- package/dist/components/dt/Kyntharil.svelte.d.ts +3 -0
- package/dist/components/layout/Footer.svelte +77 -104
- package/dist/components/layout/Footer.svelte.d.ts +2 -17
- package/dist/components/layout/NavBar.svelte +134 -0
- package/dist/components/layout/NavBar.svelte.d.ts +3 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +19 -3
- package/dist/stores/app.svelte.js +1 -1
- package/dist/stores/auth.svelte.d.ts +19 -0
- package/dist/stores/auth.svelte.js +26 -0
- package/dist/stores/db.svelte.d.ts +27 -0
- package/dist/stores/db.svelte.js +52 -0
- package/dist/stores/layout/footer.svelte.d.ts +35 -0
- package/dist/stores/layout/footer.svelte.js +73 -0
- package/package.json +75 -74
|
@@ -58,8 +58,6 @@
|
|
|
58
58
|
];
|
|
59
59
|
</script>
|
|
60
60
|
|
|
61
|
-
<ThemeSelector />
|
|
62
|
-
|
|
63
61
|
<div class="min-h-screen bg-base-200 p-4">
|
|
64
62
|
<!-- Header with Theme Demo -->
|
|
65
63
|
<div class="text-center mb-8 hero bg-base-100 rounded-box p-8">
|
|
@@ -70,6 +68,7 @@
|
|
|
70
68
|
<div class="flex justify-center gap-2">
|
|
71
69
|
<button class="btn btn-primary" onclick={() => modalOpen = true}>Open Modal</button>
|
|
72
70
|
<button class="btn btn-secondary" onclick={() => drawerOpen = true}>Open Drawer</button>
|
|
71
|
+
<ThemeSelector />
|
|
73
72
|
</div>
|
|
74
73
|
</div>
|
|
75
74
|
</div>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<!-- Altharun (from Old Norse "aldir" = age, wisdom) -->
|
|
2
|
+
<!-- src/lib/components/data/Altharun.svelte -->
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { onMount } from 'svelte';
|
|
5
|
+
import { databaseStore } from 'rune-lab';
|
|
6
|
+
|
|
7
|
+
onMount(async () => {
|
|
8
|
+
await databaseStore.init();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Debug the schemas whenever they change
|
|
12
|
+
$effect(() => {
|
|
13
|
+
console.log('Current schemas:', Object.keys(databaseStore.schemas));
|
|
14
|
+
if (databaseStore.activeSchema) {
|
|
15
|
+
console.log('Active schema tables:',
|
|
16
|
+
Object.keys(databaseStore.schemas[databaseStore.activeSchema]));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div class="container mx-auto px-4 py-8">
|
|
22
|
+
<h1 class="text-3xl font-bold mb-8">Database Management</h1>
|
|
23
|
+
|
|
24
|
+
{#each Object.keys(databaseStore.schemas) as schemaName}
|
|
25
|
+
{@const currentSchema = databaseStore.schemas[schemaName]}
|
|
26
|
+
<div class="mb-6">
|
|
27
|
+
<button
|
|
28
|
+
class="w-full flex items-center justify-between p-4 bg-base-200 rounded-lg"
|
|
29
|
+
onclick={() => databaseStore.toggleSchema(schemaName)}
|
|
30
|
+
>
|
|
31
|
+
<span class="text-xl font-semibold">{schemaName}</span>
|
|
32
|
+
<span class="transform transition-transform duration-200"
|
|
33
|
+
class:rotate-180={databaseStore.activeSchema === schemaName}>
|
|
34
|
+
▼
|
|
35
|
+
</span>
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
{#if databaseStore.activeSchema === schemaName && currentSchema}
|
|
39
|
+
<div class="mt-4 space-y-2 pl-4">
|
|
40
|
+
{#each Object.keys(currentSchema) as tableName}
|
|
41
|
+
<div class="card bg-base-100 shadow-lg hover:shadow-xl transition-all">
|
|
42
|
+
<div class="card-body">
|
|
43
|
+
<div class="flex justify-between items-center mb-4">
|
|
44
|
+
<h3 class="card-title text-xl">{tableName}</h3>
|
|
45
|
+
<span class="badge badge-outline">{currentSchema[tableName].columns.length} columns</span>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="overflow-x-auto">
|
|
49
|
+
<table class="table table-sm w-full">
|
|
50
|
+
<thead>
|
|
51
|
+
<tr>
|
|
52
|
+
<th>Column</th>
|
|
53
|
+
<th>Type</th>
|
|
54
|
+
<th class="text-center">Key</th>
|
|
55
|
+
<th class="text-center">Required</th>
|
|
56
|
+
</tr>
|
|
57
|
+
</thead>
|
|
58
|
+
<tbody>
|
|
59
|
+
{#each currentSchema[tableName].columns as column}
|
|
60
|
+
<tr class="hover">
|
|
61
|
+
<td class="font-mono">{column.name}</td>
|
|
62
|
+
<td class="text-sm opacity-75">{column.type}</td>
|
|
63
|
+
<td class="text-center">
|
|
64
|
+
{#if column.is_primary_key}
|
|
65
|
+
<span class="badge badge-primary badge-sm">PK</span>
|
|
66
|
+
{:else if column.is_foreign_key}
|
|
67
|
+
<span class="badge badge-secondary badge-sm">FK</span>
|
|
68
|
+
{/if}
|
|
69
|
+
</td>
|
|
70
|
+
<td class="text-center">
|
|
71
|
+
{#if column.is_primary_key || !column.nullable}
|
|
72
|
+
<span class="text-error">*</span>
|
|
73
|
+
{/if}
|
|
74
|
+
</td>
|
|
75
|
+
</tr>
|
|
76
|
+
{/each}
|
|
77
|
+
</tbody>
|
|
78
|
+
</table>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="card-actions justify-end mt-4">
|
|
82
|
+
<button
|
|
83
|
+
class="btn btn-sm btn-primary"
|
|
84
|
+
onclick={() => databaseStore.setActiveTable(schemaName, tableName)}
|
|
85
|
+
>
|
|
86
|
+
Manage Table
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
{/each}
|
|
92
|
+
</div>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
95
|
+
{/each}
|
|
96
|
+
</div>
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<!-- Kyntharil (from Old Norse "kyn" = kind, kin + suffix -tharil suggesting evolution) -->
|
|
2
|
+
<!-- src/lib/components/dt/Kyntharil.svelte -->
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { Gear, Database, User, X } from 'phosphor-svelte';
|
|
5
|
+
import { appData } from '../../stores/app.svelte.js';
|
|
6
|
+
import { apiStore } from '../../stores/api.svelte.js';
|
|
7
|
+
import { authStore } from '../../stores/auth.svelte.js';
|
|
8
|
+
|
|
9
|
+
// Local state management with runes
|
|
10
|
+
let isOpen = $state(false);
|
|
11
|
+
let activeTab = $state('app');
|
|
12
|
+
let testApiConnection = $state(false);
|
|
13
|
+
let showToast = $state(false);
|
|
14
|
+
let toastMessage = $state('');
|
|
15
|
+
let toastType = $state<'success' | 'error'>('success');
|
|
16
|
+
|
|
17
|
+
// Form states using runes
|
|
18
|
+
let appForm = $state({
|
|
19
|
+
name: appData.name,
|
|
20
|
+
version: appData.version,
|
|
21
|
+
description: appData.description,
|
|
22
|
+
author: appData.author
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
let apiForm = $state({
|
|
26
|
+
URL: apiStore.URL,
|
|
27
|
+
VERSION: apiStore.VERSION,
|
|
28
|
+
TIMEOUT: apiStore.TIMEOUT
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let authForm = $state({
|
|
32
|
+
id: authStore.user?.id || '',
|
|
33
|
+
name: authStore.user?.name || '',
|
|
34
|
+
email: authStore.user?.email || '',
|
|
35
|
+
role: authStore.user?.role || 'user'
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Toast handler
|
|
39
|
+
function displayToast(message: string, type: 'success' | 'error' = 'success') {
|
|
40
|
+
toastMessage = message;
|
|
41
|
+
toastType = type;
|
|
42
|
+
showToast = true;
|
|
43
|
+
setTimeout(() => showToast = false, 3000);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Form submission handlers
|
|
47
|
+
function handleAppSubmit() {
|
|
48
|
+
appData.init(appForm);
|
|
49
|
+
displayToast('App settings updated successfully');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function handleApiSubmit() {
|
|
53
|
+
apiStore.init(apiForm);
|
|
54
|
+
if (testApiConnection) {
|
|
55
|
+
try {
|
|
56
|
+
await apiStore.checkConnection();
|
|
57
|
+
displayToast(
|
|
58
|
+
apiStore.IS_CONNECTED ? 'API connected successfully' : 'API connection failed',
|
|
59
|
+
apiStore.IS_CONNECTED ? 'success' : 'error'
|
|
60
|
+
);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
displayToast('API connection check failed', 'error');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function handleAuthSubmit() {
|
|
68
|
+
if (authStore.isAuthenticated) {
|
|
69
|
+
authStore.login({
|
|
70
|
+
id: authForm.id,
|
|
71
|
+
name: authForm.name,
|
|
72
|
+
email: authForm.email,
|
|
73
|
+
role: authForm.role as 'admin' | 'staff' | 'user'
|
|
74
|
+
});
|
|
75
|
+
displayToast('Auth settings updated successfully');
|
|
76
|
+
} else {
|
|
77
|
+
displayToast('Please log in first', 'error');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Tab configuration
|
|
82
|
+
const tabs = [
|
|
83
|
+
{ id: 'app', icon: Gear, label: 'App Settings' },
|
|
84
|
+
{ id: 'api', icon: Database, label: 'API Configuration' },
|
|
85
|
+
{ id: 'auth', icon: User, label: 'Auth Management' }
|
|
86
|
+
];
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<div class="fixed bottom-4 right-4 z-50">
|
|
90
|
+
<!-- Toggle Button -->
|
|
91
|
+
<button
|
|
92
|
+
class="btn btn-circle btn-lg btn-primary"
|
|
93
|
+
onclick={() => isOpen = !isOpen}
|
|
94
|
+
aria-label="Toggle Store Manager"
|
|
95
|
+
>
|
|
96
|
+
<Gear size={24} class={isOpen ? 'animate-spin' : ''} />
|
|
97
|
+
</button>
|
|
98
|
+
|
|
99
|
+
<!-- Manager Panel -->
|
|
100
|
+
{#if isOpen}
|
|
101
|
+
<div class="fixed inset-0 bg-base-300 bg-opacity-50 backdrop-blur-sm">
|
|
102
|
+
<div class="absolute right-0 top-0 h-full w-96 bg-base-100 shadow-xl">
|
|
103
|
+
<!-- Header -->
|
|
104
|
+
<div class="flex items-center justify-between p-4 border-b border-base-200">
|
|
105
|
+
<h2 class="text-xl font-bold">Store Manager</h2>
|
|
106
|
+
<button
|
|
107
|
+
class="btn btn-ghost btn-circle"
|
|
108
|
+
onclick={() => isOpen = false}
|
|
109
|
+
aria-label="Close"
|
|
110
|
+
>
|
|
111
|
+
<X size={20} />
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- Tabs -->
|
|
116
|
+
<div class="tabs tabs-boxed m-4">
|
|
117
|
+
{#each tabs as tab}
|
|
118
|
+
<button
|
|
119
|
+
class="tab flex-1 {activeTab === tab.id ? 'tab-active' : ''}"
|
|
120
|
+
onclick={() => activeTab = tab.id}
|
|
121
|
+
>
|
|
122
|
+
<tab.icon size={16} class="mr-2" />
|
|
123
|
+
{tab.label}
|
|
124
|
+
</button>
|
|
125
|
+
{/each}
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<!-- Content -->
|
|
129
|
+
<div class="p-4">
|
|
130
|
+
{#if activeTab === 'app'}
|
|
131
|
+
<form class="space-y-4" onsubmit={handleAppSubmit}>
|
|
132
|
+
<div class="form-control">
|
|
133
|
+
<label class="label" for="app-name">
|
|
134
|
+
<span class="label-text">App Name</span>
|
|
135
|
+
</label>
|
|
136
|
+
<input
|
|
137
|
+
id="app-name"
|
|
138
|
+
type="text"
|
|
139
|
+
class="input input-bordered"
|
|
140
|
+
bind:value={appForm.name}
|
|
141
|
+
/>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div class="form-control">
|
|
145
|
+
<label class="label" for="app-version">
|
|
146
|
+
<span class="label-text">Version</span>
|
|
147
|
+
</label>
|
|
148
|
+
<input
|
|
149
|
+
id="app-version"
|
|
150
|
+
type="text"
|
|
151
|
+
class="input input-bordered"
|
|
152
|
+
bind:value={appForm.version}
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="form-control">
|
|
157
|
+
<label class="label" for="app-description">
|
|
158
|
+
<span class="label-text">Description</span>
|
|
159
|
+
</label>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="form-control">
|
|
163
|
+
<label class="label" for="app-author">
|
|
164
|
+
<span class="label-text">Author</span>
|
|
165
|
+
</label>
|
|
166
|
+
<input
|
|
167
|
+
id="app-author"
|
|
168
|
+
type="text"
|
|
169
|
+
class="input input-bordered"
|
|
170
|
+
bind:value={appForm.author}
|
|
171
|
+
/>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<button type="submit" class="btn btn-primary w-full">
|
|
175
|
+
Update App Settings
|
|
176
|
+
</button>
|
|
177
|
+
</form>
|
|
178
|
+
|
|
179
|
+
{:else if activeTab === 'api'}
|
|
180
|
+
<form class="space-y-4" onsubmit={handleApiSubmit}>
|
|
181
|
+
<div class="form-control">
|
|
182
|
+
<label class="label" for="api-url">
|
|
183
|
+
<span class="label-text">API URL</span>
|
|
184
|
+
</label>
|
|
185
|
+
<input
|
|
186
|
+
id="api-url"
|
|
187
|
+
type="url"
|
|
188
|
+
class="input input-bordered"
|
|
189
|
+
bind:value={apiForm.URL}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div class="form-control">
|
|
194
|
+
<label class="label" for="api-version">
|
|
195
|
+
<span class="label-text">API Version</span>
|
|
196
|
+
</label>
|
|
197
|
+
<input
|
|
198
|
+
id="api-version"
|
|
199
|
+
type="text"
|
|
200
|
+
class="input input-bordered"
|
|
201
|
+
bind:value={apiForm.VERSION}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div class="form-control">
|
|
206
|
+
<label class="label" for="api-timeout">
|
|
207
|
+
<span class="label-text">Timeout (ms)</span>
|
|
208
|
+
</label>
|
|
209
|
+
<input
|
|
210
|
+
id="api-timeout"
|
|
211
|
+
type="number"
|
|
212
|
+
class="input input-bordered"
|
|
213
|
+
bind:value={apiForm.TIMEOUT}
|
|
214
|
+
/>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div class="form-control">
|
|
218
|
+
<label class="label cursor-pointer">
|
|
219
|
+
<span class="label-text">Test connection after update</span>
|
|
220
|
+
<input
|
|
221
|
+
type="checkbox"
|
|
222
|
+
class="toggle toggle-primary"
|
|
223
|
+
bind:checked={testApiConnection}
|
|
224
|
+
/>
|
|
225
|
+
</label>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div class="flex gap-2">
|
|
229
|
+
<button type="submit" class="btn btn-primary flex-1">
|
|
230
|
+
Update API Settings
|
|
231
|
+
</button>
|
|
232
|
+
<button
|
|
233
|
+
type="button"
|
|
234
|
+
class="btn btn-outline"
|
|
235
|
+
onclick={() => apiStore.checkConnection()}
|
|
236
|
+
>
|
|
237
|
+
Test Connection
|
|
238
|
+
</button>
|
|
239
|
+
</div>
|
|
240
|
+
</form>
|
|
241
|
+
|
|
242
|
+
{:else if activeTab === 'auth'}
|
|
243
|
+
<form class="space-y-4" onsubmit={handleAuthSubmit}>
|
|
244
|
+
<div class="form-control">
|
|
245
|
+
<label class="label" for="auth-id">
|
|
246
|
+
<span class="label-text">User ID</span>
|
|
247
|
+
</label>
|
|
248
|
+
<input
|
|
249
|
+
id="auth-id"
|
|
250
|
+
type="text"
|
|
251
|
+
class="input input-bordered"
|
|
252
|
+
bind:value={authForm.id}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<div class="form-control">
|
|
257
|
+
<label class="label" for="auth-name">
|
|
258
|
+
<span class="label-text">Name</span>
|
|
259
|
+
</label>
|
|
260
|
+
<input
|
|
261
|
+
id="auth-name"
|
|
262
|
+
type="text"
|
|
263
|
+
class="input input-bordered"
|
|
264
|
+
bind:value={authForm.name}
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<div class="form-control">
|
|
269
|
+
<label class="label" for="auth-email">
|
|
270
|
+
<span class="label-text">Email</span>
|
|
271
|
+
</label>
|
|
272
|
+
<input
|
|
273
|
+
id="auth-email"
|
|
274
|
+
type="email"
|
|
275
|
+
class="input input-bordered"
|
|
276
|
+
bind:value={authForm.email}
|
|
277
|
+
/>
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
<div class="form-control">
|
|
281
|
+
<label class="label" for="auth-role">
|
|
282
|
+
<span class="label-text">Role</span>
|
|
283
|
+
</label>
|
|
284
|
+
<select
|
|
285
|
+
id="auth-role"
|
|
286
|
+
class="select select-bordered"
|
|
287
|
+
bind:value={authForm.role}
|
|
288
|
+
>
|
|
289
|
+
<option value="user">User</option>
|
|
290
|
+
<option value="staff">Staff</option>
|
|
291
|
+
<option value="admin">Admin</option>
|
|
292
|
+
</select>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div class="flex gap-2">
|
|
296
|
+
<button type="submit" class="btn btn-primary flex-1">
|
|
297
|
+
Update Auth Settings
|
|
298
|
+
</button>
|
|
299
|
+
<button
|
|
300
|
+
type="button"
|
|
301
|
+
class="btn btn-outline"
|
|
302
|
+
onclick={() => authStore.logout()}
|
|
303
|
+
>
|
|
304
|
+
Logout
|
|
305
|
+
</button>
|
|
306
|
+
</div>
|
|
307
|
+
</form>
|
|
308
|
+
{/if}
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<!-- Status Bar -->
|
|
312
|
+
<div class="absolute bottom-0 w-full p-4 bg-base-200 border-t border-base-300">
|
|
313
|
+
<div class="flex items-center justify-between text-sm">
|
|
314
|
+
<div class="flex items-center gap-2">
|
|
315
|
+
<div class="badge badge-sm {apiStore.IS_CONNECTED ? 'badge-success' : 'badge-error'}">
|
|
316
|
+
API {apiStore.IS_CONNECTED ? 'Connected' : 'Disconnected'}
|
|
317
|
+
</div>
|
|
318
|
+
<div class="badge badge-sm {authStore.isAuthenticated ? 'badge-success' : 'badge-error'}">
|
|
319
|
+
{authStore.isAuthenticated ? 'Authenticated' : 'Not Authenticated'}
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
<span class="opacity-50">v{appData.version}</span>
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
{/if}
|
|
328
|
+
|
|
329
|
+
<!-- Toast Notification -->
|
|
330
|
+
{#if showToast}
|
|
331
|
+
<div class="toast toast-end">
|
|
332
|
+
<div class="alert alert-{toastType}">
|
|
333
|
+
<span>{toastMessage}</span>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
{/if}
|
|
337
|
+
</div>
|
|
@@ -1,115 +1,88 @@
|
|
|
1
1
|
<!-- src/lib/components/layout/Footer.svelte -->
|
|
2
2
|
<script lang="ts">
|
|
3
3
|
import { appData } from '../../stores/app.svelte.js';
|
|
4
|
+
import { footerStore } from '../../stores/layout/footer.svelte.js';
|
|
4
5
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
href: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface FooterSection {
|
|
12
|
-
title: string;
|
|
13
|
-
links: FooterLink[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Footer sections configuration
|
|
17
|
-
const footerSections: FooterSection[] = [
|
|
18
|
-
{
|
|
19
|
-
title: "Product",
|
|
20
|
-
links: [
|
|
21
|
-
{ text: "Features", href: "/features" },
|
|
22
|
-
{ text: "Docs", href: "/docs" },
|
|
23
|
-
{ text: "API", href: "/api" },
|
|
24
|
-
{ text: "Pricing", href: "/pricing" }
|
|
25
|
-
]
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
title: "Company",
|
|
29
|
-
links: [
|
|
30
|
-
{ text: "About", href: "/about" },
|
|
31
|
-
{ text: "Blog", href: "/blog" },
|
|
32
|
-
{ text: "Careers", href: "/careers" },
|
|
33
|
-
{ text: "Contact", href: "/contact" }
|
|
34
|
-
]
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
title: "Resources",
|
|
38
|
-
links: [
|
|
39
|
-
{ text: "Community", href: "/community" },
|
|
40
|
-
{ text: "Help Center", href: "/help" },
|
|
41
|
-
{ text: "Status", href: "/status" },
|
|
42
|
-
{ text: "Terms", href: "/terms" }
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
];
|
|
46
|
-
|
|
6
|
+
// Use $derived for reactive configuration
|
|
7
|
+
let config = $derived(footerStore.config);
|
|
8
|
+
|
|
47
9
|
// Current year for copyright
|
|
48
10
|
const currentYear = new Date().getFullYear();
|
|
49
11
|
</script>
|
|
50
12
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
13
|
+
{#if config.sections.length > 0}
|
|
14
|
+
<footer class="footer p-10 bg-base-200 text-base-content">
|
|
15
|
+
<!-- Logo and company info -->
|
|
16
|
+
<aside class="flex flex-col items-start gap-4">
|
|
17
|
+
<img
|
|
18
|
+
src="/favicon.png"
|
|
19
|
+
alt="{appData.name} Logo"
|
|
20
|
+
class="h-12 w-12 rounded-lg hover:animate-pulse"
|
|
21
|
+
/>
|
|
22
|
+
<div>
|
|
23
|
+
<h2 class="text-lg font-bold">{appData.name}</h2>
|
|
24
|
+
<p class="text-sm opacity-75">
|
|
25
|
+
Building better software<br/>
|
|
26
|
+
since {currentYear}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
66
29
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
30
|
+
<!-- Social media links -->
|
|
31
|
+
{#if config.showSocialLinks}
|
|
32
|
+
<div class="flex gap-4">
|
|
33
|
+
{#if config.socialLinks.github}
|
|
34
|
+
<a href={config.socialLinks.github}
|
|
35
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
36
|
+
aria-label="Visit GitHub profile">
|
|
37
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
38
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
|
39
|
+
</svg>
|
|
40
|
+
</a>
|
|
41
|
+
{/if}
|
|
42
|
+
|
|
43
|
+
{#if config.socialLinks.twitter}
|
|
44
|
+
<a href={config.socialLinks.twitter}
|
|
45
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
46
|
+
aria-label="Visit Twitter profile">
|
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
48
|
+
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
|
49
|
+
</svg>
|
|
50
|
+
</a>
|
|
51
|
+
{/if}
|
|
52
|
+
|
|
53
|
+
{#if config.socialLinks.linkedin}
|
|
54
|
+
<a href={config.socialLinks.linkedin}
|
|
55
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
56
|
+
aria-label="Visit LinkedIn profile">
|
|
57
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
58
|
+
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
|
|
59
|
+
</svg>
|
|
60
|
+
</a>
|
|
61
|
+
{/if}
|
|
62
|
+
</div>
|
|
63
|
+
{/if}
|
|
64
|
+
</aside>
|
|
97
65
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</footer>
|
|
66
|
+
<!-- Navigation sections -->
|
|
67
|
+
{#each config.sections as section}
|
|
68
|
+
<nav>
|
|
69
|
+
<h6 class="footer-title">{section.title}</h6>
|
|
70
|
+
{#each section.links as link}
|
|
71
|
+
<a href={link.href} class="link link-hover">{link.text}</a>
|
|
72
|
+
{/each}
|
|
73
|
+
</nav>
|
|
74
|
+
{/each}
|
|
75
|
+
</footer>
|
|
108
76
|
|
|
109
|
-
<!-- Copyright footer -->
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</
|
|
77
|
+
<!-- Copyright footer -->
|
|
78
|
+
{#if config.showCopyright}
|
|
79
|
+
<footer class="footer footer-center p-4 bg-base-300 text-base-content">
|
|
80
|
+
<aside class="flex flex-col items-center gap-2">
|
|
81
|
+
<p>Copyright © {currentYear} {appData.name} - All rights reserved</p>
|
|
82
|
+
{#if config.showVersion}
|
|
83
|
+
<p class="text-sm opacity-75">Version {appData.version}</p>
|
|
84
|
+
{/if}
|
|
85
|
+
</aside>
|
|
86
|
+
</footer>
|
|
87
|
+
{/if}
|
|
88
|
+
{/if}
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 Footer: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
-
[evt: string]: CustomEvent<any>;
|
|
16
|
-
}, {}, {}, string>;
|
|
17
|
-
type Footer = InstanceType<typeof Footer>;
|
|
1
|
+
declare const Footer: import("svelte").Component<Record<string, never>, {}, "">;
|
|
2
|
+
type Footer = ReturnType<typeof Footer>;
|
|
18
3
|
export default Footer;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
MagnifyingGlass,
|
|
4
|
+
Gear,
|
|
5
|
+
User,
|
|
6
|
+
// Pills,
|
|
7
|
+
// Activity,
|
|
8
|
+
SignIn,
|
|
9
|
+
SignOut,
|
|
10
|
+
Bell,
|
|
11
|
+
ShoppingCart
|
|
12
|
+
} from 'phosphor-svelte';
|
|
13
|
+
|
|
14
|
+
// Import stores
|
|
15
|
+
import { appData } from '../../stores/app.svelte.js';
|
|
16
|
+
import { authStore } from '../../stores/auth.svelte.js';
|
|
17
|
+
import { themeStore } from '../../stores/theme.svelte.js';
|
|
18
|
+
|
|
19
|
+
// Optional components you can import
|
|
20
|
+
import ThemeSelector from './ThemeSelector.svelte';
|
|
21
|
+
|
|
22
|
+
// State using Runes
|
|
23
|
+
let isSearchActive = $state(false);
|
|
24
|
+
let searchQuery = $state('');
|
|
25
|
+
let notificationCount = $state(3);
|
|
26
|
+
|
|
27
|
+
// Navigation items with proper typing
|
|
28
|
+
type NavItem = {
|
|
29
|
+
icon: any; // PhosphorIcon type
|
|
30
|
+
label: string;
|
|
31
|
+
path: string;
|
|
32
|
+
requiresAuth: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function handleSearch(e: KeyboardEvent) {
|
|
36
|
+
if (e.key === 'Enter' && searchQuery.trim()) {
|
|
37
|
+
// Handle search - you might want to emit an event
|
|
38
|
+
isSearchActive = false;
|
|
39
|
+
} else if (e.key === 'Escape') {
|
|
40
|
+
isSearchActive = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function handleLogin() {
|
|
45
|
+
// Example login
|
|
46
|
+
authStore.login({
|
|
47
|
+
id: '1',
|
|
48
|
+
name: 'Test User',
|
|
49
|
+
email: 'test@example.com',
|
|
50
|
+
role: 'staff'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<nav class="navbar bg-base-100">
|
|
56
|
+
<!-- Logo Section -->
|
|
57
|
+
<div class="flex-1">
|
|
58
|
+
<a href="/" class="btn btn-ghost normal-case text-xl gap-2">
|
|
59
|
+
<div class="w-10 h-10">
|
|
60
|
+
<img
|
|
61
|
+
src="/favicon.png"
|
|
62
|
+
alt="{appData.name} Logo"
|
|
63
|
+
class="w-full h-full object-contain rounded-full hover:animate-spin"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
<span>{appData.name}</span>
|
|
67
|
+
</a>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="flex-none gap-2">
|
|
71
|
+
|
|
72
|
+
<!-- Search -->
|
|
73
|
+
{#if isSearchActive}
|
|
74
|
+
<div class="form-control">
|
|
75
|
+
<input
|
|
76
|
+
type="text"
|
|
77
|
+
placeholder="Search..."
|
|
78
|
+
bind:value={searchQuery}
|
|
79
|
+
onkeydown={handleSearch}
|
|
80
|
+
class="input input-bordered"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
{:else}
|
|
84
|
+
<button
|
|
85
|
+
class="btn btn-ghost btn-circle"
|
|
86
|
+
onclick={() => isSearchActive = true}
|
|
87
|
+
aria-label="Search"
|
|
88
|
+
>
|
|
89
|
+
<MagnifyingGlass size={20} weight="bold" />
|
|
90
|
+
</button>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
93
|
+
<!-- Authenticated Content -->
|
|
94
|
+
{#if authStore.isAuthenticated}
|
|
95
|
+
<!-- Notifications -->
|
|
96
|
+
<div class="dropdown dropdown-end">
|
|
97
|
+
<button class="btn btn-ghost btn-circle" aria-label="Notifications">
|
|
98
|
+
<div class="indicator">
|
|
99
|
+
<Bell size={20} weight="bold" />
|
|
100
|
+
{#if notificationCount > 0}
|
|
101
|
+
<span class="badge badge-sm indicator-item">{notificationCount}</span>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
</button>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- User Menu -->
|
|
108
|
+
<div class="dropdown dropdown-end">
|
|
109
|
+
<button class="btn btn-ghost btn-circle avatar" aria-label="User menu">
|
|
110
|
+
<div class="w-10 rounded-full">
|
|
111
|
+
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed={authStore.user?.name}" alt="User avatar" />
|
|
112
|
+
</div>
|
|
113
|
+
</button>
|
|
114
|
+
<ul class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
|
115
|
+
<li><a href="/profile"><User size={16} weight="bold" /> Profile</a></li>
|
|
116
|
+
<li><a href="/settings"><Gear size={16} weight="bold" /> Settings</a></li>
|
|
117
|
+
<li><button onclick={() => authStore.logout()}><SignOut size={16} weight="bold" /> Logout</button></li>
|
|
118
|
+
</ul>
|
|
119
|
+
</div>
|
|
120
|
+
{:else}
|
|
121
|
+
<!-- Login Button -->
|
|
122
|
+
<button class="btn btn-primary btn-sm" onclick={handleLogin}>
|
|
123
|
+
<SignIn size={16} weight="bold" />
|
|
124
|
+
Sign In
|
|
125
|
+
</button>
|
|
126
|
+
{/if}
|
|
127
|
+
|
|
128
|
+
<!-- Theme Selector -->
|
|
129
|
+
<!-- if isAuthenticated show the theme selector -->
|
|
130
|
+
{#if authStore.isAuthenticated}
|
|
131
|
+
<ThemeSelector />
|
|
132
|
+
{/if}
|
|
133
|
+
</div>
|
|
134
|
+
</nav>
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export { default as UIShowcase } from './components/UIShowcase.svelte';
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
2
|
+
export { default as Altharun } from './components/dt/Altharun.svelte';
|
|
3
|
+
export { default as Kyntharil } from './components/dt/Kyntharil.svelte';
|
|
4
|
+
export { default as NavBar } from './components/layout/NavBar.svelte';
|
|
4
5
|
export { default as UrlDisplay } from './components/layout/URLDisplay.svelte';
|
|
5
6
|
export { default as Footer } from './components/layout/Footer.svelte';
|
|
6
7
|
export { themeStore } from './stores/theme.svelte.js';
|
|
7
8
|
export { default as ThemeSelector } from './components/layout/ThemeSelector.svelte';
|
|
9
|
+
export { footerStore, type FooterLink, type FooterConfig, type FooterSection } from './stores/layout/footer.svelte.js';
|
|
10
|
+
export { appData, type AppData } from './stores/app.svelte.js';
|
|
11
|
+
export { apiStore, type ApiStore } from './stores/api.svelte.js';
|
|
12
|
+
export { authStore, type AuthState } from './stores/auth.svelte.js';
|
|
13
|
+
export { databaseStore, type DatabaseState, } from './stores/db.svelte.js';
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
// * UI Library entry point...
|
|
3
3
|
// ^ UI Showcase
|
|
4
4
|
export { default as UIShowcase } from './components/UIShowcase.svelte';
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// * RUENES OF...
|
|
6
|
+
// * Kyntharil
|
|
7
|
+
// * from Old Norse "kyn" = kind, kin + suffix -tharil suggesting evolution
|
|
8
|
+
export { default as Altharun } from './components/dt/Altharun.svelte';
|
|
9
|
+
// * Altharun
|
|
10
|
+
// * from Old Norse "aldir" = age, wisdom
|
|
11
|
+
export { default as Kyntharil } from './components/dt/Kyntharil.svelte';
|
|
8
12
|
// layout related components
|
|
13
|
+
export { default as NavBar } from './components/layout/NavBar.svelte';
|
|
9
14
|
export { default as UrlDisplay } from './components/layout/URLDisplay.svelte';
|
|
10
15
|
export { default as Footer } from './components/layout/Footer.svelte';
|
|
11
16
|
// ^ data related components
|
|
@@ -23,3 +28,14 @@ export { default as ThemeSelector } from './components/layout/ThemeSelector.svel
|
|
|
23
28
|
// export * from './components/tools/qr.js';
|
|
24
29
|
// export * from './components/tools/uuid.js';
|
|
25
30
|
// export * from './components/tools/validate.js';
|
|
31
|
+
// * I don't know if this is some allusion or something...
|
|
32
|
+
// * But I think that if I import this later, (I mean, at last)
|
|
33
|
+
// * This fix some of the problems with the store management...
|
|
34
|
+
// * I don't know why, but it works...
|
|
35
|
+
// todo: investigate why this works...
|
|
36
|
+
// ^ App related components
|
|
37
|
+
export { footerStore } from './stores/layout/footer.svelte.js';
|
|
38
|
+
export { appData } from './stores/app.svelte.js';
|
|
39
|
+
export { apiStore } from './stores/api.svelte.js';
|
|
40
|
+
export { authStore } from './stores/auth.svelte.js';
|
|
41
|
+
export { databaseStore, } from './stores/db.svelte.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface User {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
email: string;
|
|
5
|
+
role: 'admin' | 'staff' | 'user';
|
|
6
|
+
}
|
|
7
|
+
export interface AuthState {
|
|
8
|
+
isAuthenticated: boolean;
|
|
9
|
+
user: User | null;
|
|
10
|
+
}
|
|
11
|
+
declare class AuthStore {
|
|
12
|
+
isAuthenticated: boolean;
|
|
13
|
+
user: User | null;
|
|
14
|
+
login(user: User): void;
|
|
15
|
+
logout(): void;
|
|
16
|
+
init(initialState?: Partial<AuthState>): void;
|
|
17
|
+
}
|
|
18
|
+
export declare const authStore: AuthStore;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/lib/stores/auth.svelte.ts
|
|
2
|
+
class AuthStore {
|
|
3
|
+
isAuthenticated = $state(false);
|
|
4
|
+
user = $state(null);
|
|
5
|
+
login(user) {
|
|
6
|
+
this.isAuthenticated = true;
|
|
7
|
+
this.user = user;
|
|
8
|
+
console.log('🔐 User logged in:', user);
|
|
9
|
+
}
|
|
10
|
+
logout() {
|
|
11
|
+
this.isAuthenticated = false;
|
|
12
|
+
this.user = null;
|
|
13
|
+
console.log('👋 User logged out');
|
|
14
|
+
}
|
|
15
|
+
init(initialState) {
|
|
16
|
+
if (initialState?.isAuthenticated)
|
|
17
|
+
this.isAuthenticated = initialState.isAuthenticated;
|
|
18
|
+
if (initialState?.user)
|
|
19
|
+
this.user = initialState.user;
|
|
20
|
+
console.log('🔑 Auth initialized:', {
|
|
21
|
+
isAuthenticated: this.isAuthenticated,
|
|
22
|
+
user: this.user
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export const authStore = new AuthStore();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { forge as f, crud, base, t_types as t } from 'ts-forge';
|
|
2
|
+
export { f, crud, base, t };
|
|
3
|
+
export interface DatabaseState {
|
|
4
|
+
schemas: Record<string, f.SchemaMetadata>;
|
|
5
|
+
tables: Record<string, Record<string, any>>;
|
|
6
|
+
activeSchema: string | null;
|
|
7
|
+
activeTable: string | null;
|
|
8
|
+
showModal: boolean;
|
|
9
|
+
currentOperation: 'POST' | 'GET' | 'PUT' | 'DELETE' | null;
|
|
10
|
+
}
|
|
11
|
+
declare class DatabaseStore {
|
|
12
|
+
schemas: Record<string, f.SchemaMetadata>;
|
|
13
|
+
tables: Record<string, Record<string, any>>;
|
|
14
|
+
activeSchema: string | null;
|
|
15
|
+
activeTable: string | null;
|
|
16
|
+
showModal: boolean;
|
|
17
|
+
currentOperation: "GET" | "POST" | "PUT" | "DELETE" | null;
|
|
18
|
+
currentTables: any;
|
|
19
|
+
toggleSchema(schema: string): void;
|
|
20
|
+
private baseClient;
|
|
21
|
+
private forge;
|
|
22
|
+
setActiveTable(schema: string, table: string): void;
|
|
23
|
+
setOperation(operation: DatabaseState['currentOperation']): void;
|
|
24
|
+
handleCrudOperation(operation: DatabaseState['currentOperation']): Promise<void>;
|
|
25
|
+
init(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
export declare const databaseStore: DatabaseStore;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/lib/stores/database.svelte.ts
|
|
2
|
+
import { forge as f, crud, base, t_types as t } from 'ts-forge';
|
|
3
|
+
export { f, crud, base, t };
|
|
4
|
+
class DatabaseStore {
|
|
5
|
+
// Core state with proper initialization
|
|
6
|
+
schemas = $state({});
|
|
7
|
+
tables = $state({});
|
|
8
|
+
activeSchema = $state(null);
|
|
9
|
+
activeTable = $state(null);
|
|
10
|
+
showModal = $state(false);
|
|
11
|
+
currentOperation = $state(null);
|
|
12
|
+
// Change this derived state
|
|
13
|
+
currentTables = $derived(this.activeSchema ? this.schemas[this.activeSchema] : null);
|
|
14
|
+
toggleSchema(schema) {
|
|
15
|
+
// Simple toggle, don't clear if already active
|
|
16
|
+
this.activeSchema = schema;
|
|
17
|
+
console.log('Active schema:', this.activeSchema, 'Tables:', this.schemas[schema]);
|
|
18
|
+
}
|
|
19
|
+
// Base client
|
|
20
|
+
baseClient = new base.BaseClient('http://localhost:8000/');
|
|
21
|
+
forge = new f.TsForge(this.baseClient);
|
|
22
|
+
// Table management
|
|
23
|
+
setActiveTable(schema, table) {
|
|
24
|
+
this.activeSchema = schema;
|
|
25
|
+
this.activeTable = table;
|
|
26
|
+
console.log('Set active:', { schema, table });
|
|
27
|
+
}
|
|
28
|
+
// CRUD operations
|
|
29
|
+
setOperation(operation) {
|
|
30
|
+
this.currentOperation = operation;
|
|
31
|
+
this.showModal = !!operation;
|
|
32
|
+
}
|
|
33
|
+
async handleCrudOperation(operation) {
|
|
34
|
+
this.setOperation(operation);
|
|
35
|
+
}
|
|
36
|
+
// Initialize the store
|
|
37
|
+
async init() {
|
|
38
|
+
try {
|
|
39
|
+
await this.forge.init();
|
|
40
|
+
this.schemas = this.forge.schemaLookup;
|
|
41
|
+
console.log('Database store initialized:', {
|
|
42
|
+
schemas: Object.keys(this.schemas)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Database store initialization failed:', error);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Export singleton instance
|
|
52
|
+
export const databaseStore = new DatabaseStore();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface FooterLink {
|
|
2
|
+
text: string;
|
|
3
|
+
href: string;
|
|
4
|
+
}
|
|
5
|
+
export interface FooterSection {
|
|
6
|
+
title: string;
|
|
7
|
+
links: FooterLink[];
|
|
8
|
+
}
|
|
9
|
+
export interface FooterConfig {
|
|
10
|
+
sections: FooterSection[];
|
|
11
|
+
socialLinks: {
|
|
12
|
+
github?: string;
|
|
13
|
+
twitter?: string;
|
|
14
|
+
linkedin?: string;
|
|
15
|
+
};
|
|
16
|
+
showSocialLinks: boolean;
|
|
17
|
+
showVersion: boolean;
|
|
18
|
+
showCopyright: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare class FooterStore {
|
|
21
|
+
private _config;
|
|
22
|
+
get config(): FooterConfig;
|
|
23
|
+
init(config: Partial<FooterConfig>): this;
|
|
24
|
+
reset(): this;
|
|
25
|
+
addSection(section: FooterSection): this;
|
|
26
|
+
updateSection(index: number, section: FooterSection): this;
|
|
27
|
+
removeSection(index: number): this;
|
|
28
|
+
clearSections(): this;
|
|
29
|
+
updateSocialLinks(links: Partial<FooterConfig['socialLinks']>): this;
|
|
30
|
+
showSocialLinks(show: boolean): this;
|
|
31
|
+
showVersion(show: boolean): this;
|
|
32
|
+
showCopyright(show: boolean): this;
|
|
33
|
+
}
|
|
34
|
+
export declare const footerStore: FooterStore;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/lib/stores/footer.svelte.ts
|
|
2
|
+
const defaultConfig = {
|
|
3
|
+
sections: [],
|
|
4
|
+
socialLinks: {},
|
|
5
|
+
showSocialLinks: false,
|
|
6
|
+
showVersion: false,
|
|
7
|
+
showCopyright: false
|
|
8
|
+
};
|
|
9
|
+
class FooterStore {
|
|
10
|
+
// State management with runes
|
|
11
|
+
_config = $state({ ...defaultConfig });
|
|
12
|
+
// Getter for the config to avoid direct state access
|
|
13
|
+
get config() {
|
|
14
|
+
return this._config;
|
|
15
|
+
}
|
|
16
|
+
// Initialize with custom configuration
|
|
17
|
+
init(config) {
|
|
18
|
+
this._config = {
|
|
19
|
+
sections: config.sections || [],
|
|
20
|
+
socialLinks: config.socialLinks || {},
|
|
21
|
+
showSocialLinks: config.showSocialLinks ?? false,
|
|
22
|
+
showVersion: config.showVersion ?? false,
|
|
23
|
+
showCopyright: config.showCopyright ?? false
|
|
24
|
+
};
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
// Set default state
|
|
28
|
+
reset() {
|
|
29
|
+
this._config = { ...defaultConfig };
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
// Section management
|
|
33
|
+
addSection(section) {
|
|
34
|
+
this._config.sections = [...this._config.sections, section];
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
updateSection(index, section) {
|
|
38
|
+
if (index >= 0 && index < this._config.sections.length) {
|
|
39
|
+
const newSections = [...this._config.sections];
|
|
40
|
+
newSections[index] = section;
|
|
41
|
+
this._config.sections = newSections;
|
|
42
|
+
}
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
removeSection(index) {
|
|
46
|
+
this._config.sections = this._config.sections.filter((_, i) => i !== index);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
clearSections() {
|
|
50
|
+
this._config.sections = [];
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
// Social links management
|
|
54
|
+
updateSocialLinks(links) {
|
|
55
|
+
this._config.socialLinks = { ...this._config.socialLinks, ...links };
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
// Show/Hide features
|
|
59
|
+
showSocialLinks(show) {
|
|
60
|
+
this._config.showSocialLinks = show;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
showVersion(show) {
|
|
64
|
+
this._config.showVersion = show;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
showCopyright(show) {
|
|
68
|
+
this._config.showCopyright = show;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Export singleton instance
|
|
73
|
+
export const footerStore = new FooterStore();
|
package/package.json
CHANGED
|
@@ -1,74 +1,75 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "rune-lab",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"type": "module",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Yrrrrrf <fer.rezac@outlook.com>",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/Yrrrrrf/rune-lab.git"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"svelte",
|
|
13
|
-
"components",
|
|
14
|
-
"ui",
|
|
15
|
-
"daisyui",
|
|
16
|
-
"tailwind"
|
|
17
|
-
],
|
|
18
|
-
"exports": {
|
|
19
|
-
".": {
|
|
20
|
-
"types": "./dist/index.d.ts",
|
|
21
|
-
"svelte": "./dist/index.js",
|
|
22
|
-
"default": "./dist/index.js"
|
|
23
|
-
},
|
|
24
|
-
"./themes": {
|
|
25
|
-
"types": "./dist/theme/static.d.ts",
|
|
26
|
-
"default": "./dist/theme/static.js"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"files": [
|
|
30
|
-
"dist",
|
|
31
|
-
"dist/stores/const/themes.js"
|
|
32
|
-
],
|
|
33
|
-
"svelte": "./dist/index.js",
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "svelte-kit sync && svelte-package",
|
|
36
|
-
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
37
|
-
"dev": "vite dev",
|
|
38
|
-
"watch": "bun watch.ts",
|
|
39
|
-
"preview": "vite preview",
|
|
40
|
-
"package": "svelte-kit sync && svelte-package && publint",
|
|
41
|
-
"prepublishOnly": "npm run package",
|
|
42
|
-
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
|
43
|
-
},
|
|
44
|
-
"sideEffects": [
|
|
45
|
-
"**/*.css"
|
|
46
|
-
],
|
|
47
|
-
"types": "./dist/index.d.ts",
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"svelte": "^5.0.0",
|
|
50
|
-
"tailwindcss": "^3.4.9",
|
|
51
|
-
"daisyui": "^4.12.23"
|
|
52
|
-
},
|
|
53
|
-
"devDependencies": {
|
|
54
|
-
"@sveltejs/adapter-auto": "^3.0.0",
|
|
55
|
-
"@sveltejs/kit": "^2.0.0",
|
|
56
|
-
"@sveltejs/package": "^2.0.0",
|
|
57
|
-
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
58
|
-
"autoprefixer": "^10.4.20",
|
|
59
|
-
"daisyui": "^4.12.23",
|
|
60
|
-
"publint": "^0.2.0",
|
|
61
|
-
"svelte": "^5.0.0",
|
|
62
|
-
"svelte-check": "^4.0.0",
|
|
63
|
-
"tailwindcss": "^3.4.9",
|
|
64
|
-
"typescript": "^5.0.0",
|
|
65
|
-
"vite": "^5.4.11"
|
|
66
|
-
},
|
|
67
|
-
"dependencies": {
|
|
68
|
-
"@tailwindcss/container-queries": "^0.1.1",
|
|
69
|
-
"@tailwindcss/forms": "^0.5.9",
|
|
70
|
-
"@tailwindcss/typography": "^0.5.15",
|
|
71
|
-
"@types/bun": "^1.1.14",
|
|
72
|
-
"phosphor-svelte": "^3.0.1"
|
|
73
|
-
|
|
74
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "rune-lab",
|
|
3
|
+
"version": "0.0.2-alpha-1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Yrrrrrf <fer.rezac@outlook.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Yrrrrrf/rune-lab.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"svelte",
|
|
13
|
+
"components",
|
|
14
|
+
"ui",
|
|
15
|
+
"daisyui",
|
|
16
|
+
"tailwind"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"svelte": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./themes": {
|
|
25
|
+
"types": "./dist/theme/static.d.ts",
|
|
26
|
+
"default": "./dist/theme/static.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"dist/stores/const/themes.js"
|
|
32
|
+
],
|
|
33
|
+
"svelte": "./dist/index.js",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "svelte-kit sync && svelte-package",
|
|
36
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
37
|
+
"dev": "vite dev",
|
|
38
|
+
"watch": "bun watch.ts",
|
|
39
|
+
"preview": "vite preview",
|
|
40
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
41
|
+
"prepublishOnly": "npm run package",
|
|
42
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
|
43
|
+
},
|
|
44
|
+
"sideEffects": [
|
|
45
|
+
"**/*.css"
|
|
46
|
+
],
|
|
47
|
+
"types": "./dist/index.d.ts",
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"svelte": "^5.0.0",
|
|
50
|
+
"tailwindcss": "^3.4.9",
|
|
51
|
+
"daisyui": "^4.12.23"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@sveltejs/adapter-auto": "^3.0.0",
|
|
55
|
+
"@sveltejs/kit": "^2.0.0",
|
|
56
|
+
"@sveltejs/package": "^2.0.0",
|
|
57
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
58
|
+
"autoprefixer": "^10.4.20",
|
|
59
|
+
"daisyui": "^4.12.23",
|
|
60
|
+
"publint": "^0.2.0",
|
|
61
|
+
"svelte": "^5.0.0",
|
|
62
|
+
"svelte-check": "^4.0.0",
|
|
63
|
+
"tailwindcss": "^3.4.9",
|
|
64
|
+
"typescript": "^5.0.0",
|
|
65
|
+
"vite": "^5.4.11"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@tailwindcss/container-queries": "^0.1.1",
|
|
69
|
+
"@tailwindcss/forms": "^0.5.9",
|
|
70
|
+
"@tailwindcss/typography": "^0.5.15",
|
|
71
|
+
"@types/bun": "^1.1.14",
|
|
72
|
+
"phosphor-svelte": "^3.0.1",
|
|
73
|
+
"ts-forge": "0.0.3"
|
|
74
|
+
}
|
|
75
|
+
}
|