juxscript 1.0.132 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -32
- package/bin/cli.js +4 -2
- package/index.d.ts +200 -0
- package/index.js +96 -22
- package/juxconfig.example.js +58 -63
- package/lib/components/alert.ts +200 -0
- package/lib/components/app.ts +247 -0
- package/lib/components/badge.ts +101 -0
- package/lib/components/base/BaseComponent.ts +421 -0
- package/lib/components/base/FormInput.ts +227 -0
- package/lib/components/button.ts +178 -0
- package/lib/components/card.ts +173 -0
- package/lib/components/chart.ts +231 -0
- package/lib/components/checkbox.ts +242 -0
- package/lib/components/code.ts +123 -0
- package/lib/components/container.ts +140 -0
- package/lib/components/data.ts +135 -0
- package/lib/components/datepicker.ts +234 -0
- package/lib/components/dialog.ts +172 -0
- package/lib/components/divider.ts +100 -0
- package/lib/components/dropdown.ts +186 -0
- package/lib/components/element.ts +267 -0
- package/lib/components/fileupload.ts +309 -0
- package/lib/components/grid.ts +291 -0
- package/lib/components/guard.ts +92 -0
- package/lib/components/heading.ts +96 -0
- package/lib/components/helpers.ts +41 -0
- package/lib/components/hero.ts +224 -0
- package/lib/components/icon.ts +178 -0
- package/lib/components/icons.ts +464 -0
- package/lib/components/include.ts +410 -0
- package/lib/components/input.ts +457 -0
- package/lib/components/list.ts +419 -0
- package/lib/components/loading.ts +100 -0
- package/lib/components/menu.ts +275 -0
- package/lib/components/modal.ts +284 -0
- package/lib/components/nav.ts +257 -0
- package/lib/components/paragraph.ts +97 -0
- package/lib/components/progress.ts +159 -0
- package/lib/components/radio.ts +278 -0
- package/lib/components/req.ts +303 -0
- package/lib/components/script.ts +41 -0
- package/lib/components/select.ts +252 -0
- package/lib/components/sidebar.ts +275 -0
- package/lib/components/style.ts +41 -0
- package/lib/components/switch.ts +246 -0
- package/lib/components/table.ts +1249 -0
- package/lib/components/tabs.ts +250 -0
- package/lib/components/theme-toggle.ts +293 -0
- package/lib/components/tooltip.ts +144 -0
- package/lib/components/view.ts +190 -0
- package/lib/components/write.ts +272 -0
- package/lib/globals.d.ts +19 -5
- package/lib/layouts/default.css +260 -0
- package/lib/layouts/figma.css +334 -0
- package/lib/reactivity/state.ts +78 -0
- package/lib/utils/{fetch.js → fetch.ts} +206 -81
- package/machinery/ast.js +347 -0
- package/machinery/build.js +466 -0
- package/machinery/compiler3.js +6 -66
- package/machinery/config.js +6 -93
- package/machinery/doc-generator.js +136 -0
- package/machinery/imports.js +155 -0
- package/machinery/server.js +166 -0
- package/machinery/ts-shim.js +46 -0
- package/machinery/watcher.js +162 -50
- package/package.json +9 -30
- package/create/index.jux +0 -77
- package/create/layout.jux +0 -18
- package/create/style.css +0 -57
- package/create/themes/assets/jux.svg +0 -34
- package/create/themes/base.css +0 -197
- package/create/themes/base2.css +0 -54
- package/create/themes/layouts/base.jux +0 -16
- package/create/themes/layouts/base_marketing.jux +0 -0
- package/create/themes/layouts/base_saas.jux +0 -0
- package/lib/componentsv2/base/BaseEngine.d.ts +0 -112
- package/lib/componentsv2/base/BaseEngine.js +0 -279
- package/lib/componentsv2/base/BaseSkin.d.ts +0 -74
- package/lib/componentsv2/base/BaseSkin.js +0 -130
- package/lib/componentsv2/base/Neighborhood.d.ts +0 -22
- package/lib/componentsv2/base/Neighborhood.js +0 -56
- package/lib/componentsv2/base/OptionsContract.d.ts +0 -20
- package/lib/componentsv2/base/OptionsContract.js +0 -107
- package/lib/componentsv2/base/State.d.ts +0 -18
- package/lib/componentsv2/base/State.js +0 -68
- package/lib/componentsv2/element/Element.d.ts +0 -30
- package/lib/componentsv2/element/Element.js +0 -50
- package/lib/componentsv2/element/ElementEngine.d.ts +0 -59
- package/lib/componentsv2/element/ElementEngine.js +0 -118
- package/lib/componentsv2/element/ElementSkin.d.ts +0 -10
- package/lib/componentsv2/element/ElementSkin.js +0 -56
- package/lib/componentsv2/element/structure.css +0 -261
- package/lib/componentsv2/grid/Grid.d.ts +0 -13
- package/lib/componentsv2/grid/Grid.js +0 -27
- package/lib/componentsv2/grid/GridEngine.d.ts +0 -77
- package/lib/componentsv2/grid/GridEngine.js +0 -153
- package/lib/componentsv2/grid/GridSkin.d.ts +0 -11
- package/lib/componentsv2/grid/GridSkin.js +0 -84
- package/lib/componentsv2/grid/structure.css +0 -27
- package/lib/componentsv2/input/Input.d.ts +0 -6
- package/lib/componentsv2/input/Input.js +0 -21
- package/lib/componentsv2/input/InputEngine.d.ts +0 -70
- package/lib/componentsv2/input/InputEngine.js +0 -143
- package/lib/componentsv2/input/InputSkin.d.ts +0 -11
- package/lib/componentsv2/input/InputSkin.js +0 -89
- package/lib/componentsv2/input/structure.css +0 -47
- package/lib/componentsv2/list/List.d.ts +0 -49
- package/lib/componentsv2/list/List.js +0 -105
- package/lib/componentsv2/list/ListEngine.d.ts +0 -121
- package/lib/componentsv2/list/ListEngine.js +0 -322
- package/lib/componentsv2/list/ListSkin.d.ts +0 -20
- package/lib/componentsv2/list/ListSkin.js +0 -345
- package/lib/componentsv2/list/structure.css +0 -359
- package/lib/componentsv2/plugins/ClientSQLitePlugin.d.ts +0 -21
- package/lib/componentsv2/plugins/ClientSQLitePlugin.js +0 -130
- package/lib/componentsv2/plugins/IndexedDBPlugin.d.ts +0 -18
- package/lib/componentsv2/plugins/IndexedDBPlugin.js +0 -75
- package/lib/componentsv2/plugins/LocalStoragePlugin.d.ts +0 -20
- package/lib/componentsv2/plugins/LocalStoragePlugin.js +0 -65
- package/lib/componentsv2/plugins/ServerSQLitePlugin.d.ts +0 -25
- package/lib/componentsv2/plugins/ServerSQLitePlugin.js +0 -70
- package/lib/componentsv2/stubs/ComponentComposition.ts.stub +0 -32
- package/lib/componentsv2/stubs/ComponentEngine.ts.stub +0 -36
- package/lib/componentsv2/stubs/ComponentSkin.ts.stub +0 -35
- package/lib/componentsv2/stubs/ComponentStructure.css.d.ts.stub +0 -2
- package/lib/componentsv2/stubs/ComponentStructure.css.stub +0 -13
- package/lib/utils/fetch.d.ts +0 -176
- package/machinery/serve.js +0 -255
- package/types/css.d.ts +0 -10
- /package/{create/themes/layouts/base_blog.jux → machinery/bundleAssets.js} +0 -0
- /package/{create/themes/layouts/base_docs.jux → machinery/bundleJux.js} +0 -0
- /package/{create/themes/layouts/base_login.jux → machinery/bundleVendors.js} +0 -0
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
.jux-list {
|
|
2
|
-
list-style: none; /* Default reset */
|
|
3
|
-
padding: 0;
|
|
4
|
-
margin: 0;
|
|
5
|
-
}
|
|
6
|
-
.jux-list.dark{
|
|
7
|
-
background: blue;
|
|
8
|
-
}
|
|
9
|
-
/* Explicit type styling based on dynamic class */
|
|
10
|
-
.jux-list-type-ol {
|
|
11
|
-
list-style: decimal;
|
|
12
|
-
padding-left: 1.5rem; /* Make room for numbers */
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.jux-list-type-ul {
|
|
16
|
-
list-style: none;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.jux-list-selectable .jux-list-item {
|
|
20
|
-
cursor: pointer;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/* Visual reflection of the disabled state */
|
|
24
|
-
.jux-list[aria-disabled="true"] {
|
|
25
|
-
opacity: 0.5;
|
|
26
|
-
pointer-events: none;
|
|
27
|
-
cursor: not-allowed;
|
|
28
|
-
user-select: none;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.jux-list-item {
|
|
32
|
-
padding: var(--space-md) var(--space-lg);
|
|
33
|
-
border-bottom: var(--border-width);
|
|
34
|
-
border-color: var(--color-border);
|
|
35
|
-
transition: background-color var(--transition-fast);
|
|
36
|
-
margin: var(--space-sm);
|
|
37
|
-
cursor: default; /* Base cursor */
|
|
38
|
-
/* ✨ Uplift: Flex layout for content <-> controls separation */
|
|
39
|
-
display: flex;
|
|
40
|
-
justify-content: space-between;
|
|
41
|
-
align-items: center;
|
|
42
|
-
}
|
|
43
|
-
.jux-list-item-highlight{
|
|
44
|
-
|
|
45
|
-
background-color: var(--color-surface-secondary-subtle, #ee76781a);
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
/* Standard Hover Effect (Native CSS) */
|
|
49
|
-
/* Apply to all items that are NOT disabled */
|
|
50
|
-
.jux-list:not([aria-disabled="true"]) .jux-list-item:not(.jux-selected):hover {
|
|
51
|
-
background-color: var(--color-surface-hover, #f5f5f5);
|
|
52
|
-
border-radius: var(--radius-sm);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.jux-list-item:last-child {
|
|
56
|
-
border-bottom: none;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/* REMOVED: .jux-list-item-hoverable logic */
|
|
60
|
-
|
|
61
|
-
.jux-list-item-icon {
|
|
62
|
-
display: inline-flex;
|
|
63
|
-
align-items: center;
|
|
64
|
-
justify-content: center;
|
|
65
|
-
width: 24px;
|
|
66
|
-
min-width: 24px;
|
|
67
|
-
height: 24px;
|
|
68
|
-
flex-shrink: 0;
|
|
69
|
-
margin-right: var(--space-lg);
|
|
70
|
-
color: var(--color-text-secondary);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.jux-list-item-content {
|
|
74
|
-
display: flex;
|
|
75
|
-
flex-direction: row;
|
|
76
|
-
align-items: center;
|
|
77
|
-
gap: var(--space-lg);
|
|
78
|
-
flex: 1; /* Take available width */
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/* Selected State Styling */
|
|
82
|
-
.jux-selected {
|
|
83
|
-
background-color: var(--color-surface-active, #f0f7ff); /* fallback light blue */
|
|
84
|
-
border-color: var(--color-primary, #0066cc);
|
|
85
|
-
font-weight: 500;
|
|
86
|
-
color: var(--color-primary, #0066cc);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/* Checkmark indicator for selected items */
|
|
90
|
-
.jux-selected .jux-list-item-content::before {
|
|
91
|
-
content: "✓";
|
|
92
|
-
display: inline-flex;
|
|
93
|
-
align-items: center;
|
|
94
|
-
justify-content: center;
|
|
95
|
-
width: 1.25rem;
|
|
96
|
-
height: 1.25rem;
|
|
97
|
-
margin-right: 0.5rem;
|
|
98
|
-
color: inherit;
|
|
99
|
-
font-weight: bold;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
103
|
-
✨ CONTROLS (Edit/Delete)
|
|
104
|
-
═════════════════════════════════════════════════════════════════ */
|
|
105
|
-
.jux-item-controls {
|
|
106
|
-
display: flex;
|
|
107
|
-
gap: var(--space-sm);
|
|
108
|
-
opacity: 0;
|
|
109
|
-
transform: translateX(10px);
|
|
110
|
-
transition: opacity 0.2s, transform 0.2s;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/* Uplift: Only show controls on hover for a cleaner UI */
|
|
114
|
-
.jux-list-item:hover .jux-item-controls {
|
|
115
|
-
opacity: 1;
|
|
116
|
-
transform: translateX(0);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.jux-control {
|
|
120
|
-
background: transparent;
|
|
121
|
-
border: none;
|
|
122
|
-
cursor: pointer;
|
|
123
|
-
opacity: 0.5;
|
|
124
|
-
transition: opacity 0.2s, background-color 0.2s;
|
|
125
|
-
font-size: 1.1rem;
|
|
126
|
-
padding: 4px 8px;
|
|
127
|
-
border-radius: var(--radius-sm);
|
|
128
|
-
display: flex;
|
|
129
|
-
align-items: center;
|
|
130
|
-
justify-content: center;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.jux-control:hover { opacity: 1; background-color: var(--color-surface-hover); }
|
|
134
|
-
.jux-action-delete:hover { color: #d32f2f; background-color: #fee2e2; }
|
|
135
|
-
.jux-action-edit:hover { color: var(--color-primary, #0066cc); background-color: #e0f2fe; }
|
|
136
|
-
|
|
137
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
138
|
-
✨ MODAL (Uplifted with Animations & Tokens)
|
|
139
|
-
═════════════════════════════════════════════════════════════════ */
|
|
140
|
-
.jux-modal-overlay {
|
|
141
|
-
position: fixed;
|
|
142
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
143
|
-
background: rgba(0, 0, 0, 0.4);
|
|
144
|
-
backdrop-filter: blur(4px); /* Glass effect */
|
|
145
|
-
display: flex;
|
|
146
|
-
align-items: center;
|
|
147
|
-
justify-content: center;
|
|
148
|
-
z-index: 1000;
|
|
149
|
-
opacity: 0;
|
|
150
|
-
animation: jux-fade-in 0.2s forwards;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
.jux-modal {
|
|
154
|
-
background: var(--color-surface-elevated, #fff);
|
|
155
|
-
padding: var(--space-xl);
|
|
156
|
-
border-radius: var(--radius-sm);
|
|
157
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
158
|
-
min-width: 360px;
|
|
159
|
-
display: flex;
|
|
160
|
-
flex-direction: column;
|
|
161
|
-
gap: var(--space-lg);
|
|
162
|
-
transform: scale(0.95);
|
|
163
|
-
opacity: 0;
|
|
164
|
-
animation: jux-scale-in 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.jux-modal h3 {
|
|
168
|
-
margin: 0;
|
|
169
|
-
font-size: var(--font-size-lg);
|
|
170
|
-
font-weight: 600;
|
|
171
|
-
color: var(--color-text-primary);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.jux-modal input {
|
|
175
|
-
padding: var(--space-md);
|
|
176
|
-
border: 1px solid var(--color-border);
|
|
177
|
-
border-radius: var(--radius-md);
|
|
178
|
-
font-size: var(--font-size-base);
|
|
179
|
-
width: 100%;
|
|
180
|
-
outline: none;
|
|
181
|
-
transition: border-color 0.2s, box-shadow 0.2s;
|
|
182
|
-
background: var(--color-surface-base);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.jux-modal input:focus {
|
|
186
|
-
border-color: var(--color-brand, #085156);
|
|
187
|
-
box-shadow: 0 0 0 3px var(--color-brand-subtle);
|
|
188
|
-
background: var(--color-background);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.jux-modal-actions {
|
|
192
|
-
display: flex;
|
|
193
|
-
justify-content: flex-end;
|
|
194
|
-
gap: var(--space-md);
|
|
195
|
-
margin-top: var(--space-sm);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
.jux-modal-btn {
|
|
199
|
-
padding: 0.6rem 1.25rem;
|
|
200
|
-
cursor: pointer;
|
|
201
|
-
border: none;
|
|
202
|
-
border-radius: var(--radius-sm);
|
|
203
|
-
font-weight: 500;
|
|
204
|
-
font-size: var(--font-size-sm);
|
|
205
|
-
transition: all 0.2s;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.jux-btn-cancel {
|
|
209
|
-
background: transparent;
|
|
210
|
-
color: var(--color-text-secondary);
|
|
211
|
-
}
|
|
212
|
-
.jux-btn-cancel:hover {
|
|
213
|
-
background: var(--color-surface-hover);
|
|
214
|
-
color: var(--color-text-primary);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.jux-btn-save {
|
|
218
|
-
background: var(--color-brand, #085156);
|
|
219
|
-
color: #fff;
|
|
220
|
-
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
221
|
-
}
|
|
222
|
-
.jux-btn-save:hover {
|
|
223
|
-
background: var(--color-brand-hover, #066666);
|
|
224
|
-
transform: translateY(-1px);
|
|
225
|
-
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
@keyframes jux-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
229
|
-
@keyframes jux-scale-in { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
|
230
|
-
|
|
231
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
232
|
-
✨ TOOLBAR (Search & Actions)
|
|
233
|
-
═════════════════════════════════════════════════════════════════ */
|
|
234
|
-
.jux-list-toolbar {
|
|
235
|
-
display: flex;
|
|
236
|
-
gap: var(--space-sm);
|
|
237
|
-
padding: var(--space-md);
|
|
238
|
-
background-color: var(--color-surface-base);
|
|
239
|
-
border-bottom: var(--border-width);
|
|
240
|
-
border-color: var(--color-border);
|
|
241
|
-
border-radius: var(--radius-sm) var(--radius-sm) 0 0;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.jux-search-input {
|
|
245
|
-
flex: 1;
|
|
246
|
-
padding: var(--space-sm) var(--space-md);
|
|
247
|
-
border: 1px solid var(--color-border);
|
|
248
|
-
border-radius: var(--radius-sm);
|
|
249
|
-
font-family: inherit;
|
|
250
|
-
font-size: var(--font-size-sm);
|
|
251
|
-
outline: none;
|
|
252
|
-
transition: box-shadow 0.2s, border-color 0.2s;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.jux-search-input:focus {
|
|
256
|
-
border-color: var(--color-brand, #085156);
|
|
257
|
-
box-shadow: 0 0 0 2px var(--color-brand-subtle);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.jux-action-add {
|
|
261
|
-
display: inline-flex;
|
|
262
|
-
align-items: center;
|
|
263
|
-
justify-content: center;
|
|
264
|
-
width: 36px;
|
|
265
|
-
height: 36px;
|
|
266
|
-
background-color: var(--color-surface-elevated, #fff);
|
|
267
|
-
border: 1px solid var(--color-border);
|
|
268
|
-
border-radius: var(--radius-sm);
|
|
269
|
-
cursor: pointer;
|
|
270
|
-
font-size: 1.25rem;
|
|
271
|
-
line-height: 1;
|
|
272
|
-
color: var(--color-text-secondary);
|
|
273
|
-
transition: all 0.2s;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.jux-action-add:hover {
|
|
277
|
-
background-color: var(--color-surface-hover);
|
|
278
|
-
color: var(--color-brand);
|
|
279
|
-
border-color: var(--color-brand-subtle);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.jux-action-add:active {
|
|
283
|
-
transform: translateY(1px);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.jux-action-sort {
|
|
287
|
-
display: inline-flex;
|
|
288
|
-
align-items: center;
|
|
289
|
-
justify-content: center;
|
|
290
|
-
width: 36px;
|
|
291
|
-
height: 36px;
|
|
292
|
-
background-color: var(--color-surface-elevated, #fff);
|
|
293
|
-
border: 1px solid var(--color-border);
|
|
294
|
-
border-radius: var(--radius-sm);
|
|
295
|
-
cursor: pointer;
|
|
296
|
-
font-size: 1.1rem;
|
|
297
|
-
line-height: 1;
|
|
298
|
-
color: var(--color-text-secondary);
|
|
299
|
-
transition: all 0.2s;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.jux-action-sort:hover {
|
|
303
|
-
background-color: var(--color-surface-hover);
|
|
304
|
-
color: var(--color-brand);
|
|
305
|
-
border-color: var(--color-brand-subtle);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.jux-action-sort:active {
|
|
309
|
-
transform: translateY(1px);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
313
|
-
✨ DRAG & DROP
|
|
314
|
-
═════════════════════════════════════════════════════════════════ */
|
|
315
|
-
.jux-list-item[draggable="true"] {
|
|
316
|
-
cursor: grab;
|
|
317
|
-
}
|
|
318
|
-
.jux-list-item[draggable="true"]:active {
|
|
319
|
-
cursor: grabbing;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
/* Aspect of the item currently being dragged (in the list, not the ghost) */
|
|
323
|
-
.jux-list-item.jux-dragging {
|
|
324
|
-
opacity: 0.4;
|
|
325
|
-
background-color: var(--color-surface-secondary);
|
|
326
|
-
border: 1px dashed var(--color-brand);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/* Aspect of the item under the cursor (drop target) */
|
|
330
|
-
.jux-item-drop-target {
|
|
331
|
-
border-top: 2px solid var(--color-brand);
|
|
332
|
-
transition: none; /* Instant feedback */
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
.jux-action-drag {
|
|
336
|
-
cursor: grab;
|
|
337
|
-
font-size: 1.25rem;
|
|
338
|
-
line-height: 1;
|
|
339
|
-
color: var(--color-text-secondary);
|
|
340
|
-
display: flex;
|
|
341
|
-
align-items: center;
|
|
342
|
-
}
|
|
343
|
-
.jux-action-drag:active {
|
|
344
|
-
cursor: grabbing;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/* ═════════════════════════════════════════════════════════════════
|
|
348
|
-
✨ EMPTY STATE
|
|
349
|
-
═════════════════════════════════════════════════════════════════ */
|
|
350
|
-
.jux-list-empty-state {
|
|
351
|
-
padding: var(--space-xl);
|
|
352
|
-
text-align: center;
|
|
353
|
-
color: var(--color-text-secondary);
|
|
354
|
-
font-style: italic;
|
|
355
|
-
background-color: var(--color-surface-base);
|
|
356
|
-
border-radius: var(--radius-sm);
|
|
357
|
-
margin: var(--space-sm);
|
|
358
|
-
}
|
|
359
|
-
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { JuxServiceContract, BaseEngine } from '../base/BaseEngine.js';
|
|
2
|
-
export interface ClientSQLiteConfig {
|
|
3
|
-
/** Key used for IndexedDB storage (the "filename") */
|
|
4
|
-
dbName: string;
|
|
5
|
-
/** SQL to run if creating a brand new DB (Schema) */
|
|
6
|
-
initSql?: string;
|
|
7
|
-
/** Main Query to bind to the Engine State */
|
|
8
|
-
query?: string;
|
|
9
|
-
/** State property to bind results to (e.g. 'items') */
|
|
10
|
-
bindTo?: string;
|
|
11
|
-
/** Transform row object to State Item shape */
|
|
12
|
-
mapRow?: (row: any) => any;
|
|
13
|
-
/** If true, persists to IDB after every write operation */
|
|
14
|
-
autoSave?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Client-Side SQLite Plugin
|
|
18
|
-
* Runs a real SQLite engine via WebAssembly (WASM) in the browser, persisted to IndexedDB.
|
|
19
|
-
*/
|
|
20
|
-
export declare const ClientSQLitePlugin: (config: ClientSQLiteConfig) => JuxServiceContract<BaseEngine<any>>;
|
|
21
|
-
//# sourceMappingURL=ClientSQLitePlugin.d.ts.map
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-Side SQLite Plugin
|
|
3
|
-
* Runs a real SQLite engine via WebAssembly (WASM) in the browser, persisted to IndexedDB.
|
|
4
|
-
*/
|
|
5
|
-
export const ClientSQLitePlugin = (config) => ({
|
|
6
|
-
name: 'client-sqlite-wasm',
|
|
7
|
-
version: '1.0.0',
|
|
8
|
-
targetEnv: 'client',
|
|
9
|
-
install: (engine) => {
|
|
10
|
-
let db = null;
|
|
11
|
-
const IDB_NAME = 'JuxSqliteStorage';
|
|
12
|
-
const IDB_STORE = 'files';
|
|
13
|
-
// --- 1. IndexedDB HELPER (The "Hard Drive") ---
|
|
14
|
-
const getFile = (key) => {
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
const r = indexedDB.open(IDB_NAME, 1);
|
|
17
|
-
r.onupgradeneeded = (e) => e.target.result.createObjectStore(IDB_STORE);
|
|
18
|
-
r.onsuccess = (e) => {
|
|
19
|
-
const t = e.target.result.transaction([IDB_STORE], 'readonly').objectStore(IDB_STORE).get(key);
|
|
20
|
-
t.onsuccess = () => resolve(t.result);
|
|
21
|
-
t.onerror = () => resolve(null);
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
const saveFile = (key, data) => {
|
|
26
|
-
const r = indexedDB.open(IDB_NAME, 1);
|
|
27
|
-
r.onsuccess = (e) => {
|
|
28
|
-
const tx = e.target.result.transaction([IDB_STORE], 'readwrite');
|
|
29
|
-
tx.objectStore(IDB_STORE).put(data, key);
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
// --- 2. SQL Helper (Result Transformation) ---
|
|
33
|
-
// sql.js return format: [{ columns: ['id', 'name'], values: [[1, 'Bob']] }]
|
|
34
|
-
// We convert this to: [{ id: 1, name: 'Bob' }]
|
|
35
|
-
const normalizeResults = (res) => {
|
|
36
|
-
if (!res || !res.length)
|
|
37
|
-
return [];
|
|
38
|
-
const columns = res[0].columns;
|
|
39
|
-
const values = res[0].values;
|
|
40
|
-
return values.map((row) => {
|
|
41
|
-
const obj = {};
|
|
42
|
-
columns.forEach((col, i) => {
|
|
43
|
-
obj[col] = row[i];
|
|
44
|
-
});
|
|
45
|
-
return obj;
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
// --- 3. REFRESH LOGIC ---
|
|
49
|
-
const refresh = () => {
|
|
50
|
-
if (!db || !config.query || !config.bindTo)
|
|
51
|
-
return;
|
|
52
|
-
try {
|
|
53
|
-
// @ts-ignore
|
|
54
|
-
if (engine.loading)
|
|
55
|
-
engine.loading(true);
|
|
56
|
-
// Run Query
|
|
57
|
-
const raw = db.exec(config.query);
|
|
58
|
-
const rows = normalizeResults(raw);
|
|
59
|
-
// Map & Update State
|
|
60
|
-
const items = config.mapRow ? rows.map(config.mapRow) : rows;
|
|
61
|
-
// @ts-ignore
|
|
62
|
-
engine.updateState({ [config.bindTo]: items });
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
if (engine.loading)
|
|
65
|
-
engine.loading(false);
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
if (engine.emit)
|
|
68
|
-
engine.emit('sql:refresh', { count: items.length });
|
|
69
|
-
}
|
|
70
|
-
catch (e) {
|
|
71
|
-
console.error('[ClientSQLite] Query Error:', e);
|
|
72
|
-
// @ts-ignore
|
|
73
|
-
if (engine.loading)
|
|
74
|
-
engine.loading(false);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
// --- 4. INITIALIZATION ---
|
|
78
|
-
const loadSqlJs = async () => {
|
|
79
|
-
// Lazy load sql.js from CDN if not present
|
|
80
|
-
if (!window.initSqlJs) {
|
|
81
|
-
await new Promise(resolve => {
|
|
82
|
-
const script = document.createElement('script');
|
|
83
|
-
// sql-wasm.js is the glue code that loads the .wasm binary
|
|
84
|
-
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js';
|
|
85
|
-
script.onload = resolve;
|
|
86
|
-
document.head.appendChild(script);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
const SQL = await window.initSqlJs({
|
|
90
|
-
locateFile: (file) => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
|
|
91
|
-
});
|
|
92
|
-
// "Mount" the file from IDB
|
|
93
|
-
const binary = await getFile(config.dbName);
|
|
94
|
-
if (binary) {
|
|
95
|
-
db = new SQL.Database(binary);
|
|
96
|
-
// @ts-ignore
|
|
97
|
-
if (engine.emit)
|
|
98
|
-
engine.emit('sql:mounted', { size: binary.length });
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
db = new SQL.Database();
|
|
102
|
-
if (config.initSql)
|
|
103
|
-
db.run(config.initSql);
|
|
104
|
-
if (config.autoSave)
|
|
105
|
-
saveFile(config.dbName, db.export());
|
|
106
|
-
// @ts-ignore
|
|
107
|
-
if (engine.emit)
|
|
108
|
-
engine.emit('sql:created');
|
|
109
|
-
}
|
|
110
|
-
// Expose SQL methods to Engine (Mixins)
|
|
111
|
-
Object.assign(engine, {
|
|
112
|
-
sqlRun: (sql) => {
|
|
113
|
-
db.run(sql);
|
|
114
|
-
if (config.autoSave)
|
|
115
|
-
saveFile(config.dbName, db.export());
|
|
116
|
-
refresh();
|
|
117
|
-
},
|
|
118
|
-
sqlExec: (sql) => {
|
|
119
|
-
return normalizeResults(db.exec(sql));
|
|
120
|
-
},
|
|
121
|
-
sqlExport: () => {
|
|
122
|
-
return db.export();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
refresh();
|
|
126
|
-
};
|
|
127
|
-
loadSqlJs();
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
//# sourceMappingURL=ClientSQLitePlugin.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { JuxServiceContract, BaseEngine } from '../base/BaseEngine.js';
|
|
2
|
-
export interface IndexedDBConfig {
|
|
3
|
-
dbName: string;
|
|
4
|
-
storeName: string;
|
|
5
|
-
/**
|
|
6
|
-
* State Binding: The property key on the Engine's state to persist.
|
|
7
|
-
* e.g., 'items'
|
|
8
|
-
*/
|
|
9
|
-
bindTo: string;
|
|
10
|
-
/** If true, loads data immediately on install */
|
|
11
|
-
autoLoad?: boolean;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* IndexedDB Plugin
|
|
15
|
-
* Asynchronous persistence for larger datasets.
|
|
16
|
-
*/
|
|
17
|
-
export declare const IndexedDBPlugin: (config: IndexedDBConfig) => JuxServiceContract<BaseEngine<any>>;
|
|
18
|
-
//# sourceMappingURL=IndexedDBPlugin.d.ts.map
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* IndexedDB Plugin
|
|
3
|
-
* Asynchronous persistence for larger datasets.
|
|
4
|
-
*/
|
|
5
|
-
export const IndexedDBPlugin = (config) => ({
|
|
6
|
-
name: 'indexed-db-persist',
|
|
7
|
-
version: '1.0.0',
|
|
8
|
-
targetEnv: 'client',
|
|
9
|
-
install: (engine) => {
|
|
10
|
-
// @ts-ignore
|
|
11
|
-
const initialState = engine.state[config.bindTo];
|
|
12
|
-
if (initialState !== undefined && !Array.isArray(initialState)) {
|
|
13
|
-
console.error(`[IndexedDBPlugin] 🛑 Configuration Error: bindTo='${config.bindTo}' is not an Array. This plugin is designed for List/Collection data.`);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
let db = null;
|
|
17
|
-
const request = indexedDB.open(config.dbName, 1);
|
|
18
|
-
// 1. Schema Setup
|
|
19
|
-
request.onupgradeneeded = (e) => {
|
|
20
|
-
const d = e.target.result;
|
|
21
|
-
if (!d.objectStoreNames.contains(config.storeName)) {
|
|
22
|
-
d.createObjectStore(config.storeName);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
// 2. IO Operations
|
|
26
|
-
const save = (data) => {
|
|
27
|
-
if (!db)
|
|
28
|
-
return;
|
|
29
|
-
const tx = db.transaction([config.storeName], 'readwrite');
|
|
30
|
-
const store = tx.objectStore(config.storeName);
|
|
31
|
-
store.put(data, 'state_snapshot'); // Storing entire bound state as one blob for simplicity
|
|
32
|
-
};
|
|
33
|
-
const load = () => {
|
|
34
|
-
if (!db)
|
|
35
|
-
return;
|
|
36
|
-
// @ts-ignore
|
|
37
|
-
if (typeof engine.loading === 'function')
|
|
38
|
-
engine.loading(true);
|
|
39
|
-
const tx = db.transaction([config.storeName], 'readonly');
|
|
40
|
-
const store = tx.objectStore(config.storeName);
|
|
41
|
-
const req = store.get('state_snapshot');
|
|
42
|
-
req.onsuccess = () => {
|
|
43
|
-
if (req.result) {
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
engine.updateState({ [config.bindTo]: req.result });
|
|
46
|
-
// @ts-ignore
|
|
47
|
-
if (engine.emit)
|
|
48
|
-
engine.emit('plugin:hydrated', { source: 'IndexedDB' });
|
|
49
|
-
}
|
|
50
|
-
// @ts-ignore
|
|
51
|
-
if (typeof engine.loading === 'function')
|
|
52
|
-
engine.loading(false);
|
|
53
|
-
};
|
|
54
|
-
req.onerror = () => {
|
|
55
|
-
// @ts-ignore
|
|
56
|
-
if (typeof engine.loading === 'function')
|
|
57
|
-
engine.loading(false);
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
// 3. Initialization
|
|
61
|
-
request.onsuccess = (e) => {
|
|
62
|
-
db = e.target.result;
|
|
63
|
-
if (config.autoLoad) {
|
|
64
|
-
load();
|
|
65
|
-
}
|
|
66
|
-
// Auto-save subscription (debounced slightly in real world, direct here)
|
|
67
|
-
engine.subscribe((state) => {
|
|
68
|
-
if (db)
|
|
69
|
-
save(state[config.bindTo]);
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
request.onerror = (e) => console.error('[IndexedDBPlugin] Open Error', e);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
//# sourceMappingURL=IndexedDBPlugin.js.map
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { JuxServiceContract, BaseEngine } from '../base/BaseEngine.js';
|
|
2
|
-
export interface LocalStorageConfig {
|
|
3
|
-
/** Unique key for storage (e.g. 'my-app-list-v1') */
|
|
4
|
-
key: string;
|
|
5
|
-
/**
|
|
6
|
-
* State Binding: The property key on the Engine's state to persist.
|
|
7
|
-
* e.g., 'items' for a ListEngine.
|
|
8
|
-
*/
|
|
9
|
-
bindTo: string;
|
|
10
|
-
/** If true, clears storage on install (fresh start) */
|
|
11
|
-
clearOnInstall?: boolean;
|
|
12
|
-
/** If true, automatically saves to storage when state changes */
|
|
13
|
-
autoSave?: boolean;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* LocalStorage Plugin
|
|
17
|
-
* Persists a slice of Engine State to Browser LocalStorage.
|
|
18
|
-
*/
|
|
19
|
-
export declare const LocalStoragePlugin: (config: LocalStorageConfig) => JuxServiceContract<BaseEngine<any>>;
|
|
20
|
-
//# sourceMappingURL=LocalStoragePlugin.d.ts.map
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LocalStorage Plugin
|
|
3
|
-
* Persists a slice of Engine State to Browser LocalStorage.
|
|
4
|
-
*/
|
|
5
|
-
export const LocalStoragePlugin = (config) => ({
|
|
6
|
-
name: 'local-storage-persist',
|
|
7
|
-
version: '1.0.0',
|
|
8
|
-
targetEnv: 'client',
|
|
9
|
-
install: (engine) => {
|
|
10
|
-
// @ts-ignore
|
|
11
|
-
const initialState = engine.state[config.bindTo];
|
|
12
|
-
if (initialState !== undefined && !Array.isArray(initialState)) {
|
|
13
|
-
console.error(`[LocalStoragePlugin] 🛑 Configuration Error: bindTo='${config.bindTo}' is not an Array. This plugin is designed for List/Collection data.`);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const storageKey = `jux:${config.key}`;
|
|
17
|
-
// 1. Hydrate (Load from Storage)
|
|
18
|
-
const load = () => {
|
|
19
|
-
try {
|
|
20
|
-
const raw = localStorage.getItem(storageKey);
|
|
21
|
-
if (raw) {
|
|
22
|
-
const data = JSON.parse(raw);
|
|
23
|
-
// @ts-ignore - Generic update
|
|
24
|
-
engine.updateState({ [config.bindTo]: data });
|
|
25
|
-
// @ts-ignore
|
|
26
|
-
if (engine.emit)
|
|
27
|
-
engine.emit('plugin:hydrated', { source: 'localStorage', count: Array.isArray(data) ? data.length : 1 });
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch (e) {
|
|
31
|
-
console.error('[LocalStoragePlugin] Load Failed', e);
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
// 2. Persist (Save to Storage)
|
|
35
|
-
const save = (state) => {
|
|
36
|
-
try {
|
|
37
|
-
const value = state[config.bindTo];
|
|
38
|
-
localStorage.setItem(storageKey, JSON.stringify(value));
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
console.error('[LocalStoragePlugin] Save Failed', e);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
// Logic
|
|
45
|
-
if (config.clearOnInstall) {
|
|
46
|
-
localStorage.removeItem(storageKey);
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
load();
|
|
50
|
-
}
|
|
51
|
-
if (config.autoSave !== false) {
|
|
52
|
-
engine.subscribe((state) => save(state));
|
|
53
|
-
}
|
|
54
|
-
// Expose manual triggers
|
|
55
|
-
engine.on('storage:save', () => save(engine.state));
|
|
56
|
-
engine.on('storage:load', load);
|
|
57
|
-
engine.on('storage:clear', () => {
|
|
58
|
-
localStorage.removeItem(storageKey);
|
|
59
|
-
// @ts-ignore
|
|
60
|
-
if (engine.emit)
|
|
61
|
-
engine.emit('plugin:cleared', { source: 'localStorage' });
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
//# sourceMappingURL=LocalStoragePlugin.js.map
|