apprun 3.37.2 → 3.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +2 -0
- package/WHATSNEW.md +6 -0
- package/ai/apprun.prompt.md +545 -109
- package/apprun.d.ts +17 -5
- package/dist/apprun-code.js +1 -1
- package/dist/apprun-code.js.map +1 -1
- package/dist/apprun-dev-tools.js +1 -1
- package/dist/apprun-dev-tools.js.map +1 -1
- package/dist/apprun-html.esm.js +7 -7
- package/dist/apprun-html.esm.js.map +1 -1
- package/dist/apprun-html.js +1 -1
- package/dist/apprun-html.js.map +1 -1
- package/dist/apprun-play-html.esm.js +7 -7
- package/dist/apprun-play-html.esm.js.map +1 -1
- package/dist/apprun-play.js +1 -1
- package/dist/apprun-play.js.map +1 -1
- package/dist/apprun.esm.js +1 -1
- package/dist/apprun.esm.js.map +1 -1
- package/dist/apprun.js +1 -1
- package/dist/apprun.js.map +1 -1
- package/dist/createState.js.map +1 -1
- package/esm/apprun-play.js.map +1 -1
- package/esm/apprun.js.map +1 -1
- package/esm/directive.js +129 -8
- package/esm/directive.js.map +1 -1
- package/esm/vdom-my-prop-attr.js +2 -2
- package/esm/vdom-my-prop-attr.js.map +1 -1
- package/esm/version.js +1 -1
- package/jsx-runtime.js +1 -1
- package/jsx-runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/apprun-play.tsx +1 -1
- package/src/apprun.ts +2 -2
- package/src/component.ts +1 -1
- package/src/createState.ts +1 -1
- package/src/directive.ts +132 -7
- package/src/global.d.ts +2 -2
- package/src/types.ts +3 -3
- package/src/vdom-my-prop-attr.ts +2 -2
- package/src/version.ts +1 -1
package/package.json
CHANGED
package/src/apprun-play.tsx
CHANGED
|
@@ -194,7 +194,7 @@ const setup_editor = (textarea, iframe, code, hide_src) => {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
class Play extends Component {
|
|
197
|
+
class Play extends Component<any> {
|
|
198
198
|
view = (state) => {
|
|
199
199
|
const code_id = state['code-element-id'];
|
|
200
200
|
const element = this.element;
|
package/src/apprun.ts
CHANGED
|
@@ -73,7 +73,7 @@ import { APPRUN_VERSION } from './version';
|
|
|
73
73
|
|
|
74
74
|
export type StatelessComponent<T = {}> = (args: T) => string | VNode | void;
|
|
75
75
|
type OnDecorator = {
|
|
76
|
-
<T =
|
|
76
|
+
<T = unknown>(options?: any): (constructor: Function) => void;
|
|
77
77
|
<E = string>(events?: E, options?: any): (target: any, key: string) => void;
|
|
78
78
|
};
|
|
79
79
|
|
|
@@ -109,7 +109,7 @@ if (!app.start) {
|
|
|
109
109
|
app.webComponent = webComponent;
|
|
110
110
|
app.safeHTML = safeHTML;
|
|
111
111
|
|
|
112
|
-
app.start = <T, E =
|
|
112
|
+
app.start = <T, E = unknown>(element?: Element | string, state?: State<T>, view?: View<T>, update?: Update<T, E>,
|
|
113
113
|
options?: AppStartOptions<T>): Component<T, E> => {
|
|
114
114
|
const opts = { render: true, global_event: true, ...options };
|
|
115
115
|
const component = new Component<T, E>(state, view, update);
|
package/src/component.ts
CHANGED
|
@@ -66,7 +66,7 @@ export const REFRESH = state => state;
|
|
|
66
66
|
|
|
67
67
|
const app = _app as unknown as IApp;
|
|
68
68
|
|
|
69
|
-
export class Component<T =
|
|
69
|
+
export class Component<T = unknown, E = unknown> {
|
|
70
70
|
static __isAppRunComponent = true;
|
|
71
71
|
private _app = new App();
|
|
72
72
|
private _actions = [];
|
package/src/createState.ts
CHANGED
package/src/directive.ts
CHANGED
|
@@ -34,16 +34,36 @@
|
|
|
34
34
|
* - Enhanced error handling for invalid event targets
|
|
35
35
|
* - Safer DOM element property access
|
|
36
36
|
*
|
|
37
|
+
* Nested State Binding Support (v3.37.4):
|
|
38
|
+
* - Enhanced $bind directive to support nested object and array paths
|
|
39
|
+
* - Supports dot notation: 'user.name', 'user.profile.settings.theme'
|
|
40
|
+
* - Supports bracket notation: 'items[0]', 'users[1].name'
|
|
41
|
+
* - Supports mixed notation: 'users[0].settings.theme', 'data["key"].value'
|
|
42
|
+
* - Safe traversal with automatic intermediate object/array creation
|
|
43
|
+
* - Maintains backward compatibility with simple property binding
|
|
44
|
+
*
|
|
37
45
|
* Usage:
|
|
38
46
|
* ```tsx
|
|
39
47
|
* // Event binding
|
|
40
48
|
* <button $onclick="event-name">Click</button>
|
|
41
49
|
* <input $oninput={e => setState(e.target.value)} />
|
|
42
50
|
*
|
|
43
|
-
* //
|
|
51
|
+
* // Simple two-way binding
|
|
44
52
|
* <input $bind="state.property" />
|
|
45
53
|
* <select $bind="selected">...</select>
|
|
46
54
|
*
|
|
55
|
+
* // Nested object binding
|
|
56
|
+
* <input $bind="user.profile.name" />
|
|
57
|
+
* <input $bind="user.settings.theme" />
|
|
58
|
+
*
|
|
59
|
+
* // Array element binding
|
|
60
|
+
* <input $bind="items[0]" />
|
|
61
|
+
* <input $bind="todos[1].title" />
|
|
62
|
+
*
|
|
63
|
+
* // Mixed nested binding
|
|
64
|
+
* <input $bind="users[0].profile.settings.notifications.email" />
|
|
65
|
+
* <select $bind="config.display.mode">...</select>
|
|
66
|
+
*
|
|
47
67
|
* // Array handlers
|
|
48
68
|
* <button $onclick={['handler', param1, param2]}>Click</button>
|
|
49
69
|
* ```
|
|
@@ -52,18 +72,123 @@
|
|
|
52
72
|
import app from './app';
|
|
53
73
|
import { safeEventTarget } from './type-utils';
|
|
54
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Parse a path string into an array of keys and indices
|
|
77
|
+
* Supports paths like: 'a.b', 'a[0]', 'a[0].b', 'a["key"]'
|
|
78
|
+
*/
|
|
79
|
+
const parsePath = (path: string): Array<string | number> => {
|
|
80
|
+
if (!path) return [];
|
|
81
|
+
|
|
82
|
+
const keys: Array<string | number> = [];
|
|
83
|
+
let current = '';
|
|
84
|
+
let inBracket = false;
|
|
85
|
+
let quoteChar = '';
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < path.length; i++) {
|
|
88
|
+
const char = path[i];
|
|
89
|
+
|
|
90
|
+
if (char === '[' && !inBracket) {
|
|
91
|
+
if (current) {
|
|
92
|
+
keys.push(current);
|
|
93
|
+
current = '';
|
|
94
|
+
}
|
|
95
|
+
inBracket = true;
|
|
96
|
+
} else if (char === ']' && inBracket) {
|
|
97
|
+
if (quoteChar) {
|
|
98
|
+
// Remove quotes from string keys
|
|
99
|
+
current = current.slice(1, -1);
|
|
100
|
+
} else if (/^\d+$/.test(current)) {
|
|
101
|
+
// Convert numeric strings to numbers
|
|
102
|
+
current = parseInt(current, 10) as any;
|
|
103
|
+
}
|
|
104
|
+
keys.push(current);
|
|
105
|
+
current = '';
|
|
106
|
+
inBracket = false;
|
|
107
|
+
quoteChar = '';
|
|
108
|
+
} else if ((char === '"' || char === "'") && inBracket) {
|
|
109
|
+
if (!quoteChar) {
|
|
110
|
+
quoteChar = char;
|
|
111
|
+
} else if (char === quoteChar) {
|
|
112
|
+
quoteChar = '';
|
|
113
|
+
}
|
|
114
|
+
current += char;
|
|
115
|
+
} else if (char === '.' && !inBracket) {
|
|
116
|
+
if (current) {
|
|
117
|
+
keys.push(current);
|
|
118
|
+
current = '';
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
current += char;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (current) {
|
|
126
|
+
keys.push(current);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return keys;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Safely get a nested value from an object using a path
|
|
134
|
+
*/
|
|
135
|
+
const getNestedValue = (obj: any, path: Array<string | number>): any => {
|
|
136
|
+
let current = obj;
|
|
137
|
+
for (const key of path) {
|
|
138
|
+
if (current == null) return undefined;
|
|
139
|
+
current = current[key];
|
|
140
|
+
}
|
|
141
|
+
return current;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Safely set a nested value in an object using a path
|
|
146
|
+
* Creates intermediate objects/arrays as needed
|
|
147
|
+
*/
|
|
148
|
+
const setNestedValue = (obj: any, path: Array<string | number>, value: any): any => {
|
|
149
|
+
if (path.length === 0) return value;
|
|
150
|
+
|
|
151
|
+
const result = { ...obj };
|
|
152
|
+
let current = result;
|
|
153
|
+
|
|
154
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
155
|
+
const key = path[i];
|
|
156
|
+
const nextKey = path[i + 1];
|
|
157
|
+
|
|
158
|
+
if (current[key] == null) {
|
|
159
|
+
// Create array if next key is numeric, object otherwise
|
|
160
|
+
current[key] = typeof nextKey === 'number' ? [] : {};
|
|
161
|
+
} else if (Array.isArray(current[key])) {
|
|
162
|
+
current[key] = [...current[key]];
|
|
163
|
+
} else if (typeof current[key] === 'object') {
|
|
164
|
+
current[key] = { ...current[key] };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
current = current[key];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
current[path[path.length - 1]] = value;
|
|
171
|
+
return result;
|
|
172
|
+
};
|
|
173
|
+
|
|
55
174
|
const getStateValue = (component, name) => {
|
|
56
|
-
|
|
175
|
+
if (!name) return component['state'] || '';
|
|
176
|
+
|
|
177
|
+
const path = parsePath(name);
|
|
178
|
+
const value = getNestedValue(component['state'], path);
|
|
179
|
+
return value !== undefined ? value : '';
|
|
57
180
|
}
|
|
58
181
|
|
|
59
182
|
const setStateValue = (component, name, value) => {
|
|
60
|
-
if (name) {
|
|
61
|
-
const state = component['state'] || {};
|
|
62
|
-
state[name] = value;
|
|
63
|
-
component.setState(state);
|
|
64
|
-
} else {
|
|
183
|
+
if (!name) {
|
|
65
184
|
component.setState(value);
|
|
185
|
+
return;
|
|
66
186
|
}
|
|
187
|
+
|
|
188
|
+
const path = parsePath(name);
|
|
189
|
+
const currentState = component['state'] || {};
|
|
190
|
+
const newState = setNestedValue(currentState, path, value);
|
|
191
|
+
component.setState(newState);
|
|
67
192
|
}
|
|
68
193
|
|
|
69
194
|
const apply_directive = (key: string, props: {}, tag, component) => {
|
package/src/global.d.ts
CHANGED
|
@@ -8,12 +8,12 @@ declare global {
|
|
|
8
8
|
app: App;
|
|
9
9
|
_AppRunVersions: string;
|
|
10
10
|
Component: typeof Component & {
|
|
11
|
-
<T =
|
|
11
|
+
<T = unknown>(options?: any): (constructor: Function) => void;
|
|
12
12
|
};
|
|
13
13
|
_React: any;
|
|
14
14
|
React: App;
|
|
15
15
|
on: {
|
|
16
|
-
<T =
|
|
16
|
+
<T = unknown>(options?: any): (constructor: Function) => void;
|
|
17
17
|
<E = string>(events?: E, options?: any): (target: any, key: string) => void;
|
|
18
18
|
};
|
|
19
19
|
customElement: (name: string) => (constructor: Function) => void;
|
package/src/types.ts
CHANGED
|
@@ -66,7 +66,7 @@ export type VDOM = false | string | VNode | Array<VNode | string> | TemplateResu
|
|
|
66
66
|
export type View<T> = (state: T) => VDOM | void;
|
|
67
67
|
export type Action<T> = (state: T, ...p: any[]) => T | Promise<T> | void | AsyncGenerator<T> | Generator<T>;
|
|
68
68
|
export type ActionDef<T, E> = (readonly [E, Action<T>, {}?]);
|
|
69
|
-
export type Update<T, E =
|
|
69
|
+
export type Update<T, E = unknown> = ActionDef<T, E>[] | { [name: string]: Action<T> | {}[] } | (E | Action<T> | {})[];
|
|
70
70
|
export type ActionOptions = {
|
|
71
71
|
render?: boolean, history?, global?: boolean;
|
|
72
72
|
callback?: (state: any) => void;
|
|
@@ -111,7 +111,7 @@ export interface IApp {
|
|
|
111
111
|
/** @deprecated Use runAsync() instead */
|
|
112
112
|
query(name: string, ...args: any[]): Promise<any[]>;
|
|
113
113
|
|
|
114
|
-
start<T, E =
|
|
114
|
+
start<T, E = unknown>(element?: Element | string, model?: T, view?: View<T>, update?: Update<T, E>,
|
|
115
115
|
options?: AppStartOptions<T>): any;
|
|
116
116
|
|
|
117
117
|
h(tag: string | Function, props?: any, ...children: any[]): VNode | VNode[];
|
|
@@ -134,7 +134,7 @@ interface ComponentLike {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
// Define component constructor type
|
|
137
|
-
type ComponentConstructor<T =
|
|
137
|
+
type ComponentConstructor<T = unknown> = new (
|
|
138
138
|
state?: T,
|
|
139
139
|
view?: any,
|
|
140
140
|
update?: any,
|
package/src/vdom-my-prop-attr.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Standards-compliant property/attribute handling with caching and special element support
|
|
4
4
|
* Features: Skip logic for preserving user interactions during VDOM reconciliation
|
|
5
5
|
* Exports: updateProps - Main function for DOM element property updates
|
|
6
|
-
* Updated: 2025-01-14 -
|
|
6
|
+
* Updated: 2025-01-14 - Skip logic for focus-sensitive (selection), scroll, and media properties
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { find, html, svg } from 'property-information';
|
|
@@ -192,7 +192,7 @@ function setAttributeOrProperty(element: Element, name: string, value: any, isSv
|
|
|
192
192
|
// Skip logic for preventing user interaction disruption during VDOM reconciliation
|
|
193
193
|
function shouldSkipPatch(dom: HTMLElement, prop: string): boolean {
|
|
194
194
|
if (document.activeElement === dom) {
|
|
195
|
-
return ['
|
|
195
|
+
return ['selectionStart', 'selectionEnd', 'selectionDirection']
|
|
196
196
|
.includes(prop);
|
|
197
197
|
}
|
|
198
198
|
if (prop === 'scrollTop' || prop === 'scrollLeft') {
|
package/src/version.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
// Import version from package.json to maintain single source of truth
|
|
12
12
|
// This version string is used across the framework
|
|
13
|
-
export const APPRUN_VERSION = '3.
|
|
13
|
+
export const APPRUN_VERSION = '3.38.0';
|
|
14
14
|
|
|
15
15
|
// Version string with prefix for global tracking
|
|
16
16
|
export const APPRUN_VERSION_GLOBAL = `AppRun-${APPRUN_VERSION}`;
|