lecodes-sdk 0.19.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/README.md +114 -0
- package/dist/global.d.ts +503 -0
- package/dist/host.d.ts +61 -0
- package/dist/inject.js +4730 -0
- package/dist/types/animate/animate.d.ts +20 -0
- package/dist/types/animate/bezier.d.ts +2 -0
- package/dist/types/animate/easings.d.ts +21 -0
- package/dist/types/canvas/Canvas.d.ts +168 -0
- package/dist/types/core/Aspect.d.ts +171 -0
- package/dist/types/core/InspectorUI.d.ts +88 -0
- package/dist/types/core/StateMachine.d.ts +81 -0
- package/dist/types/core/color.d.ts +13 -0
- package/dist/types/core/compWrite.d.ts +11 -0
- package/dist/types/core/events.d.ts +11 -0
- package/dist/types/core/fields.d.ts +58 -0
- package/dist/types/core/registry.d.ts +7 -0
- package/dist/types/core/signals.d.ts +29 -0
- package/dist/types/core/time.d.ts +31 -0
- package/dist/types/g2/Camera2D.d.ts +19 -0
- package/dist/types/g2/CharacterController2D.d.ts +134 -0
- package/dist/types/g2/Node2D.d.ts +91 -0
- package/dist/types/g2/OneWay2D.d.ts +24 -0
- package/dist/types/g2/Physics2D.d.ts +147 -0
- package/dist/types/g2/Scene2D.d.ts +72 -0
- package/dist/types/g2/Shape2D.d.ts +58 -0
- package/dist/types/g2/Sprite.d.ts +51 -0
- package/dist/types/g2/SpriteAnimation.d.ts +50 -0
- package/dist/types/g2/SpriteSheet.d.ts +69 -0
- package/dist/types/g2/Texture2D.d.ts +16 -0
- package/dist/types/g2/Tilemap.d.ts +20 -0
- package/dist/types/g2/Tileset.d.ts +32 -0
- package/dist/types/g2/Trigger2D.d.ts +20 -0
- package/dist/types/g2/autotile.d.ts +133 -0
- package/dist/types/g2/cells.d.ts +14 -0
- package/dist/types/g2/defineScene2d.d.ts +141 -0
- package/dist/types/g2/groups2d.d.ts +16 -0
- package/dist/types/g2/loop.d.ts +4 -0
- package/dist/types/g2/scenarios2d.d.ts +30 -0
- package/dist/types/g2/touch.d.ts +2 -0
- package/dist/types/gl/Camera.d.ts +79 -0
- package/dist/types/gl/CameraPlace.d.ts +26 -0
- package/dist/types/gl/CharacterController.d.ts +98 -0
- package/dist/types/gl/Gearbox.d.ts +86 -0
- package/dist/types/gl/Geometry.d.ts +39 -0
- package/dist/types/gl/IK.d.ts +53 -0
- package/dist/types/gl/InstancedMesh.d.ts +46 -0
- package/dist/types/gl/Light.d.ts +73 -0
- package/dist/types/gl/Lightmap.d.ts +85 -0
- package/dist/types/gl/Locomotion.d.ts +279 -0
- package/dist/types/gl/Material.d.ts +100 -0
- package/dist/types/gl/Mesh.d.ts +33 -0
- package/dist/types/gl/Model.d.ts +53 -0
- package/dist/types/gl/NavAgent.d.ts +99 -0
- package/dist/types/gl/NavMesh.d.ts +169 -0
- package/dist/types/gl/Node.d.ts +99 -0
- package/dist/types/gl/Noise.d.ts +20 -0
- package/dist/types/gl/Particles.d.ts +277 -0
- package/dist/types/gl/Physics.d.ts +91 -0
- package/dist/types/gl/Plane.d.ts +19 -0
- package/dist/types/gl/Ragdoll.d.ts +86 -0
- package/dist/types/gl/Ray.d.ts +8 -0
- package/dist/types/gl/Scene.d.ts +221 -0
- package/dist/types/gl/Shape.d.ts +122 -0
- package/dist/types/gl/Terrain.d.ts +280 -0
- package/dist/types/gl/Texture.d.ts +27 -0
- package/dist/types/gl/Trigger.d.ts +10 -0
- package/dist/types/gl/Vehicle.d.ts +191 -0
- package/dist/types/gl/Wheel.d.ts +95 -0
- package/dist/types/gl/animation/AnimationClip.d.ts +60 -0
- package/dist/types/gl/animation/Animator.d.ts +219 -0
- package/dist/types/gl/animation/Layer.d.ts +31 -0
- package/dist/types/gl/animation/Loop.d.ts +17 -0
- package/dist/types/gl/animation/Playback.d.ts +36 -0
- package/dist/types/gl/animation/core.d.ts +367 -0
- package/dist/types/gl/controls.d.ts +19 -0
- package/dist/types/gl/physicsEvents.d.ts +1 -0
- package/dist/types/gl/scenarios.d.ts +98 -0
- package/dist/types/gl/state.d.ts +5 -0
- package/dist/types/gl/terrainMesh.d.ts +46 -0
- package/dist/types/gl/touch.d.ts +2 -0
- package/dist/types/inject.d.ts +123 -0
- package/dist/types/math/Mathf.d.ts +39 -0
- package/dist/types/math/mat4.d.ts +66 -0
- package/dist/types/math/quat.d.ts +53 -0
- package/dist/types/math/vec.d.ts +129 -0
- package/dist/types/net/codec.d.ts +25 -0
- package/dist/types/net/core.d.ts +106 -0
- package/dist/types/net/index.d.ts +80 -0
- package/dist/types/net/replication.d.ts +101 -0
- package/dist/types/plugins/camera.d.ts +25 -0
- package/dist/types/plugins/geolocation.d.ts +39 -0
- package/dist/types/plugins/oauth.d.ts +25 -0
- package/dist/types/plugins/permission.d.ts +1 -0
- package/dist/types/plugins/push.d.ts +46 -0
- package/dist/types/plugins/qr.d.ts +21 -0
- package/dist/types/plugins/service.d.ts +33 -0
- package/dist/types/runtime/app.d.ts +67 -0
- package/dist/types/runtime/appEvents.d.ts +7 -0
- package/dist/types/runtime/channel.d.ts +1 -0
- package/dist/types/runtime/clipboard.d.ts +7 -0
- package/dist/types/runtime/datetime.d.ts +80 -0
- package/dist/types/runtime/device.d.ts +126 -0
- package/dist/types/runtime/fetch.d.ts +31 -0
- package/dist/types/runtime/files.d.ts +33 -0
- package/dist/types/runtime/input.d.ts +121 -0
- package/dist/types/runtime/media.d.ts +46 -0
- package/dist/types/runtime/misc.d.ts +11 -0
- package/dist/types/runtime/net.d.ts +15 -0
- package/dist/types/runtime/rpc.d.ts +44 -0
- package/dist/types/runtime/service.d.ts +1 -0
- package/dist/types/runtime/share.d.ts +2 -0
- package/dist/types/runtime/storage.d.ts +5 -0
- package/dist/types/runtime/touch.d.ts +56 -0
- package/dist/types/scene/defineScene.d.ts +296 -0
- package/dist/types/scene/editorPlugins.d.ts +82 -0
- package/dist/types/scene/gizmos.d.ts +56 -0
- package/dist/types/scene/grammar.d.ts +56 -0
- package/dist/types/scene/level.d.ts +98 -0
- package/dist/types/scene/material.d.ts +76 -0
- package/dist/types/server/auth/appConfig.d.ts +16 -0
- package/dist/types/server/auth/global.d.ts +56 -0
- package/dist/types/server/auth/models.d.ts +164 -0
- package/dist/types/server/auth/types.d.ts +56 -0
- package/dist/types/server/channel.d.ts +28 -0
- package/dist/types/server/context.d.ts +27 -0
- package/dist/types/server/db/defineDb.d.ts +36 -0
- package/dist/types/server/db/fields.d.ts +146 -0
- package/dist/types/server/db/index.d.ts +6 -0
- package/dist/types/server/db/marci/query.d.ts +326 -0
- package/dist/types/server/db/types.d.ts +163 -0
- package/dist/types/server/errors.d.ts +8 -0
- package/dist/types/server/inject.d.ts +5 -0
- package/dist/types/ui/NativeView.d.ts +38 -0
- package/dist/types/ui/UI.d.ts +27 -0
- package/dist/types/ui/UIBottomSheet.d.ts +71 -0
- package/dist/types/ui/UIButton.d.ts +30 -0
- package/dist/types/ui/UIContainer.d.ts +33 -0
- package/dist/types/ui/UIImage.d.ts +35 -0
- package/dist/types/ui/UIInput.d.ts +110 -0
- package/dist/types/ui/UIModal.d.ts +70 -0
- package/dist/types/ui/UINode.d.ts +363 -0
- package/dist/types/ui/UIPager.d.ts +123 -0
- package/dist/types/ui/UIPopover.d.ts +39 -0
- package/dist/types/ui/UIScreen.d.ts +57 -0
- package/dist/types/ui/UIScrollable.d.ts +40 -0
- package/dist/types/ui/UISpacer.d.ts +9 -0
- package/dist/types/ui/UITabs.d.ts +96 -0
- package/dist/types/ui/UIText.d.ts +19 -0
- package/dist/types/ui/UIVideo.d.ts +28 -0
- package/dist/types/ui/UIVirtualizedList.d.ts +97 -0
- package/dist/types/ui/UIWidget.d.ts +65 -0
- package/dist/types/ui/fonts.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +68 -0
- package/dist/types/ui/router.d.ts +40 -0
- package/dist/types/ui/theme.d.ts +26 -0
- package/dist/types.json +1 -0
- package/package.json +46 -0
- package/prompts/2d.md +395 -0
- package/prompts/3d-scene-files.md +113 -0
- package/prompts/3d-scene.md +119 -0
- package/prompts/3d.md +303 -0
- package/prompts/README.md +142 -0
- package/prompts/ar.md +109 -0
- package/prompts/canvas.md +27 -0
- package/prompts/compose.ts +46 -0
- package/prompts/concept.md +49 -0
- package/prompts/core-design.md +75 -0
- package/prompts/core.md +264 -0
- package/prompts/design.md +218 -0
- package/prompts/dist/2d-game.md +1530 -0
- package/prompts/dist/3d-app.md +1672 -0
- package/prompts/dist/ar-app.md +1548 -0
- package/prompts/dist/concept.md +49 -0
- package/prompts/dist/design.md +498 -0
- package/prompts/dist/ui-app.md +1134 -0
- package/prompts/index.md +37 -0
- package/prompts/intake-prompt.md +33 -0
- package/prompts/namer-prompt.md +12 -0
- package/prompts/router-prompt.md +50 -0
- package/prompts/select.ts +111 -0
- package/prompts/ui-design.md +203 -0
- package/prompts/ui.md +803 -0
- package/src/animate/animate.ts +238 -0
- package/src/animate/bezier.ts +138 -0
- package/src/animate/easings.ts +126 -0
- package/src/bridges.d.ts +1345 -0
- package/src/canvas/Canvas.ts +305 -0
- package/src/compile/__tests__/assetIconMacro.test.ts +219 -0
- package/src/compile/__tests__/assetMacro.test.ts +74 -0
- package/src/compile/__tests__/assetName.test.ts +55 -0
- package/src/compile/__tests__/compile.test.ts +287 -0
- package/src/compile/__tests__/detectEntry.test.ts +132 -0
- package/src/compile/__tests__/fontMacro.test.ts +199 -0
- package/src/compile/aspectMacro.ts +86 -0
- package/src/compile/assetIconMacro.ts +384 -0
- package/src/compile/assetMacro.ts +146 -0
- package/src/compile/assetName.ts +50 -0
- package/src/compile/bundler.ts +287 -0
- package/src/compile/compileProject.ts +127 -0
- package/src/compile/detectEntry.ts +128 -0
- package/src/compile/fontMacro.ts +459 -0
- package/src/compile/fontRegistry.ts +78 -0
- package/src/compile/header.ts +67 -0
- package/src/compile/index.ts +100 -0
- package/src/compile/libraryImports.ts +52 -0
- package/src/compile/liteMaterial.ts +247 -0
- package/src/compile/sceneEditor.ts +88 -0
- package/src/compile/serverSplit.ts +233 -0
- package/src/compile/serverTypes.ts +227 -0
- package/src/compile/sfnt.ts +98 -0
- package/src/compile/shaderSchema.ts +202 -0
- package/src/compile/shaderTargets.ts +77 -0
- package/src/compile/sourcemap.ts +25 -0
- package/src/core/Aspect.ts +568 -0
- package/src/core/InspectorUI.ts +212 -0
- package/src/core/StateMachine.ts +308 -0
- package/src/core/__tests__/stateMachine.test.ts +132 -0
- package/src/core/color.ts +66 -0
- package/src/core/compWrite.ts +42 -0
- package/src/core/events.ts +38 -0
- package/src/core/fields.ts +120 -0
- package/src/core/registry.ts +23 -0
- package/src/core/signals.ts +277 -0
- package/src/core/time.ts +84 -0
- package/src/g2/Camera2D.ts +40 -0
- package/src/g2/CharacterController2D.ts +276 -0
- package/src/g2/Node2D.ts +267 -0
- package/src/g2/OneWay2D.ts +66 -0
- package/src/g2/Physics2D.ts +346 -0
- package/src/g2/Scene2D.ts +209 -0
- package/src/g2/Shape2D.ts +259 -0
- package/src/g2/Sprite.ts +89 -0
- package/src/g2/SpriteAnimation.ts +171 -0
- package/src/g2/SpriteSheet.ts +166 -0
- package/src/g2/Texture2D.ts +47 -0
- package/src/g2/Tilemap.ts +41 -0
- package/src/g2/Tileset.ts +71 -0
- package/src/g2/Trigger2D.ts +77 -0
- package/src/g2/autotile.ts +433 -0
- package/src/g2/cells.ts +91 -0
- package/src/g2/defineScene2d.ts +381 -0
- package/src/g2/groups2d.ts +106 -0
- package/src/g2/loop.ts +50 -0
- package/src/g2/scenarios2d.ts +69 -0
- package/src/g2/touch.ts +83 -0
- package/src/gl/Camera.ts +160 -0
- package/src/gl/CameraPlace.ts +52 -0
- package/src/gl/CharacterController.ts +238 -0
- package/src/gl/Gearbox.ts +212 -0
- package/src/gl/Geometry.ts +279 -0
- package/src/gl/IK.ts +193 -0
- package/src/gl/InstancedMesh.ts +132 -0
- package/src/gl/Light.ts +135 -0
- package/src/gl/Lightmap.ts +402 -0
- package/src/gl/Locomotion.ts +498 -0
- package/src/gl/Material.ts +279 -0
- package/src/gl/Mesh.ts +83 -0
- package/src/gl/Model.ts +124 -0
- package/src/gl/NavAgent.ts +337 -0
- package/src/gl/NavMesh.ts +397 -0
- package/src/gl/Node.ts +350 -0
- package/src/gl/Noise.ts +30 -0
- package/src/gl/Particles.ts +676 -0
- package/src/gl/Physics.ts +222 -0
- package/src/gl/Plane.ts +53 -0
- package/src/gl/Ragdoll.ts +270 -0
- package/src/gl/Ray.ts +16 -0
- package/src/gl/Scene.ts +505 -0
- package/src/gl/Shape.ts +409 -0
- package/src/gl/Terrain.ts +1092 -0
- package/src/gl/Texture.ts +63 -0
- package/src/gl/Trigger.ts +45 -0
- package/src/gl/Vehicle.ts +473 -0
- package/src/gl/Wheel.ts +240 -0
- package/src/gl/animation/AnimationClip.ts +204 -0
- package/src/gl/animation/Animator.ts +329 -0
- package/src/gl/animation/Layer.ts +39 -0
- package/src/gl/animation/Loop.ts +31 -0
- package/src/gl/animation/Playback.ts +52 -0
- package/src/gl/animation/core.ts +670 -0
- package/src/gl/controls.ts +95 -0
- package/src/gl/physicsEvents.ts +20 -0
- package/src/gl/scenarios.ts +291 -0
- package/src/gl/state.ts +6 -0
- package/src/gl/terrainMesh.ts +219 -0
- package/src/gl/touch.ts +68 -0
- package/src/host.d.ts +61 -0
- package/src/inject.ts +203 -0
- package/src/math/Mathf.ts +118 -0
- package/src/math/mat4.ts +278 -0
- package/src/math/quat.ts +232 -0
- package/src/math/vec.ts +255 -0
- package/src/net/codec.ts +119 -0
- package/src/net/core.ts +384 -0
- package/src/net/index.ts +181 -0
- package/src/net/replication.ts +622 -0
- package/src/plugins/camera.ts +81 -0
- package/src/plugins/geolocation.ts +123 -0
- package/src/plugins/oauth.ts +61 -0
- package/src/plugins/permission.ts +7 -0
- package/src/plugins/push.ts +132 -0
- package/src/plugins/qr.ts +73 -0
- package/src/plugins/service.ts +47 -0
- package/src/runtime/app.ts +101 -0
- package/src/runtime/appEvents.ts +54 -0
- package/src/runtime/channel.ts +61 -0
- package/src/runtime/clipboard.ts +20 -0
- package/src/runtime/datetime.ts +329 -0
- package/src/runtime/device.ts +293 -0
- package/src/runtime/fetch.ts +77 -0
- package/src/runtime/files.ts +108 -0
- package/src/runtime/input.ts +175 -0
- package/src/runtime/media.ts +111 -0
- package/src/runtime/misc.ts +16 -0
- package/src/runtime/net.ts +36 -0
- package/src/runtime/rpc.ts +218 -0
- package/src/runtime/service.ts +83 -0
- package/src/runtime/share.ts +9 -0
- package/src/runtime/storage.ts +13 -0
- package/src/runtime/touch.ts +76 -0
- package/src/scene/defineScene.ts +1396 -0
- package/src/scene/editorPlugins.ts +110 -0
- package/src/scene/gizmos.ts +148 -0
- package/src/scene/grammar.ts +120 -0
- package/src/scene/level.ts +296 -0
- package/src/scene/material.ts +188 -0
- package/src/server/auth/appConfig.ts +12 -0
- package/src/server/auth/global.ts +80 -0
- package/src/server/auth/host.ts +318 -0
- package/src/server/auth/models.ts +83 -0
- package/src/server/auth/types.ts +50 -0
- package/src/server/channel.ts +56 -0
- package/src/server/context.ts +36 -0
- package/src/server/db/defineDb.ts +237 -0
- package/src/server/db/fields.ts +132 -0
- package/src/server/db/httpTransport.ts +93 -0
- package/src/server/db/index.ts +7 -0
- package/src/server/db/marci/query.ts +412 -0
- package/src/server/db/types.ts +202 -0
- package/src/server/errors.ts +12 -0
- package/src/server/host.ts +74 -0
- package/src/server/inject.ts +13 -0
- package/src/server/runtime.ts +133 -0
- package/src/server/validate.ts +87 -0
- package/src/ui/NativeView.ts +142 -0
- package/src/ui/UI.ts +39 -0
- package/src/ui/UIBottomSheet.ts +139 -0
- package/src/ui/UIButton.ts +101 -0
- package/src/ui/UIContainer.ts +60 -0
- package/src/ui/UIImage.ts +83 -0
- package/src/ui/UIInput.ts +185 -0
- package/src/ui/UIModal.ts +139 -0
- package/src/ui/UINode.ts +830 -0
- package/src/ui/UIPager.ts +362 -0
- package/src/ui/UIPopover.ts +100 -0
- package/src/ui/UIScreen.ts +123 -0
- package/src/ui/UIScrollable.ts +87 -0
- package/src/ui/UISpacer.ts +14 -0
- package/src/ui/UITabs.ts +236 -0
- package/src/ui/UIText.ts +51 -0
- package/src/ui/UIVideo.ts +88 -0
- package/src/ui/UIVirtualizedList.ts +241 -0
- package/src/ui/UIWidget.ts +127 -0
- package/src/ui/fonts.ts +13 -0
- package/src/ui/presentable.ts +117 -0
- package/src/ui/router.ts +132 -0
- package/src/ui/theme.ts +84 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// The shared app-event dispatcher. The host exposes ONE registerAppEvent slot per JS world
|
|
2
|
+
// (docs/app-lifecycle-plan.md), so every SDK consumer (the `app` global's pause/resume/url, the
|
|
3
|
+
// `device` global's online/offline) subscribes here and this module registers with the host once,
|
|
4
|
+
// lazily on the first subscription — no top-level side effect, tree-shaking drops it when unused.
|
|
5
|
+
|
|
6
|
+
type AppEventCallback = (data?: string) => void
|
|
7
|
+
|
|
8
|
+
const listeners: Record<string, AppEventCallback[]> = {}
|
|
9
|
+
let registered = false
|
|
10
|
+
|
|
11
|
+
const dispatch = (event: string, data?: string): void => {
|
|
12
|
+
const arr = listeners[event]
|
|
13
|
+
if (arr) for (const cb of arr.slice()) cb(data)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const appEventsOn = (event: string, cb: AppEventCallback): void => {
|
|
17
|
+
;(listeners[event] ??= []).push(cb)
|
|
18
|
+
if (!registered) {
|
|
19
|
+
registered = true
|
|
20
|
+
_creatorUtils.registerAppEvent?.(dispatch)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const appEventsOff = (event: string, cb: AppEventCallback): void => {
|
|
25
|
+
const arr = listeners[event]
|
|
26
|
+
if (!arr) return
|
|
27
|
+
const i = arr.indexOf(cb)
|
|
28
|
+
if (i >= 0) arr.splice(i, 1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// The keyboard channel is a separate host slot (registerKeyboardEvent) — the registerAppEvent
|
|
32
|
+
// string channel can't carry the (height, duration) number pair. Same lazy single-slot pattern.
|
|
33
|
+
|
|
34
|
+
type KeyboardCallback = (height: number, duration: number) => void
|
|
35
|
+
|
|
36
|
+
const keyboardListeners: KeyboardCallback[] = []
|
|
37
|
+
let keyboardRegistered = false
|
|
38
|
+
|
|
39
|
+
const dispatchKeyboard = (height: number, duration: number): void => {
|
|
40
|
+
for (const cb of keyboardListeners.slice()) cb(height, duration)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const keyboardEventsOn = (cb: KeyboardCallback): void => {
|
|
44
|
+
keyboardListeners.push(cb)
|
|
45
|
+
if (!keyboardRegistered) {
|
|
46
|
+
keyboardRegistered = true
|
|
47
|
+
_creatorUtils.registerKeyboardEvent?.(dispatchKeyboard)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const keyboardEventsOff = (cb: KeyboardCallback): void => {
|
|
52
|
+
const i = keyboardListeners.indexOf(cb)
|
|
53
|
+
if (i >= 0) keyboardListeners.splice(i, 1)
|
|
54
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The JSON wire protocol shared by the two host plugin channels — NativeView
|
|
2
|
+
// (`_creatorUI.viewCall`, see ui/NativeView.ts) and headless services
|
|
3
|
+
// (`_creatorUtils.serviceCall`, see runtime/service.ts). One protocol, two transports:
|
|
4
|
+
// `args`, results, and event payloads travel as JSON strings, and an absent/empty string
|
|
5
|
+
// decodes to `undefined`. Typed plugins (QRScanner, CameraView, Geolocation) layer methods
|
|
6
|
+
// over this — see src/plugins/.
|
|
7
|
+
|
|
8
|
+
/** @internal event-name → listeners. NativeView exposes it as `nvl` (host contract). */
|
|
9
|
+
export type ChannelListeners = Record<string, ((data?: any) => void)[]>
|
|
10
|
+
|
|
11
|
+
/** @internal */
|
|
12
|
+
export const _channelOn = (listeners: ChannelListeners, event: string, callback: (data?: any) => void): void => {
|
|
13
|
+
(listeners[event] ??= []).push(callback)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const _channelOff = (listeners: ChannelListeners, event: string, callback: (data?: any) => void): void => {
|
|
18
|
+
const list = listeners[event]
|
|
19
|
+
if (!list) return
|
|
20
|
+
const i = list.indexOf(callback)
|
|
21
|
+
if (i >= 0) list.splice(i, 1)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @internal deliver a host event: decode the JSON payload, fan out (copy-before-iterate). */
|
|
25
|
+
export const _channelEmit = (listeners: ChannelListeners, event: string, dataJson?: string): void => {
|
|
26
|
+
const list = listeners[event]
|
|
27
|
+
if (!list || list.length === 0) return
|
|
28
|
+
const data = dataJson === undefined || dataJson === "" ? undefined : JSON.parse(dataJson)
|
|
29
|
+
for (const cb of [...list]) cb(data)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ChannelCallBridge = (
|
|
33
|
+
id: number, method: string, args: string,
|
|
34
|
+
onComplete: (result?: string) => void, onError: (err: string) => void,
|
|
35
|
+
) => void
|
|
36
|
+
|
|
37
|
+
/** @internal one JSON call over a channel bridge.
|
|
38
|
+
*
|
|
39
|
+
* Takes the HANDLER OBJECT plus the member name — never a plucked-off function reference. On
|
|
40
|
+
* Apple hosts the bridge members are JSExport-ed Objective-C/Swift methods, and JavaScriptCore
|
|
41
|
+
* binds those to their native receiver: invoking a detached `handler.viewCall(…)` with no `this`
|
|
42
|
+
* fails at runtime with "self type check failed for Objective-C instance method". QuickJS and web
|
|
43
|
+
* hosts are plain functions that ignore `this`, so a detached call works there and the breakage
|
|
44
|
+
* looks iOS/macOS-only. Calling through `owner` keeps the receiver attached on every host.
|
|
45
|
+
*
|
|
46
|
+
* `bridgeName` may be absent from the host object (optional ABI member) — that's the
|
|
47
|
+
* unsupported-host path, not an error. */
|
|
48
|
+
export const _channelCall = (
|
|
49
|
+
owner: Record<string, any> | undefined, bridgeName: string, unsupportedError: string,
|
|
50
|
+
id: number, method: string, args: any[],
|
|
51
|
+
): Promise<any> =>
|
|
52
|
+
new Promise((res, rej) => {
|
|
53
|
+
const bridge = owner?.[bridgeName] as ChannelCallBridge | undefined
|
|
54
|
+
if (typeof bridge !== "function") {
|
|
55
|
+
rej(new Error(unsupportedError))
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
bridge.call(owner, id, method, JSON.stringify(args),
|
|
59
|
+
(result?: string) => res(result === undefined || result === "" ? undefined : JSON.parse(result)),
|
|
60
|
+
(err: string) => rej(new Error(err)))
|
|
61
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// System clipboard (docs/app-lifecycle-plan.md). Write is fire-and-forget; read is async because
|
|
2
|
+
// web gates it behind a permission prompt (native hosts complete synchronously).
|
|
3
|
+
|
|
4
|
+
export const clipboard = {
|
|
5
|
+
/** Put `text` on the system clipboard. Silent no-op on hosts without clipboard access. */
|
|
6
|
+
write(text: string): void {
|
|
7
|
+
_creatorUtils.clipboardWrite?.(text)
|
|
8
|
+
},
|
|
9
|
+
/** Read text from the system clipboard. Rejects when the platform has no clipboard access, the
|
|
10
|
+
* user denied it, or there is nothing readable. */
|
|
11
|
+
read(): Promise<string> {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
if (typeof _creatorUtils.clipboardRead !== "function") {
|
|
14
|
+
reject(new Error("Clipboard is not supported on this platform"))
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
_creatorUtils.clipboardRead(resolve, (err: string) => reject(new Error(err)))
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// Locale-aware dates, dayjs-style: `date(value).format(...)`. Pure JS (no `Intl` — QuickJS hosts
|
|
2
|
+
// don't ship it reliably), so output is byte-identical on every platform. Two locales: English and
|
|
3
|
+
// Russian.
|
|
4
|
+
//
|
|
5
|
+
// ── Designed for chisel fusion ──────────────────────────────────────────────────────────────────
|
|
6
|
+
// A date is a *single-scalar* value (epoch milliseconds), which is the 1-component analog of a Vec3
|
|
7
|
+
// chain. The wrapper is written so a future chisel date-fuser can lower `date(root).<chain>()` to
|
|
8
|
+
// scalar ms arithmetic and drop the wrapper allocation, the same way Vec3 chains fuse:
|
|
9
|
+
// • IMMUTABLE — every method returns a NEW `DateValue`, never mutates `t` (aliasing safety).
|
|
10
|
+
// • ONE canonical scalar — `t` (epoch ms), also reachable via `valueOf()`; that's the "component".
|
|
11
|
+
// • Terminals DELEGATE to the module-private free functions `formatImpl` / `timeAgoImpl`, taking
|
|
12
|
+
// `(ms, …)`. So `date(x).format(p)` can fuse to `formatImpl(toMs(x), p)` with no wrapper.
|
|
13
|
+
// • Linear-unit arithmetic (ms…week) is closed-form scalar math → fusable; calendar units
|
|
14
|
+
// (month/year) and local-tz `startOf`/`endOf` use `Date` and are left to the real method.
|
|
15
|
+
// Even before that pass lands, each method is a class method, so chisel's method-granular DCE drops
|
|
16
|
+
// whichever ones a project doesn't use.
|
|
17
|
+
|
|
18
|
+
export type Locale = "en" | "ru"
|
|
19
|
+
export type DateInput = Date | number | string | DateValue
|
|
20
|
+
export type Unit = "ms" | "second" | "minute" | "hour" | "day" | "week" | "month" | "year"
|
|
21
|
+
|
|
22
|
+
// ---- locale data -------------------------------------------------------------------------------
|
|
23
|
+
// Russian needs the month in two grammatical cases: genitive ("5 сентября") when a day number is
|
|
24
|
+
// present, nominative ("Сентябрь 2024") when it stands alone. `format` picks by whether the pattern
|
|
25
|
+
// contains a day (`D`) token — the same rule day.js's ru locale uses.
|
|
26
|
+
|
|
27
|
+
type LocaleData = {
|
|
28
|
+
months: readonly string[]
|
|
29
|
+
monthsGenitive: readonly string[]
|
|
30
|
+
monthsShort: readonly string[]
|
|
31
|
+
weekdays: readonly string[] // Sunday..Saturday (JS getDay() order)
|
|
32
|
+
weekdaysShort: readonly string[]
|
|
33
|
+
am: string
|
|
34
|
+
pm: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const EN: LocaleData = {
|
|
38
|
+
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
|
39
|
+
monthsGenitive: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
|
40
|
+
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
|
41
|
+
weekdays: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
|
42
|
+
weekdaysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
43
|
+
am: "AM",
|
|
44
|
+
pm: "PM",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const RU: LocaleData = {
|
|
48
|
+
months: ["Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"],
|
|
49
|
+
monthsGenitive: ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"],
|
|
50
|
+
monthsShort: ["янв", "фев", "мар", "апр", "мая", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"],
|
|
51
|
+
weekdays: ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"],
|
|
52
|
+
weekdaysShort: ["вс", "пн", "вт", "ср", "чт", "пт", "сб"],
|
|
53
|
+
am: "AM",
|
|
54
|
+
pm: "PM",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const DATA: Record<Locale, LocaleData> = { en: EN, ru: RU }
|
|
58
|
+
|
|
59
|
+
const resolveLocale = (locale?: Locale): Locale => {
|
|
60
|
+
if (locale) return locale === "ru" ? "ru" : "en"
|
|
61
|
+
const lang = typeof _creatorUtils !== "undefined" ? _creatorUtils.language : ""
|
|
62
|
+
return lang && lang.toLowerCase().indexOf("ru") === 0 ? "ru" : "en"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Milliseconds per linear unit. Absent keys (month/year) mark the non-fusable calendar units.
|
|
66
|
+
const MS: Partial<Record<Unit, number>> = {
|
|
67
|
+
ms: 1,
|
|
68
|
+
second: 1000,
|
|
69
|
+
minute: 60_000,
|
|
70
|
+
hour: 3_600_000,
|
|
71
|
+
day: 86_400_000,
|
|
72
|
+
week: 604_800_000,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const toMs = (value: DateInput): number => {
|
|
76
|
+
if (value instanceof DateValue) return value.t
|
|
77
|
+
if (value instanceof Date) return value.getTime()
|
|
78
|
+
if (typeof value === "number") return value
|
|
79
|
+
return new Date(value).getTime()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---- formatting (the fusion escape target: takes a scalar ms, holds the heavy tables) -----------
|
|
83
|
+
|
|
84
|
+
const pad2 = (n: number): string => (n < 10 ? "0" + n : "" + n)
|
|
85
|
+
|
|
86
|
+
// Longest tokens first so `MMMM` matches before `MM`, `YYYY` before `YY`, etc.
|
|
87
|
+
const TOKEN = /YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g
|
|
88
|
+
|
|
89
|
+
const formatImpl = (ms: number, pattern: string, locale?: Locale): string => {
|
|
90
|
+
const d = new Date(ms)
|
|
91
|
+
const L = DATA[resolveLocale(locale)]
|
|
92
|
+
const hasDay = /D/.test(pattern) // genitive month only when a day number accompanies it
|
|
93
|
+
const months = hasDay ? L.monthsGenitive : L.months
|
|
94
|
+
|
|
95
|
+
const year = d.getFullYear()
|
|
96
|
+
const month = d.getMonth()
|
|
97
|
+
const day = d.getDate()
|
|
98
|
+
const weekday = d.getDay()
|
|
99
|
+
const hours = d.getHours()
|
|
100
|
+
const hour12 = hours % 12 || 12
|
|
101
|
+
const minutes = d.getMinutes()
|
|
102
|
+
const seconds = d.getSeconds()
|
|
103
|
+
|
|
104
|
+
return pattern.replace(TOKEN, (t) => {
|
|
105
|
+
switch (t) {
|
|
106
|
+
case "YYYY": return "" + year
|
|
107
|
+
case "YY": return pad2(year % 100)
|
|
108
|
+
case "MMMM": return months[month]
|
|
109
|
+
case "MMM": return L.monthsShort[month]
|
|
110
|
+
case "MM": return pad2(month + 1)
|
|
111
|
+
case "M": return "" + (month + 1)
|
|
112
|
+
case "DD": return pad2(day)
|
|
113
|
+
case "D": return "" + day
|
|
114
|
+
case "dddd": return L.weekdays[weekday]
|
|
115
|
+
case "ddd": return L.weekdaysShort[weekday]
|
|
116
|
+
case "HH": return pad2(hours)
|
|
117
|
+
case "H": return "" + hours
|
|
118
|
+
case "hh": return pad2(hour12)
|
|
119
|
+
case "h": return "" + hour12
|
|
120
|
+
case "mm": return pad2(minutes)
|
|
121
|
+
case "m": return "" + minutes
|
|
122
|
+
case "ss": return pad2(seconds)
|
|
123
|
+
case "s": return "" + seconds
|
|
124
|
+
case "A": return hours < 12 ? L.am : L.pm
|
|
125
|
+
case "a": return (hours < 12 ? L.am : L.pm).toLowerCase()
|
|
126
|
+
default: return t
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Russian plural picks one of three forms by the number: 1 минута / 2 минуты / 5 минут.
|
|
132
|
+
const ruPlural = (n: number, one: string, few: string, many: string): string => {
|
|
133
|
+
const mod10 = n % 10
|
|
134
|
+
const mod100 = n % 100
|
|
135
|
+
if (mod10 === 1 && mod100 !== 11) return one
|
|
136
|
+
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return few
|
|
137
|
+
return many
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
type RelUnit = { limit: number; secs: number; en: [string, string]; ru: [string, string, string] }
|
|
141
|
+
|
|
142
|
+
// Ordered thresholds. `limit` is the upper bound (in seconds) this unit covers; `secs` is the unit's
|
|
143
|
+
// length used to compute the count.
|
|
144
|
+
const REL_UNITS: readonly RelUnit[] = [
|
|
145
|
+
{ limit: 60, secs: 1, en: ["second", "seconds"], ru: ["секунду", "секунды", "секунд"] },
|
|
146
|
+
{ limit: 3600, secs: 60, en: ["minute", "minutes"], ru: ["минуту", "минуты", "минут"] },
|
|
147
|
+
{ limit: 86400, secs: 3600, en: ["hour", "hours"], ru: ["час", "часа", "часов"] },
|
|
148
|
+
{ limit: 2592000, secs: 86400, en: ["day", "days"], ru: ["день", "дня", "дней"] },
|
|
149
|
+
{ limit: 31536000, secs: 2592000, en: ["month", "months"], ru: ["месяц", "месяца", "месяцев"] },
|
|
150
|
+
{ limit: Infinity, secs: 31536000, en: ["year", "years"], ru: ["год", "года", "лет"] },
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
const timeAgoImpl = (ms: number, locale: Locale | undefined, nowMs: number): string => {
|
|
154
|
+
const loc = resolveLocale(locale)
|
|
155
|
+
const diffSecs = (nowMs - ms) / 1000
|
|
156
|
+
const future = diffSecs < 0
|
|
157
|
+
const abs = Math.abs(diffSecs)
|
|
158
|
+
|
|
159
|
+
if (abs < 45) return loc === "ru" ? "только что" : "just now"
|
|
160
|
+
|
|
161
|
+
let count = 1
|
|
162
|
+
let unit = REL_UNITS[REL_UNITS.length - 1]
|
|
163
|
+
for (const u of REL_UNITS) {
|
|
164
|
+
if (abs < u.limit) { unit = u; count = Math.max(1, Math.round(abs / u.secs)); break }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (loc === "ru") {
|
|
168
|
+
const word = ruPlural(count, unit.ru[0], unit.ru[1], unit.ru[2])
|
|
169
|
+
return future ? `через ${count} ${word}` : `${count} ${word} назад`
|
|
170
|
+
}
|
|
171
|
+
const word = count === 1 ? unit.en[0] : unit.en[1]
|
|
172
|
+
return future ? `in ${count} ${word}` : `${count} ${word} ago`
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ---- the immutable wrapper ---------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* An immutable moment in time — the value returned by [`date()`]. Wraps a single epoch-millisecond
|
|
179
|
+
* scalar (`t`); every method returns a new `DateValue` or a plain scalar, never mutating. Construct
|
|
180
|
+
* it with `date(...)`, not `new`.
|
|
181
|
+
*/
|
|
182
|
+
export class DateValue {
|
|
183
|
+
/** Epoch milliseconds — the one canonical scalar. Prefer `valueOf()`; this is public so `date(a)`
|
|
184
|
+
* can read it back cheaply and so tooling can treat it as the value's single component. */
|
|
185
|
+
readonly t: number
|
|
186
|
+
|
|
187
|
+
constructor(ms: number) {
|
|
188
|
+
this.t = ms
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// -- materialize (delegate to the free functions; the fuser can inline these to `*Impl(t, …)`) --
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Format against a `day.js`-style token pattern (default `"D MMMM YYYY"`). Locale defaults to the
|
|
195
|
+
* current device language.
|
|
196
|
+
*
|
|
197
|
+
* Tokens: `YYYY`/`YY` year · `MMMM`/`MMM`/`MM`/`M` month · `DD`/`D` day · `dddd`/`ddd` weekday ·
|
|
198
|
+
* `HH`/`H` 24-hour · `hh`/`h` 12-hour · `mm`/`m` minute · `ss`/`s` second · `A`/`a` AM/PM.
|
|
199
|
+
*
|
|
200
|
+
* ```ts
|
|
201
|
+
* date().format() // "10 July 2026" / "10 июля 2026"
|
|
202
|
+
* date(ts).format("DD.MM.YYYY HH:mm") // "10.07.2026 14:05"
|
|
203
|
+
* date(ts).format("MMMM YYYY", "ru") // "Июль 2026" (nominative — no day token)
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
format(pattern = "D MMMM YYYY", locale?: Locale): string {
|
|
207
|
+
return formatImpl(this.t, pattern, locale)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Human relative time vs `now` (default: the current time) — "5 minutes ago", "in 2 days",
|
|
212
|
+
* "5 минут назад", "через 2 дня". Reads as "just now" under ~45 seconds; handles Russian number
|
|
213
|
+
* agreement (`1 минуту` / `2 минуты` / `5 минут`). Locale defaults to the device language.
|
|
214
|
+
*/
|
|
215
|
+
timeAgo(locale?: Locale, now: DateInput = Date.now()): string {
|
|
216
|
+
return timeAgoImpl(this.t, locale, toMs(now))
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// -- arithmetic (pure, chainable; linear units are closed-form scalar math → fusable) -----------
|
|
220
|
+
|
|
221
|
+
/** Add `n` of a unit, returning a new value. Linear units (`ms`…`week`) are scalar ms math;
|
|
222
|
+
* `month`/`year` are calendar-aware (clamp on short months, like day.js). */
|
|
223
|
+
add(n: number, unit: Unit): DateValue {
|
|
224
|
+
const ms = MS[unit]
|
|
225
|
+
if (ms !== undefined) return new DateValue(this.t + n * ms)
|
|
226
|
+
// Calendar units: shift month/year but clamp the day so Jan 31 + 1 month → Feb 28 (not Mar 3).
|
|
227
|
+
const d = new Date(this.t)
|
|
228
|
+
const day = d.getDate()
|
|
229
|
+
d.setDate(1)
|
|
230
|
+
if (unit === "year") d.setFullYear(d.getFullYear() + n)
|
|
231
|
+
else d.setMonth(d.getMonth() + n) // "month"
|
|
232
|
+
const daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate()
|
|
233
|
+
d.setDate(Math.min(day, daysInMonth))
|
|
234
|
+
return new DateValue(d.getTime())
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Subtract `n` of a unit. Equivalent to `add(-n, unit)`. */
|
|
238
|
+
subtract(n: number, unit: Unit): DateValue {
|
|
239
|
+
return this.add(-n, unit)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Snap down to the start of a unit (local time): start of day/hour/month/year, etc. */
|
|
243
|
+
startOf(unit: Unit): DateValue {
|
|
244
|
+
const d = new Date(this.t)
|
|
245
|
+
switch (unit) {
|
|
246
|
+
case "year": d.setMonth(0); d.setDate(1); d.setHours(0, 0, 0, 0); break
|
|
247
|
+
case "month": d.setDate(1); d.setHours(0, 0, 0, 0); break
|
|
248
|
+
case "day": d.setHours(0, 0, 0, 0); break
|
|
249
|
+
case "hour": d.setMinutes(0, 0, 0); break
|
|
250
|
+
case "minute": d.setSeconds(0, 0); break
|
|
251
|
+
case "second": d.setMilliseconds(0); break
|
|
252
|
+
case "week": {
|
|
253
|
+
d.setHours(0, 0, 0, 0)
|
|
254
|
+
d.setDate(d.getDate() - d.getDay()) // week starts Sunday
|
|
255
|
+
break
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return new DateValue(d.getTime())
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Snap up to the end of a unit (local time): the last millisecond of the day/hour/month/… */
|
|
262
|
+
endOf(unit: Unit): DateValue {
|
|
263
|
+
return this.startOf(unit).add(1, unit).add(-1, "ms")
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// -- scalar terminals (bare number / boolean → allocate nothing when fused) ---------------------
|
|
267
|
+
|
|
268
|
+
/** Epoch milliseconds. Enables `+date(x)`, `date(a) < date(b)`, `date(a) - date(b)` via coercion. */
|
|
269
|
+
valueOf(): number {
|
|
270
|
+
return this.t
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** Whole seconds since the epoch (Unix time). */
|
|
274
|
+
unix(): number {
|
|
275
|
+
return Math.floor(this.t / 1000)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** Signed difference to `other`, in whole `unit`s (default `ms`), truncated toward zero. */
|
|
279
|
+
diff(other: DateInput, unit: Unit = "ms"): number {
|
|
280
|
+
const raw = this.t - toMs(other)
|
|
281
|
+
const per = MS[unit]
|
|
282
|
+
if (per !== undefined) return Math.trunc(raw / per)
|
|
283
|
+
// month/year: count calendar steps.
|
|
284
|
+
const a = new Date(this.t)
|
|
285
|
+
const b = new Date(toMs(other))
|
|
286
|
+
let months = (a.getFullYear() - b.getFullYear()) * 12 + (a.getMonth() - b.getMonth())
|
|
287
|
+
if (a.getDate() < b.getDate()) months -= Math.sign(months) || 0
|
|
288
|
+
return unit === "year" ? Math.trunc(months / 12) : months
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
isBefore(other: DateInput): boolean { return this.t < toMs(other) }
|
|
292
|
+
isAfter(other: DateInput): boolean { return this.t > toMs(other) }
|
|
293
|
+
isSame(other: DateInput): boolean { return this.t === toMs(other) }
|
|
294
|
+
|
|
295
|
+
// -- field accessors (scalar terminals) ---------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
year(): number { return new Date(this.t).getFullYear() }
|
|
298
|
+
/** 0-based month (0 = January), matching `Date.getMonth`. */
|
|
299
|
+
month(): number { return new Date(this.t).getMonth() }
|
|
300
|
+
/** Day of the month, 1–31. */
|
|
301
|
+
day(): number { return new Date(this.t).getDate() }
|
|
302
|
+
/** Day of the week, 0 (Sunday)–6 (Saturday), matching `Date.getDay`. */
|
|
303
|
+
weekday(): number { return new Date(this.t).getDay() }
|
|
304
|
+
hour(): number { return new Date(this.t).getHours() }
|
|
305
|
+
minute(): number { return new Date(this.t).getMinutes() }
|
|
306
|
+
second(): number { return new Date(this.t).getSeconds() }
|
|
307
|
+
|
|
308
|
+
/** A native `Date` snapshot of this value. */
|
|
309
|
+
toDate(): Date {
|
|
310
|
+
return new Date(this.t)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Create a `DateValue` — a locale-aware, immutable moment, dayjs-style. Accepts a `Date`, a
|
|
316
|
+
* millisecond timestamp (`Date.now()`), a parseable string, or another `DateValue`; with no argument
|
|
317
|
+
* it's the current time. Chain arithmetic and finish with `format` / `timeAgo`, both of which
|
|
318
|
+
* understand **English** and **Russian** and default to the current device language
|
|
319
|
+
* ([`device.language`](../runtime/device.ts)).
|
|
320
|
+
*
|
|
321
|
+
* ```ts
|
|
322
|
+
* date().format("dddd, D MMMM") // "Friday, 10 July" / "пятница, 10 июля"
|
|
323
|
+
* date(ts).timeAgo() // "5 minutes ago" / "5 минут назад"
|
|
324
|
+
* date().add(3, "day").startOf("day").format("D MMMM")
|
|
325
|
+
* date(a).diff(b, "hour") // signed whole hours
|
|
326
|
+
* date(a).isBefore(b) // boolean
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
export const date = (value: DateInput = Date.now()): DateValue => new DateValue(toMs(value))
|