lecodes-cli 0.7.2 → 0.8.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/dist/index.js +2909 -725
- package/package.json +2 -2
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk/canvas/Canvas.ts +16 -0
- package/runtime/sdk/gl/Material.ts +4 -0
- package/runtime/sdk/inject.ts +7 -4
- package/runtime/sdk/plugins/service.ts +47 -0
- package/runtime/sdk/runtime/app.ts +21 -1
- package/runtime/sdk/runtime/appEvents.ts +25 -0
- package/runtime/sdk/runtime/service.ts +3 -2
- package/runtime/sdk/ui/NativeView.ts +2 -1
- package/runtime/sdk/ui/UI.ts +3 -1
- package/runtime/sdk/ui/UIButton.ts +10 -5
- package/runtime/sdk/ui/UIContainer.ts +16 -9
- package/runtime/sdk/ui/UIImage.ts +5 -2
- package/runtime/sdk/ui/UIInput.ts +75 -8
- package/runtime/sdk/ui/UIModal.ts +149 -0
- package/runtime/sdk/ui/UINode.ts +113 -50
- package/runtime/sdk/ui/UIPager.ts +297 -0
- package/runtime/sdk/ui/UIPopover.ts +106 -0
- package/runtime/sdk/ui/UIScreen.ts +11 -10
- package/runtime/sdk/ui/UIScrollable.ts +2 -1
- package/runtime/sdk/ui/UISpacer.ts +1 -1
- package/runtime/sdk/ui/UIText.ts +2 -1
- package/runtime/sdk/ui/UIVideo.ts +2 -1
- package/runtime/sdk/ui/UIVirtualizedList.ts +2 -1
- package/runtime/sdk/ui/UIWidget.ts +19 -22
- package/runtime/sdk/ui/theme.ts +66 -0
- package/runtime/sdk-types.json +1 -1
- package/runtime/sdk/compile/aspectMacro.ts +0 -42
- package/runtime/sdk/compile/assetMacro.ts +0 -45
- package/runtime/sdk/compile/bundler.ts +0 -207
- package/runtime/sdk/compile/compileProject.ts +0 -113
- package/runtime/sdk/compile/header.ts +0 -46
- package/runtime/sdk/compile/index.ts +0 -18
- package/runtime/sdk/compile/libraryImports.ts +0 -47
- package/runtime/sdk/compile/sceneEditor.ts +0 -78
- package/runtime/sdk/compile/sourcemap.ts +0 -25
- package/runtime/sdk/kit/UITabs.ts +0 -105
- package/runtime/sdk/ui/UIScreenHost.ts +0 -213
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TouchStartEvent } from "../runtime/touch"
|
|
2
2
|
import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
|
|
3
|
-
import { ContainerElement, type AnimateStyle, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type OnLayoutCallback, type PaddingStyle, type ScrollStyle, type Style, type UINodeChild, type ChildrenFn, } from "./UINode"
|
|
3
|
+
import { ContainerElement, type AnimateStyle, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type OnLayoutCallback, type BoundingClientRect, type PaddingStyle, type ScrollStyle, type Style, type UINodeChild, type ChildrenFn, } from "./UINode"
|
|
4
4
|
|
|
5
5
|
export type UIScreenStyle = ContainerStyle & DrawableStyle & PaddingStyle & ScrollStyle & BaseStyle & ScrollStyle & { refreshControlColor?: Color }
|
|
6
6
|
|
|
@@ -8,11 +8,11 @@ export type UIScreenStyle = ContainerStyle & DrawableStyle & PaddingStyle & Scro
|
|
|
8
8
|
// plain `UIScreen`, and a `UIScrollableScreen` (a screen that also scrolls). `makeScrollable()`
|
|
9
9
|
// upgrades a plain screen to the scrollable one, where `onScroll`/`onOverscroll`/`onRefresh` live —
|
|
10
10
|
// they mean nothing without scrolling. `UIScrollableScreen extends UIScreen`, so anywhere a
|
|
11
|
-
// `UIScreen`
|
|
11
|
+
// `UIScreen` is accepted (router pages, `UIPager`), a scrollable one works too.
|
|
12
12
|
//
|
|
13
13
|
// This is a subtype split, NOT a `UIScreen<Scrollable>` generic: a generic can't work while `style`
|
|
14
14
|
// is `Style<this,…>`, because `this` is then contravariant and the type becomes invariant in its own
|
|
15
|
-
// params — that was the old `UIScreen<false>`-not-assignable-to-`
|
|
15
|
+
// params — that was the old `UIScreen<false>`-not-assignable-to-`UIScreen<boolean>` bug. See the StyleFn
|
|
16
16
|
// note in UINode.ts; the contract is pinned by tests/ui-types.test.ts.
|
|
17
17
|
export interface UIScreen {
|
|
18
18
|
readonly type: "screen",
|
|
@@ -41,17 +41,18 @@ export interface UIScreen {
|
|
|
41
41
|
onClose(callback: () => void): this
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* Keep this screen's built native tree alive when a
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
44
|
+
* Keep this screen's built native tree alive when a host detaches it: instead of being destroyed,
|
|
45
|
+
* its views + layout + live state (scroll position, input text) are retained, so re-hosting the
|
|
46
|
+
* SAME screen object restores it in place rather than rebuilding. `onClose`/`onOpen` still fire on
|
|
47
|
+
* detach/re-attach. You own the lifetime — a kept tree is freed only by `dispose()` or when the
|
|
48
|
+
* project unloads. (Native-only today; web rebuilds.)
|
|
49
49
|
*/
|
|
50
50
|
keepAlive(enabled?: boolean): this
|
|
51
51
|
/** Free a `keepAlive()` screen's retained tree now. No-op unless it's currently detached-and-kept. */
|
|
52
52
|
dispose(): this
|
|
53
53
|
|
|
54
54
|
onLayout(onLayout: OnLayoutCallback): this
|
|
55
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
55
56
|
|
|
56
57
|
setClass(name: string, enabled: boolean): this
|
|
57
58
|
toggleClass(name: string): this
|
|
@@ -63,8 +64,8 @@ export interface UIScreen {
|
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
66
|
* A `UIScreen` in scroll mode — what `makeScrollable()` returns. Adds the scroll lifecycle that a
|
|
66
|
-
* plain screen doesn't carry. A subtype of `UIScreen`, so it's accepted anywhere a screen
|
|
67
|
-
*
|
|
67
|
+
* plain screen doesn't carry. A subtype of `UIScreen`, so it's accepted anywhere a screen is
|
|
68
|
+
* (router pages, `UIPager`).
|
|
68
69
|
*/
|
|
69
70
|
export interface UIScrollableScreen extends UIScreen {
|
|
70
71
|
readonly scrollable: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { UIContainerStyle } from "./UIContainer"
|
|
2
|
-
import { type Style, type AnimateStyle, type UINodeChild, type ChildrenFn, type OnLayoutCallback, ContainerElement } from "./UINode"
|
|
2
|
+
import { type Style, type AnimateStyle, type UINodeChild, type ChildrenFn, type OnLayoutCallback, type BoundingClientRect, ContainerElement } from "./UINode"
|
|
3
3
|
|
|
4
4
|
export type ScrollStyle = { showScrollbar?: boolean, overscrollMode?: "none" | "absorb" | "default" }
|
|
5
5
|
|
|
@@ -17,6 +17,7 @@ export interface UIScrollable {
|
|
|
17
17
|
setContent(nodes: UINodeChild[] | ChildrenFn): this
|
|
18
18
|
|
|
19
19
|
onLayout(onLayout: OnLayoutCallback): this
|
|
20
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
20
21
|
|
|
21
22
|
setClass(name: string, enabled: boolean): this
|
|
22
23
|
toggleClass(name: string): this
|
|
@@ -14,5 +14,5 @@ export interface UISpacer {
|
|
|
14
14
|
export function UISpacer(): UISpacer
|
|
15
15
|
export function UISpacer(style: UISpacerStyle): UISpacer
|
|
16
16
|
export function UISpacer(style?: UISpacerStyle): UISpacer {
|
|
17
|
-
return new Element("spacer", style
|
|
17
|
+
return new Element("spacer", { flexGrow: 1, ...style })
|
|
18
18
|
}
|
package/runtime/sdk/ui/UIText.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createBinding } from "../core/signals"
|
|
2
|
-
import { Element, type AnimateStyle, type BaseStyle, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type Style, type TextStyle } from "./UINode"
|
|
2
|
+
import { Element, type AnimateStyle, type BaseStyle, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type BoundingClientRect, type Style, type TextStyle } from "./UINode"
|
|
3
3
|
|
|
4
4
|
export type UITextStyle = ElementStyle & TextStyle & DrawableStyle
|
|
5
5
|
|
|
@@ -11,6 +11,7 @@ export interface UIText {
|
|
|
11
11
|
animateFrom: AnimateStyle<this, DrawableStyle & BaseStyle & TextStyle>
|
|
12
12
|
|
|
13
13
|
onLayout(onLayout: OnLayoutCallback): this
|
|
14
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
14
15
|
|
|
15
16
|
setClass(name: string, enabled: boolean): this
|
|
16
17
|
toggleClass(name: string): this
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { VideoPlayer } from "../runtime/media"
|
|
2
2
|
import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
|
|
3
|
-
import { Element, type AnimateStyle, type AppearStyle, type BaseStyle, type DisappearStyle, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type Style, type StyleFn } from "./UINode"
|
|
3
|
+
import { Element, type AnimateStyle, type AppearStyle, type BaseStyle, type DisappearStyle, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type BoundingClientRect, type Style, type StyleFn } from "./UINode"
|
|
4
4
|
|
|
5
5
|
export type UIVideoStyle = ElementStyle & DrawableStyle & { objectFit?: "cover" | "contain" | "fill" }
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@ export interface UIVideo {
|
|
|
20
20
|
onBackPressed(callback: () => void): this
|
|
21
21
|
|
|
22
22
|
onLayout(onLayout: OnLayoutCallback): this
|
|
23
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
23
24
|
|
|
24
25
|
setClass(name: string, enabled: boolean): this
|
|
25
26
|
toggleClass(name: string): this
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Element, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type PaddingStyle, type Style, type UINodeChild } from "./UINode"
|
|
1
|
+
import { Element, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type BoundingClientRect, type PaddingStyle, type Style, type UINodeChild } from "./UINode"
|
|
2
2
|
|
|
3
3
|
export type UIVirtualizedListStyle = DrawableStyle & ElementStyle
|
|
4
4
|
|
|
@@ -45,6 +45,7 @@ export interface UIVirtualizedList<T = unknown> {
|
|
|
45
45
|
/** Порог от верхнего края — подгрузка истории в чате */
|
|
46
46
|
onStartReached(thresholdPx: number, callback: () => void): this
|
|
47
47
|
onLayout(onLayout: OnLayoutCallback): this
|
|
48
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
48
49
|
|
|
49
50
|
setClass(name: string, enabled: boolean): this
|
|
50
51
|
toggleClass(name: string): this
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TouchStartEvent } from "../runtime/touch"
|
|
2
2
|
import { _descOf, _navSupported, Presentable } from "./presentable"
|
|
3
|
-
import { ContainerElement, type AnimateStyle, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type OnLayoutCallback, type PaddingStyle, type PositionStyle, type Style, type UINodeChild, type ChildrenFn } from "./UINode"
|
|
3
|
+
import { ContainerElement, type AnimateStyle, type BaseStyle, type Color, type ContainerStyle, type DrawableStyle, type OnLayoutCallback, type BoundingClientRect, type PaddingStyle, type PositionStyle, type Style, type UINodeChild, type ChildrenFn } from "./UINode"
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
export type UIWidgetStyle = ContainerStyle & DrawableStyle & PaddingStyle & BaseStyle & PositionStyle & {
|
|
@@ -17,22 +17,18 @@ export interface UIWidget {
|
|
|
17
17
|
animateFrom: AnimateStyle<this, DrawableStyle & BaseStyle & { overlayColor?: Color | null }>
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* one until `attachTo(null)` (= back to automatic). Set it while the widget is hidden.
|
|
20
|
+
* Attach this widget to a destination (a `Scene`, a `UIScreen`, …): the widget lives on that
|
|
21
|
+
* page's layer — it shows/hides with the page and rides its transition, and a screen pushed
|
|
22
|
+
* on top covers it (a scene HUD behaves like part of the scene). Sticky until `attachTo(null)`
|
|
23
|
+
* (= back to a global overlay). Set it while the widget is hidden.
|
|
25
24
|
*/
|
|
26
25
|
attachTo(owner: Presentable | null): this,
|
|
27
26
|
|
|
28
|
-
/** Show the widget
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
/** Show the widget. By default it is a global overlay above every destination — it stays
|
|
28
|
+
* visible across navigation until `hide()` (dialogs, mini-player, global loader). If the
|
|
29
|
+
* widget was attached via `attachTo(owner)`, it mounts on that page's layer instead and is
|
|
30
|
+
* only visible while the owner is presented. */
|
|
32
31
|
show(): void,
|
|
33
|
-
/** Show the widget as a global overlay above every destination — it stays visible across
|
|
34
|
-
* navigation (mini-player, global loader). */
|
|
35
|
-
showDetached(): void,
|
|
36
32
|
hide(): void,
|
|
37
33
|
|
|
38
34
|
readonly isShow: boolean,
|
|
@@ -45,6 +41,7 @@ export interface UIWidget {
|
|
|
45
41
|
readonly children: UINodeChild[],
|
|
46
42
|
|
|
47
43
|
onLayout(onLayout: OnLayoutCallback): this
|
|
44
|
+
getBoundingClientRect(): BoundingClientRect | null
|
|
48
45
|
onTouchStart(callback: (ev: TouchStartEvent<UIWidget>) => void): this,
|
|
49
46
|
onOverlayTap(callback: () => void): this,
|
|
50
47
|
onBackPressed(callback: () => void): this
|
|
@@ -59,24 +56,24 @@ export interface UIWidget {
|
|
|
59
56
|
export class WidgetElement extends ContainerElement<"widget"> {
|
|
60
57
|
scrollable = false as const
|
|
61
58
|
_owner?: Presentable
|
|
59
|
+
constructor(type: "widget", style: UIWidgetStyle, children: UINodeChild[] | ChildrenFn) {
|
|
60
|
+
// Widgets are absolutely positioned in device coordinates — in the constructor (not the
|
|
61
|
+
// factory) so subclasses like ModalElement inherit it.
|
|
62
|
+
super(type, { position: "absolute", ...style }, children)
|
|
63
|
+
}
|
|
62
64
|
attachTo(owner: Presentable | null): this {
|
|
63
65
|
this._owner = owner ?? undefined
|
|
64
66
|
return this
|
|
65
67
|
}
|
|
66
68
|
show(): void {
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (owner && _navSupported()) {
|
|
71
|
-
_creatorUI.showWidget(this, _descOf(owner))
|
|
69
|
+
// Global overlay unless explicitly attached via attachTo(owner).
|
|
70
|
+
if (this._owner && _navSupported()) {
|
|
71
|
+
_creatorUI.showWidget(this, _descOf(this._owner))
|
|
72
72
|
} else {
|
|
73
|
-
//
|
|
73
|
+
// Unattached (or legacy host): global overlay above navigation.
|
|
74
74
|
_creatorUI.showWidget(this)
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
showDetached(): void {
|
|
78
|
-
_creatorUI.showWidget(this)
|
|
79
|
-
}
|
|
80
77
|
hide(): void {
|
|
81
78
|
_creatorUI.hideWidget(this)
|
|
82
79
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// theme() — app theme variables (docs/ui-theme-plan.md).
|
|
2
|
+
//
|
|
3
|
+
// One global variable table lives in the UI core. Styles reference entries with CSS syntax —
|
|
4
|
+
// `color: "var(--primaryColor)"` — resolved at style-apply time in the core, so re-calling
|
|
5
|
+
// theme() with new values re-styles the LIVE UI (dark mode is just a second call). Two access
|
|
6
|
+
// forms with one rule: bare style tokens (`pt: "comfort-top"`) are keywords that may expand to
|
|
7
|
+
// formulas (max(safe-top, knob)); `var(--comfort-top)` always reads the raw table value (the
|
|
8
|
+
// knob itself).
|
|
9
|
+
//
|
|
10
|
+
// theme(values) MERGES into the table and returns typed accessors whose values ARE the var()
|
|
11
|
+
// strings — define once in a module, import where custom keys need typing:
|
|
12
|
+
//
|
|
13
|
+
// export const T = theme({ primaryColor: "#0A84FF", cardBg: "#1C1C1E" })
|
|
14
|
+
// card.style({ bgColor: T.cardBg }) // T.cardBg === "var(--cardBg)"
|
|
15
|
+
//
|
|
16
|
+
// System vars need no import — they're static properties on the callable itself:
|
|
17
|
+
// theme.primaryColor, theme.fontFamily, theme["comfort-top"], …
|
|
18
|
+
//
|
|
19
|
+
// Values: strings pass through (colors, fonts, expressions like "max(safe-top, 24px)");
|
|
20
|
+
// numbers are LENGTHS (px). `null` removes a key (comfort knobs reset to their defaults:
|
|
21
|
+
// 12 vertical, 16 horizontal — the page gutter). Env names (safe-*, vw/vh/vmin/vmax) are
|
|
22
|
+
// host-owned facts, never theme keys — they're skipped with a warning.
|
|
23
|
+
|
|
24
|
+
export type ThemeValues = Record<string, string | number | null>
|
|
25
|
+
|
|
26
|
+
export type ThemeAccessors<T extends ThemeValues> = {
|
|
27
|
+
readonly [K in keyof T]: K extends string ? `var(--${K})` : never
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const RESERVED = ["safe-top", "safe-bottom", "safe-left", "safe-right", "vw", "vh", "vmin", "vmax"]
|
|
31
|
+
|
|
32
|
+
const setTheme = <T extends ThemeValues>(values: T): ThemeAccessors<T> => {
|
|
33
|
+
const accessors: Record<string, string> = {}
|
|
34
|
+
let send: ThemeValues = values
|
|
35
|
+
for (const key of Object.keys(values)) {
|
|
36
|
+
if (RESERVED.includes(key)) {
|
|
37
|
+
console.warn(`theme(): "${key}" is an environment value (host-owned) — ignored`)
|
|
38
|
+
if (send === values) send = { ...values }
|
|
39
|
+
delete send[key]
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
accessors[key] = `var(--${key})`
|
|
43
|
+
}
|
|
44
|
+
// Optional during rollout: hosts without the method simply don't theme (values still type).
|
|
45
|
+
_creatorUI.setTheme?.(send)
|
|
46
|
+
return accessors as ThemeAccessors<T>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const theme = Object.assign(setTheme, {
|
|
50
|
+
/** The app accent color. Kit components will read it via `var(--primaryColor, <fallback>)`. */
|
|
51
|
+
primaryColor: "var(--primaryColor)",
|
|
52
|
+
/** Secondary / inactive content color for kit components. */
|
|
53
|
+
mutedColor: "var(--mutedColor)",
|
|
54
|
+
/** DEFAULT text color — text without an explicit `color` follows it (hosts source their
|
|
55
|
+
* default from the theme; unset = the host default, white on dark screens). Set it once for
|
|
56
|
+
* a light theme instead of `color: "black"` on every label. */
|
|
57
|
+
color: "var(--color)",
|
|
58
|
+
/** DEFAULT text font — text without an explicit `fontFamily` follows it. */
|
|
59
|
+
fontFamily: "var(--fontFamily)",
|
|
60
|
+
/** Raw comfort knobs (logical px). The bare `comfort-top` style TOKEN applies the safe-area
|
|
61
|
+
* formula — these accessors read the knob value itself, for manual composition. */
|
|
62
|
+
"comfort-top": "var(--comfort-top)",
|
|
63
|
+
"comfort-bottom": "var(--comfort-bottom)",
|
|
64
|
+
"comfort-left": "var(--comfort-left)",
|
|
65
|
+
"comfort-right": "var(--comfort-right)",
|
|
66
|
+
} as const)
|