jsswift 2.0.1 → 2.1.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/AI.md +5 -0
- package/README.md +2 -0
- package/dist/build/build-configured-runtime.d.mts +29 -0
- package/dist/build/build-configured-runtime.mjs +92 -0
- package/dist/build/runtime-layout.mjs +13 -0
- package/dist/build/select-ui.mjs +167 -0
- package/dist/build/vite-jss.d.mts +2 -0
- package/dist/build/vite-jss.mjs +73 -0
- package/dist/cms.js +3522 -0
- package/dist/documentions/AI.md +5 -0
- package/dist/documentions/README.md +1 -0
- package/dist/documentions/configured-runtime.md +82 -0
- package/dist/documentions/knowledge-map.json +20 -1
- package/dist/ui-src/00-bootstrap.js +330 -0
- package/dist/ui-src/01-foundation-helpers.js +809 -0
- package/dist/ui-src/10-primitives-layout.js +5091 -0
- package/dist/ui-src/20-display-content.js +780 -0
- package/dist/ui-src/21-list-content.js +246 -0
- package/dist/ui-src/22-banner-content.js +376 -0
- package/dist/ui-src/23-tooltip.js +276 -0
- package/dist/ui-src/31-form-advanced.js +2784 -0
- package/dist/ui-src/40-navigation.js +1262 -0
- package/dist/ui-src/41-tab-panel.js +896 -0
- package/dist/ui-src/50-feedback.js +999 -0
- package/dist/ui-src/51-feedback-service.js +166 -0
- package/dist/ui-src/60-shell-app.js +1223 -0
- package/dist/ui-src/61-form-service.js +368 -0
- package/dist/ui-src/70-dialog.js +333 -0
- package/dist/ui-src/71-menu-overlays.js +1217 -0
- package/dist/ui-src/80-data.js +437 -0
- package/dist/ui-src/modules.json +212 -0
- package/dist/ui.js +12565 -0
- package/package.json +26 -2
package/dist/documentions/AI.md
CHANGED
|
@@ -16,6 +16,7 @@ The installed package exposes this same guide as `AI.md` at its root and through
|
|
|
16
16
|
| --- | --- | --- |
|
|
17
17
|
| Small page with the six components below | This guide | Implement the supported subset without opening component contracts. |
|
|
18
18
|
| Product CRUD with validation, persistence and focus | [CRUD example](examples/crud.md) | Reuse the example; do not also concatenate Input, Select, Dialog and Table contracts. |
|
|
19
|
+
| jss.config.js, selective runtime, Vite plugin or optional extras | [Configured runtime](configured-runtime.md) (`guide:configured-runtime`) | Follow the existing app configuration; do not also import the complete runtime/CSS. |
|
|
19
20
|
| One missing prop or callback | Map → exact document, or selective MCP read | Read only the needed section; do not search the runtime bundle. |
|
|
20
21
|
| Custom appearance | Supported props below, then one relevant contract | Keep component styles; add CSS for page layout and task-specific visuals. |
|
|
21
22
|
|
|
@@ -23,6 +24,8 @@ The map locates a contract; it does not replace it. Avoid reading all of `ui/com
|
|
|
23
24
|
|
|
24
25
|
## Import, create, mount
|
|
25
26
|
|
|
27
|
+
For an existing app, preserve its loading method. With the configured Vite plugin, import `jss:runtime` instead of the two traditional imports below: it includes the configured JavaScript, CSS and extras. Read [configured-runtime.md](configured-runtime.md) for setup and package availability; older installed releases may not expose this tooling.
|
|
28
|
+
|
|
26
29
|
With Vite, provide `<div id="app"></div>` and a module entrypoint:
|
|
27
30
|
|
|
28
31
|
```js
|
|
@@ -73,6 +76,8 @@ Prefer public props to rebuilding controls: `_.Button({color:'primary',outline:t
|
|
|
73
76
|
|
|
74
77
|
Classes and theme variables use `jss-`; migrate old `cms-` overrides. Fonts are system defaults. Import `jsswift/css/fonts-nunito.css`, `fonts-material.css`, or `jsswift/tabler` only when required; Material Symbols downloads a remote font.
|
|
75
78
|
|
|
79
|
+
With `jss:runtime`, choose assets in `jss.config.js` through `extras` instead of adding these imports. `ui: {all:true}` includes the complete UI and services. For a selective build, use `ui: {components:['Button','Input','Select','Card','Table'],services:['Dialog']}` for the example above; dependencies are automatic. Services also support Notify, Loading and Form (useForm). Add every component used through dynamic lookup explicitly. `responsive:false` retains base utilities and component mobile CSS but excludes breakpoint utilities; use true for tablet/pc props. Shared and complex CSS rules are retained conservatively.
|
|
80
|
+
|
|
76
81
|
## Accessibility and verification
|
|
77
82
|
|
|
78
83
|
String labels name Input/Select; set Dialog `ariaLabel`. Field errors are associated automatically. Dialog preserves focus assigned in `onOpen`. If rendering replaces the trigger, restore its current replacement in `onClose`; see CRUD.
|
|
@@ -8,6 +8,7 @@ Build applications with JSswift by reading only the smallest focused guide. Exac
|
|
|
8
8
|
| --- | --- |
|
|
9
9
|
| AI discovery, portable Skill, local MCP | [ai-development.md](ai-development.md), [knowledge-map.json](knowledge-map.json) |
|
|
10
10
|
| First app or screen | [AI.md](AI.md) alone; [getting-started.md](getting-started.md) only for additional setup |
|
|
11
|
+
| Experimental configured runtime build | [configured-runtime.md](configured-runtime.md) |
|
|
11
12
|
| Component choice and composition | [application-rules.md](application-rules.md), [components/README.md](components/README.md) |
|
|
12
13
|
| State and controlled inputs | [reactivity.md](reactivity.md) |
|
|
13
14
|
| Layout and breakpoints | [ui/layout-and-responsive.md](ui/layout-and-responsive.md) |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Configured application runtime (experimental)
|
|
2
|
+
|
|
3
|
+
`jss.config.js` selects UI components, services and optional assets at build time. The complete runtime remains compatible with existing imports and script bundles. The package build includes the tooling under jsswift/vite and jsswift/build, plus the jss-build command. These additions are included starting with jsswift 2.1.0; older installed releases do not expose them. Build tooling requires Node.js 20.19.x or >=22.12.0.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
export default {
|
|
9
|
+
ui: { all: true, responsive: true },
|
|
10
|
+
extras: ['material-symbols-outlined'],
|
|
11
|
+
};
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
For a selective application:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
export default {
|
|
18
|
+
ui: {
|
|
19
|
+
components: ['Button', 'Input', 'Table'],
|
|
20
|
+
services: ['Dialog', 'Notify'],
|
|
21
|
+
responsive: false,
|
|
22
|
+
},
|
|
23
|
+
extras: ['material-symbols-outlined'],
|
|
24
|
+
};
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`ui.all` defaults to true when neither components nor services is specified. Providing either list enables selective mode unless all is explicitly true. Use all:true alone; combining it with nonempty selection lists is rejected. `ui: {all:false}` without lists builds core-only JavaScript and base CSS. Unknown component, service, extra and configuration names fail the build.
|
|
28
|
+
|
|
29
|
+
Component names match the public API, including Button/Btn and cardHeader/cardBody/cardFooter. Supported services are Dialog (component plus alert/confirm/prompt shortcuts), Notify (component plus toast service), Loading (loading overlay), and Form (useForm). Dependencies are resolved from source statements; Button includes Icon, Tooltip and Spinner, while Table also includes Card. Referenced optional branches are included conservatively. Applications using dynamic component lookup must explicitly request every possible component.
|
|
30
|
+
|
|
31
|
+
`ui.responsive` defaults to true. False retains base layout utilities and component mobile CSS, but excludes breakpoint utilities. In selective mode, component-owned CSS selectors are pruned; shared tokens, native rules, utilities, animations, unknown selectors and complex functional selectors are retained conservatively. The core is always included completely.
|
|
32
|
+
|
|
33
|
+
Supported extras are `material-symbols-outlined`, `nunito-font`, and `tabler`; the default is an empty array. Material Symbols uses a remote Google font. Nunito font files and the Tabler sprite are local assets. Extra assets are included only when configured; separate application CSS/imports can still load additional assets independently.
|
|
34
|
+
|
|
35
|
+
## Vite
|
|
36
|
+
|
|
37
|
+
Install jsswift and Vite in your application, then register the plugin:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { defineConfig } from 'vite';
|
|
41
|
+
import { jssRuntime } from 'jsswift/vite';
|
|
42
|
+
|
|
43
|
+
export default defineConfig({ plugins: [jssRuntime()] });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Import the configured runtime from your application module:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
import 'jss:runtime';
|
|
50
|
+
_.mount('#app', _.Button({ label: 'Save' }));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The plugin includes JavaScript, CSS and extras together, emits jss-build-report.json in production, and restarts the development server when configuration changes. An alternative config path can be passed as `jssRuntime({config:'./other.config.js'})`. The repository ui.html demo now uses this integration; it requires Vite rather than opening the source HTML directly as a local file. The demo calls many components, so retain all:true unless its example code is reduced to the selected components.
|
|
54
|
+
|
|
55
|
+
## Standalone output
|
|
56
|
+
|
|
57
|
+
With jsswift installed, run the packaged CLI from your project:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npx jss-build jss.config.js dist-runtime
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You can also put `"build:runtime": "jss-build"` in your application package.json scripts. `jss-build --help` prints the arguments. For programmatic builds, import buildConfiguredRuntime or validateConfig from `jsswift/build`. The plugin and CLI use the package's own runtime, CSS, source modules and assets; no framework checkout is needed. Vite is an optional peer dependency and is not required for standalone builds.
|
|
64
|
+
|
|
65
|
+
In the framework checkout, first run `npm run build:runtime`, then:
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
npm run build:configured -- jss.config.js dist-runtime
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This generates runtime.js, css/runtime.css, selected extra assets and build-report.json. The report lists selected components/services, dependency reasons, inputs, removed CSS selectors and JavaScript/CSS byte counts. The output directory must be empty to avoid stale optional assets; use a fresh destination for subsequent standalone builds. Vite builds manage their own output normally.
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<link rel="stylesheet" href="./dist-runtime/css/runtime.css">
|
|
76
|
+
<script type="module">
|
|
77
|
+
import './dist-runtime/runtime.js';
|
|
78
|
+
_.mount('#app', _.Button({label:'Save'}));
|
|
79
|
+
</script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Continue using the single public `_` application namespace.
|
|
@@ -241,6 +241,25 @@
|
|
|
241
241
|
"paths": [
|
|
242
242
|
"ui/themes.md"
|
|
243
243
|
]
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"id": "configured-runtime",
|
|
247
|
+
"title": "Configured runtime and selective application builds",
|
|
248
|
+
"keywords": [
|
|
249
|
+
"jss.config.js",
|
|
250
|
+
"jss:runtime",
|
|
251
|
+
"jsswift/vite",
|
|
252
|
+
"jsswift/build",
|
|
253
|
+
"jss-build",
|
|
254
|
+
"selective runtime",
|
|
255
|
+
"componenti selettivi",
|
|
256
|
+
"extras",
|
|
257
|
+
"ui.all",
|
|
258
|
+
"runtime configurabile"
|
|
259
|
+
],
|
|
260
|
+
"paths": [
|
|
261
|
+
"configured-runtime.md"
|
|
262
|
+
]
|
|
244
263
|
}
|
|
245
264
|
],
|
|
246
265
|
"components": [
|
|
@@ -1004,5 +1023,5 @@
|
|
|
1004
1023
|
"documentAliases": {
|
|
1005
1024
|
"components/button.md": "components/btn.md"
|
|
1006
1025
|
},
|
|
1007
|
-
"packageVersion": "2.0
|
|
1026
|
+
"packageVersion": "2.1.0"
|
|
1008
1027
|
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
// ===============================
|
|
2
|
+
// JSswift UI Kit MVP
|
|
3
|
+
// ===============================
|
|
4
|
+
(function initJSswiftUI(app) {
|
|
5
|
+
app.ui = app.ui || {};
|
|
6
|
+
app.services = app.services || {};
|
|
7
|
+
app.services.notify = app.services.notify || {};
|
|
8
|
+
|
|
9
|
+
const uiRodPathRaw = Symbol("jss.ui.rodPathRaw");
|
|
10
|
+
const uiRodPathEnhanced = Symbol("jss.ui.rodPathEnhanced");
|
|
11
|
+
const uiRodPathPatchedH = Symbol("jss.ui.rodPathPatchedH");
|
|
12
|
+
const uiRodPathWrappedUI = Symbol("jss.ui.rodPathWrappedUI");
|
|
13
|
+
const uiRodPathAccess = new WeakMap();
|
|
14
|
+
const uiRodPathProxyCache = new WeakMap();
|
|
15
|
+
const uiWrappedFunctionCache = new WeakMap();
|
|
16
|
+
const uiRodPathReadBuffer = [];
|
|
17
|
+
const uiIsIndexKey = (key) => {
|
|
18
|
+
if (typeof key === "number") return Number.isInteger(key) && key >= 0;
|
|
19
|
+
if (typeof key !== "string" || key === "") return false;
|
|
20
|
+
if (key[0] === "-" || key.includes(".")) return false;
|
|
21
|
+
const num = Number(key);
|
|
22
|
+
return Number.isInteger(num) && num >= 0 && String(num) === key;
|
|
23
|
+
};
|
|
24
|
+
const uiTrackablePrimitive = (v) => {
|
|
25
|
+
const t = typeof v;
|
|
26
|
+
return t === "string" || t === "number" || t === "boolean" || t === "bigint";
|
|
27
|
+
};
|
|
28
|
+
const uiIsPlainObject = (v) => {
|
|
29
|
+
if (!v || typeof v !== "object") return false;
|
|
30
|
+
const p = Object.getPrototypeOf(v);
|
|
31
|
+
return p === Object.prototype || p === null;
|
|
32
|
+
};
|
|
33
|
+
let uiRodPathReadFlushQueued = false;
|
|
34
|
+
const uiScheduleRodPathReadFlush = () => {
|
|
35
|
+
if (uiRodPathReadFlushQueued) return;
|
|
36
|
+
uiRodPathReadFlushQueued = true;
|
|
37
|
+
queueMicrotask(() => {
|
|
38
|
+
uiRodPathReadFlushQueued = false;
|
|
39
|
+
uiRodPathReadBuffer.length = 0;
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const uiPushRodPathRead = (entry) => {
|
|
43
|
+
uiRodPathReadBuffer.push(entry);
|
|
44
|
+
if (uiRodPathReadBuffer.length > 128) {
|
|
45
|
+
uiRodPathReadBuffer.splice(0, uiRodPathReadBuffer.length - 128);
|
|
46
|
+
}
|
|
47
|
+
uiScheduleRodPathReadFlush();
|
|
48
|
+
};
|
|
49
|
+
const uiCreateRodPathCursor = () => {
|
|
50
|
+
if (!uiRodPathReadBuffer.length) return null;
|
|
51
|
+
const items = uiRodPathReadBuffer.slice();
|
|
52
|
+
uiRodPathReadBuffer.length = 0;
|
|
53
|
+
return { items, index: 0 };
|
|
54
|
+
};
|
|
55
|
+
const uiTakeRodPathMatch = (cursor, value) => {
|
|
56
|
+
if (!cursor) return null;
|
|
57
|
+
const entry = cursor.items[cursor.index];
|
|
58
|
+
if (!entry) return null;
|
|
59
|
+
if (!Object.is(entry.value, value)) return null;
|
|
60
|
+
cursor.index += 1;
|
|
61
|
+
return entry;
|
|
62
|
+
};
|
|
63
|
+
const uiUnwrapRodPathValue = (v) => {
|
|
64
|
+
if (v && typeof v === "object" && v[uiRodPathRaw]) return v[uiRodPathRaw];
|
|
65
|
+
return v;
|
|
66
|
+
};
|
|
67
|
+
const uiReadRodPath = (rod, path) => {
|
|
68
|
+
const access = uiRodPathAccess.get(rod);
|
|
69
|
+
if (!access) return undefined;
|
|
70
|
+
let cur = access.rawGet();
|
|
71
|
+
for (const key of path) {
|
|
72
|
+
if (cur == null) return undefined;
|
|
73
|
+
cur = cur[key];
|
|
74
|
+
}
|
|
75
|
+
return cur;
|
|
76
|
+
};
|
|
77
|
+
const uiWriteRodPath = (rod, path, nextValue) => {
|
|
78
|
+
const access = uiRodPathAccess.get(rod);
|
|
79
|
+
if (!access) return;
|
|
80
|
+
const unwrapped = uiUnwrapRodPathValue(nextValue);
|
|
81
|
+
if (!path.length) {
|
|
82
|
+
if (Object.is(access.rawGet(), unwrapped)) return;
|
|
83
|
+
access.rawSet(unwrapped);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let root = access.rawGet();
|
|
87
|
+
if (!root || typeof root !== "object") {
|
|
88
|
+
root = uiIsIndexKey(path[0]) ? [] : {};
|
|
89
|
+
}
|
|
90
|
+
let cur = root;
|
|
91
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
92
|
+
const key = path[i];
|
|
93
|
+
let child = cur[key];
|
|
94
|
+
if (!child || typeof child !== "object") {
|
|
95
|
+
const nextKey = path[i + 1];
|
|
96
|
+
child = uiIsIndexKey(nextKey) ? [] : {};
|
|
97
|
+
cur[key] = child;
|
|
98
|
+
}
|
|
99
|
+
cur = child;
|
|
100
|
+
}
|
|
101
|
+
if (Object.is(cur[path[path.length - 1]], unwrapped)) return;
|
|
102
|
+
cur[path[path.length - 1]] = unwrapped;
|
|
103
|
+
access.rawSet(root);
|
|
104
|
+
};
|
|
105
|
+
const uiCreateRodPathModel = (rod, path) => [
|
|
106
|
+
() => uiReadRodPath(rod, path),
|
|
107
|
+
(nextValue) => uiWriteRodPath(rod, path, nextValue)
|
|
108
|
+
];
|
|
109
|
+
const uiCreateRodPathProxy = (rod, target, path = []) => {
|
|
110
|
+
if (!target || typeof target !== "object") return target;
|
|
111
|
+
let byTarget = uiRodPathProxyCache.get(rod);
|
|
112
|
+
if (!byTarget) {
|
|
113
|
+
byTarget = new WeakMap();
|
|
114
|
+
uiRodPathProxyCache.set(rod, byTarget);
|
|
115
|
+
}
|
|
116
|
+
const cached = byTarget.get(target);
|
|
117
|
+
if (cached) return cached;
|
|
118
|
+
const proxy = new Proxy(target, {
|
|
119
|
+
get(obj, prop, receiver) {
|
|
120
|
+
if (prop === uiRodPathRaw) return obj;
|
|
121
|
+
const value = Reflect.get(obj, prop, receiver);
|
|
122
|
+
if (typeof prop === "symbol") return value;
|
|
123
|
+
const nextPath = path.concat(prop);
|
|
124
|
+
if (value && typeof value === "object") {
|
|
125
|
+
return uiCreateRodPathProxy(rod, value, nextPath);
|
|
126
|
+
}
|
|
127
|
+
if (typeof value === "function") {
|
|
128
|
+
return value.bind(receiver);
|
|
129
|
+
}
|
|
130
|
+
if (uiTrackablePrimitive(value)) {
|
|
131
|
+
uiPushRodPathRead({ rod, path: nextPath, value });
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
},
|
|
135
|
+
set(obj, prop, value, receiver) {
|
|
136
|
+
const nextValue = uiUnwrapRodPathValue(value);
|
|
137
|
+
const prevValue = Reflect.get(obj, prop, receiver);
|
|
138
|
+
if (Object.is(prevValue, nextValue)) return true;
|
|
139
|
+
const ok = Reflect.set(obj, prop, nextValue, receiver);
|
|
140
|
+
if (ok) {
|
|
141
|
+
const access = uiRodPathAccess.get(rod);
|
|
142
|
+
access?.rawSet(access.rawGet());
|
|
143
|
+
}
|
|
144
|
+
return ok;
|
|
145
|
+
},
|
|
146
|
+
deleteProperty(obj, prop) {
|
|
147
|
+
const hadKey = Reflect.has(obj, prop);
|
|
148
|
+
const ok = Reflect.deleteProperty(obj, prop);
|
|
149
|
+
if (ok && hadKey) {
|
|
150
|
+
const access = uiRodPathAccess.get(rod);
|
|
151
|
+
access?.rawSet(access.rawGet());
|
|
152
|
+
}
|
|
153
|
+
return ok;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
byTarget.set(target, proxy);
|
|
157
|
+
return proxy;
|
|
158
|
+
};
|
|
159
|
+
const uiEnhanceRodPath = (rod) => {
|
|
160
|
+
if (!rod || typeof rod !== "object" || rod.type !== "rod" || rod[uiRodPathEnhanced]) return rod;
|
|
161
|
+
const desc = Object.getOwnPropertyDescriptor(rod, "value");
|
|
162
|
+
if (!desc || typeof desc.get !== "function" || typeof desc.set !== "function") return rod;
|
|
163
|
+
const rawGet = () => desc.get.call(rod);
|
|
164
|
+
const rawSet = (v) => desc.set.call(rod, uiUnwrapRodPathValue(v));
|
|
165
|
+
uiRodPathAccess.set(rod, { rawGet, rawSet });
|
|
166
|
+
Object.defineProperty(rod, "value", {
|
|
167
|
+
get() {
|
|
168
|
+
const raw = rawGet();
|
|
169
|
+
if (!raw || typeof raw !== "object") return raw;
|
|
170
|
+
return uiCreateRodPathProxy(rod, raw, []);
|
|
171
|
+
},
|
|
172
|
+
set(v) {
|
|
173
|
+
rawSet(v);
|
|
174
|
+
},
|
|
175
|
+
configurable: true
|
|
176
|
+
});
|
|
177
|
+
Object.defineProperty(rod, uiRodPathEnhanced, { value: true, configurable: false });
|
|
178
|
+
return rod;
|
|
179
|
+
};
|
|
180
|
+
const uiPatchRodFactory = () => {
|
|
181
|
+
if (typeof window._.rod !== "function" || window._.rod[uiRodPathEnhanced]) return;
|
|
182
|
+
const baseRod = window._.rod;
|
|
183
|
+
const patchedRod = function patchedRod(...args) {
|
|
184
|
+
return uiEnhanceRodPath(baseRod(...args));
|
|
185
|
+
};
|
|
186
|
+
Object.defineProperty(patchedRod, uiRodPathEnhanced, { value: true, configurable: false });
|
|
187
|
+
window._.rod = patchedRod;
|
|
188
|
+
};
|
|
189
|
+
app.ui.withoutRodPathCapture = (fn) => {
|
|
190
|
+
const length = uiRodPathReadBuffer.length;
|
|
191
|
+
try {
|
|
192
|
+
return fn();
|
|
193
|
+
} finally {
|
|
194
|
+
uiRodPathReadBuffer.length = length;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const uiPatchValueForH = (value, cursor, keyHint = "") => {
|
|
198
|
+
if (Array.isArray(value)) {
|
|
199
|
+
let changed = false;
|
|
200
|
+
const next = value.map((item) => {
|
|
201
|
+
const out = uiPatchValueForH(item, cursor, "");
|
|
202
|
+
if (out !== item) changed = true;
|
|
203
|
+
return out;
|
|
204
|
+
});
|
|
205
|
+
return changed ? next : value;
|
|
206
|
+
}
|
|
207
|
+
if (uiTrackablePrimitive(value)) {
|
|
208
|
+
const entry = uiTakeRodPathMatch(cursor, value);
|
|
209
|
+
if (!entry) return value;
|
|
210
|
+
if (keyHint === "model") return uiCreateRodPathModel(entry.rod, entry.path);
|
|
211
|
+
return () => uiReadRodPath(entry.rod, entry.path);
|
|
212
|
+
}
|
|
213
|
+
if (!uiIsPlainObject(value)) return value;
|
|
214
|
+
let changed = false;
|
|
215
|
+
const out = {};
|
|
216
|
+
for (const key of Object.keys(value)) {
|
|
217
|
+
const next = uiPatchValueForH(value[key], cursor, key);
|
|
218
|
+
out[key] = next;
|
|
219
|
+
if (next !== value[key]) changed = true;
|
|
220
|
+
}
|
|
221
|
+
return changed ? out : value;
|
|
222
|
+
};
|
|
223
|
+
const uiPatchValueForUI = (value, cursor, keyHint = "") => {
|
|
224
|
+
if (Array.isArray(value)) {
|
|
225
|
+
let changed = false;
|
|
226
|
+
const next = value.map((item) => {
|
|
227
|
+
const out = uiPatchValueForUI(item, cursor, "");
|
|
228
|
+
if (out !== item) changed = true;
|
|
229
|
+
return out;
|
|
230
|
+
});
|
|
231
|
+
return changed ? next : value;
|
|
232
|
+
}
|
|
233
|
+
if (uiTrackablePrimitive(value)) {
|
|
234
|
+
const entry = uiTakeRodPathMatch(cursor, value);
|
|
235
|
+
if (!entry) return value;
|
|
236
|
+
if (keyHint === "model") return uiCreateRodPathModel(entry.rod, entry.path);
|
|
237
|
+
return () => uiReadRodPath(entry.rod, entry.path);
|
|
238
|
+
}
|
|
239
|
+
if (!uiIsPlainObject(value)) return value;
|
|
240
|
+
let changed = false;
|
|
241
|
+
const out = {};
|
|
242
|
+
for (const key of Object.keys(value)) {
|
|
243
|
+
const shouldPatch = key === "model" || key === "children";
|
|
244
|
+
const next = shouldPatch ? uiPatchValueForUI(value[key], cursor, key) : value[key];
|
|
245
|
+
out[key] = next;
|
|
246
|
+
if (next !== value[key]) changed = true;
|
|
247
|
+
}
|
|
248
|
+
return changed ? out : value;
|
|
249
|
+
};
|
|
250
|
+
const uiWrapUIFunction = (fn) => {
|
|
251
|
+
if (typeof fn !== "function" || fn[uiRodPathWrappedUI]) return fn;
|
|
252
|
+
const cached = uiWrappedFunctionCache.get(fn);
|
|
253
|
+
if (cached) return cached;
|
|
254
|
+
const wrapped = function uiWrappedComponent(...args) {
|
|
255
|
+
const cursor = uiCreateRodPathCursor();
|
|
256
|
+
if (!cursor) return fn.apply(this, args);
|
|
257
|
+
let changed = false;
|
|
258
|
+
const patchedArgs = args.map((arg) => {
|
|
259
|
+
const out = uiPatchValueForUI(arg, cursor, "");
|
|
260
|
+
if (out !== arg) changed = true;
|
|
261
|
+
return out;
|
|
262
|
+
});
|
|
263
|
+
return fn.apply(this, changed ? patchedArgs : args);
|
|
264
|
+
};
|
|
265
|
+
Object.defineProperty(wrapped, uiRodPathWrappedUI, { value: true, configurable: false });
|
|
266
|
+
uiWrappedFunctionCache.set(fn, wrapped);
|
|
267
|
+
return wrapped;
|
|
268
|
+
};
|
|
269
|
+
const uiInstallUIProxy = () => {
|
|
270
|
+
const uiTarget = app.ui;
|
|
271
|
+
if (!uiTarget || uiTarget[uiRodPathWrappedUI]) return;
|
|
272
|
+
Object.defineProperty(uiTarget, uiRodPathWrappedUI, { value: true, configurable: false });
|
|
273
|
+
for (const key of Object.keys(uiTarget)) {
|
|
274
|
+
if (typeof uiTarget[key] === "function") {
|
|
275
|
+
uiTarget[key] = uiWrapUIFunction(uiTarget[key]);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
app.ui = new Proxy(uiTarget, {
|
|
279
|
+
get(target, prop, receiver) {
|
|
280
|
+
return Reflect.get(target, prop, receiver);
|
|
281
|
+
},
|
|
282
|
+
set(target, prop, value, receiver) {
|
|
283
|
+
const next = typeof value === "function" ? uiWrapUIFunction(value) : value;
|
|
284
|
+
return Reflect.set(target, prop, next, receiver);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
};
|
|
288
|
+
const uiPatchHyperscript = () => {
|
|
289
|
+
if (!window._ || window._[uiRodPathPatchedH]) return;
|
|
290
|
+
Object.defineProperty(window._, uiRodPathPatchedH, { value: true, configurable: false });
|
|
291
|
+
const skipKeys = new Set(["rod", "rodBind", "rodFromSignal"]);
|
|
292
|
+
for (const key of Object.keys(window._)) {
|
|
293
|
+
if (skipKeys.has(key)) continue;
|
|
294
|
+
const base = window._[key];
|
|
295
|
+
if (typeof base !== "function") continue;
|
|
296
|
+
window._[key] = function uiWrappedHyperscript(...args) {
|
|
297
|
+
const cursor = uiCreateRodPathCursor();
|
|
298
|
+
if (!cursor) return base.apply(this, args);
|
|
299
|
+
let changed = false;
|
|
300
|
+
const patchedArgs = args.map((arg) => {
|
|
301
|
+
const out = uiPatchValueForH(arg, cursor, "");
|
|
302
|
+
if (out !== arg) changed = true;
|
|
303
|
+
return out;
|
|
304
|
+
});
|
|
305
|
+
return base.apply(this, changed ? patchedArgs : args);
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
uiPatchRodFactory();
|
|
311
|
+
if (JSswift.rod?._all instanceof Set) {
|
|
312
|
+
JSswift.rod._all.forEach((rod) => uiEnhanceRodPath(rod));
|
|
313
|
+
}
|
|
314
|
+
uiPatchHyperscript();
|
|
315
|
+
uiInstallUIProxy();
|
|
316
|
+
|
|
317
|
+
const UI_STATE_ALIASES = {
|
|
318
|
+
error: "danger",
|
|
319
|
+
danger: "danger",
|
|
320
|
+
warn: "warning",
|
|
321
|
+
warning: "warning",
|
|
322
|
+
success: "success",
|
|
323
|
+
info: "info",
|
|
324
|
+
primary: "primary",
|
|
325
|
+
secondary: "secondary",
|
|
326
|
+
light: "light",
|
|
327
|
+
dark: "dark"
|
|
328
|
+
};
|
|
329
|
+
const UI_STATE_TOKENS = new Set(Object.values(UI_STATE_ALIASES));
|
|
330
|
+
const UI_RADIUS_TOKENS = new Set(["none", "xxs", "xs", "sm", "md", "lg", "xl", "xxl", "xxxl", "full"]);
|