From 6349859e6687829022a451fe1cdf7bfb1ca21cb8 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sun, 7 Jun 2026 14:40:15 +0300 Subject: [PATCH] working on cutscenes --- CUTSCENES.md | 351 +++ cutsceneEditor/.gitignore | 46 + cutsceneEditor/CUTSCENES.md | 351 +++ cutsceneEditor/cutscene_example.json | 65 + cutsceneEditor/dist/assets/index-B96C0g1n.css | 1 + cutsceneEditor/dist/assets/index-D_5Tak8P.js | 40 + cutsceneEditor/dist/index.html | 13 + cutsceneEditor/index.html | 12 + cutsceneEditor/package-lock.json | 1890 +++++++++++++++++ cutsceneEditor/package.json | 24 + cutsceneEditor/src/App.module.css | 6 + cutsceneEditor/src/App.tsx | 14 + .../CenterPanel/CenterPanel.module.css | 18 + .../components/CenterPanel/CenterPanel.tsx | 16 + .../components/Controls/Controls.module.css | 94 + .../src/components/Controls/Controls.tsx | 67 + .../components/LeftPanel/LeftPanel.module.css | 100 + .../src/components/LeftPanel/LeftPanel.tsx | 76 + .../src/components/Preview/Preview.module.css | 51 + .../src/components/Preview/Preview.tsx | 85 + .../RightPanel/CutsceneProperties.tsx | 72 + .../components/RightPanel/LayerProperties.tsx | 132 ++ .../RightPanel/RightPanel.module.css | 208 ++ .../src/components/RightPanel/RightPanel.tsx | 41 + .../components/RightPanel/SubtitleLines.tsx | 89 + .../components/Timeline/Timeline.module.css | 192 ++ .../src/components/Timeline/Timeline.tsx | 285 +++ cutsceneEditor/src/constants/easings.ts | 25 + cutsceneEditor/src/constants/images.ts | 12 + cutsceneEditor/src/hooks/usePlayback.ts | 52 + cutsceneEditor/src/index.css | 52 + cutsceneEditor/src/main.tsx | 10 + cutsceneEditor/src/store/cutsceneStore.ts | 242 +++ cutsceneEditor/src/types/cutscene.ts | 49 + cutsceneEditor/src/utils/fileIO.ts | 76 + cutsceneEditor/src/utils/rendering.ts | 79 + cutsceneEditor/src/vite-env.d.ts | 6 + cutsceneEditor/tsconfig.json | 20 + cutsceneEditor/vite.config.ts | 26 + resources/dialogue/cutscenes.json | 20 +- resources/dialogue/cutscenes001.json | 396 ++++ resources/dialogue/cutscenes002.json | 415 ++++ resources/dialogue/cutscenes003.json | 415 ++++ resources/start_uni_interior.lua | 4 +- src/Game.cpp | 2 +- src/Location.cpp | 3 +- src/cutscene/CutsceneOverlay.cpp | 12 +- 47 files changed, 6234 insertions(+), 21 deletions(-) create mode 100644 CUTSCENES.md create mode 100644 cutsceneEditor/.gitignore create mode 100644 cutsceneEditor/CUTSCENES.md create mode 100644 cutsceneEditor/cutscene_example.json create mode 100644 cutsceneEditor/dist/assets/index-B96C0g1n.css create mode 100644 cutsceneEditor/dist/assets/index-D_5Tak8P.js create mode 100644 cutsceneEditor/dist/index.html create mode 100644 cutsceneEditor/index.html create mode 100644 cutsceneEditor/package-lock.json create mode 100644 cutsceneEditor/package.json create mode 100644 cutsceneEditor/src/App.module.css create mode 100644 cutsceneEditor/src/App.tsx create mode 100644 cutsceneEditor/src/components/CenterPanel/CenterPanel.module.css create mode 100644 cutsceneEditor/src/components/CenterPanel/CenterPanel.tsx create mode 100644 cutsceneEditor/src/components/Controls/Controls.module.css create mode 100644 cutsceneEditor/src/components/Controls/Controls.tsx create mode 100644 cutsceneEditor/src/components/LeftPanel/LeftPanel.module.css create mode 100644 cutsceneEditor/src/components/LeftPanel/LeftPanel.tsx create mode 100644 cutsceneEditor/src/components/Preview/Preview.module.css create mode 100644 cutsceneEditor/src/components/Preview/Preview.tsx create mode 100644 cutsceneEditor/src/components/RightPanel/CutsceneProperties.tsx create mode 100644 cutsceneEditor/src/components/RightPanel/LayerProperties.tsx create mode 100644 cutsceneEditor/src/components/RightPanel/RightPanel.module.css create mode 100644 cutsceneEditor/src/components/RightPanel/RightPanel.tsx create mode 100644 cutsceneEditor/src/components/RightPanel/SubtitleLines.tsx create mode 100644 cutsceneEditor/src/components/Timeline/Timeline.module.css create mode 100644 cutsceneEditor/src/components/Timeline/Timeline.tsx create mode 100644 cutsceneEditor/src/constants/easings.ts create mode 100644 cutsceneEditor/src/constants/images.ts create mode 100644 cutsceneEditor/src/hooks/usePlayback.ts create mode 100644 cutsceneEditor/src/index.css create mode 100644 cutsceneEditor/src/main.tsx create mode 100644 cutsceneEditor/src/store/cutsceneStore.ts create mode 100644 cutsceneEditor/src/types/cutscene.ts create mode 100644 cutsceneEditor/src/utils/fileIO.ts create mode 100644 cutsceneEditor/src/utils/rendering.ts create mode 100644 cutsceneEditor/src/vite-env.d.ts create mode 100644 cutsceneEditor/tsconfig.json create mode 100644 cutsceneEditor/vite.config.ts create mode 100644 resources/dialogue/cutscenes001.json create mode 100644 resources/dialogue/cutscenes002.json create mode 100644 resources/dialogue/cutscenes003.json diff --git a/CUTSCENES.md b/CUTSCENES.md new file mode 100644 index 0000000..0619303 --- /dev/null +++ b/CUTSCENES.md @@ -0,0 +1,351 @@ +# Cutscene System + +Cutscenes are defined in JSON and loaded by `CutsceneDatabase`. Each cutscene is a self-contained object with an array of animated image layers and optional subtitle lines. + +The file can contain multiple cutscenes: + +```json +{ + "cutscenes": [ + { "id": "intro", ... }, + { "id": "ending", ... } + ] +} +``` + +Cutscenes and dialogues are loaded from **separate files**: + +```cpp +dialogueSystem.loadDatabase("resources/dialogue/uni_interior.json"); // dialogues +dialogueSystem.loadCutsceneDatabase("resources/dialogue/cutscenes.json"); // cutscenes +``` + +--- + +## Cutscene object + +```json +{ + "id": "intro_cutscene", + "skippable": true, + "durationMs": 8000, + "fadeOutMs": 500, + "fadeInMs": 500, + "endFadeOutMs": 500, + "endFadeInMs": 500, + "onFadeInCallback": "", + "imageSegments": [ ... ], + "lines": [ ... ] +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `id` | string | — | Unique identifier used to start the cutscene from C++ or dialogue (**required**) | +| `skippable` | bool | `true` | Whether the player can skip by holding LMB / touch | +| `durationMs` | int | `0` | Minimum content duration in ms. The cutscene will not end before this time even if all subtitle lines have finished. `0` means duration is determined solely by subtitle lines or `imageSegments.endMs` | +| `fadeOutMs` | int | `0` | Duration of the **opening fade** — game world fades to black before the cutscene images appear | +| `fadeInMs` | int | `0` | Duration of the **opening reveal** — cutscene images fade in from black after `fadeOutMs` | +| `endFadeOutMs` | int | `0` | Duration of the **closing fade** — cutscene fades to black at the end of content | +| `endFadeInMs` | int | `0` | Duration of the **closing reveal** — game world fades back in from black | +| `onFadeInCallback` | string | `""` | Lua function name called once the opening fade-in completes (fired after `fadeOutMs + fadeInMs` ms) | +| `imageSegments` | array | `[]` | Image layers with motion — see [Image segments](#image-segments) | +| `lines` | array | `[]` | Subtitle lines shown sequentially — see [Subtitle lines](#subtitle-lines) | + +### Timing model + +The total cutscene duration is: + +``` +contentDuration = max(durationMs, max(segment.endMs for all segments)) +totalDuration = contentDuration + endFadeOutMs + endFadeInMs +``` + +The full timeline looks like this: + +``` +|-- fadeOutMs --|-- fadeInMs --|--- content plays (images + subtitles) ---|-- endFadeOutMs --|-- endFadeInMs --| + world→black black→images images→black black→world +``` + +--- + +## Image segments + +Each entry in `imageSegments` describes one image layer: when it is visible, how it fades in/out, and how it animates from a start pose to an end pose. + +```json +{ + "path": "resources/cutscenes/bg_layer.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 8000, + "fadeInMs": 300, + "fadeOutMs": 300, + "easing": "EaseInOutSine", + "from": { "centerX": 0.4, "centerY": 0.5, "scale": 1.1 }, + "to": { "centerX": 0.6, "centerY": 0.5, "scale": 1.0 } +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `path` | string | — | Path to the PNG image (**required**) | +| `width` | int | `0` | Logical width used for all UV and aspect-ratio math. `0` uses the actual texture pixel width | +| `height` | int | `0` | Logical height. `0` uses the actual texture pixel height | +| `startMs` | int | `0` | Time (ms from cutscene start) when this layer becomes active | +| `endMs` | int | `0` | Time (ms) when this layer stops being active. Must be > `startMs` | +| `fadeInMs` | int | `0` | Alpha fades from 0 → 1 over this many ms after `startMs`. `0` = instant | +| `fadeOutMs` | int | `0` | Alpha fades from 1 → 0 over this many ms before `endMs`. `0` = instant | +| `easing` | string | `"Linear"` | Easing applied to the pose interpolation — see [Easing types](#easing-types) | +| `from` | pose object | center/1.0 | Pose at `startMs` — see [Image pose](#image-pose) | +| `to` | pose object | same as `from` | Pose at `endMs`. If omitted, the layer stays at `from` the whole time | + +Multiple segments can be active at the same time. They are rendered **in declaration order** (first = bottom layer, last = top layer), which enables parallax layering. + +--- + +## Image pose + +A pose defines how an image is framed on screen at a given moment. + +```json +{ "centerX": 0.5, "centerY": 0.5, "scale": 1.0 } +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `centerX` | float | `0.5` | Normalized X position (0 = left edge of image, 1 = right edge) of the point that is placed at the horizontal center of the screen | +| `centerY` | float | `0.5` | Normalized Y position (0 = top edge, 1 = bottom edge) placed at the screen center | +| `scale` | float | `1.0` | Zoom level. `1.0` = the image fills the screen exactly (aspect-ratio corrected). `2.0` = zoomed in 2×, showing half the image area | + +The runtime interpolates all three values independently from `from` to `to` using the chosen easing. + +**Coordinate clamping:** `centerX`/`centerY` are automatically clamped so the viewport never shows area outside the image. For a zoomed-in segment (`scale > 1`) you therefore have more freedom to pan; for `scale = 1.0` the center is locked to `0.5/0.5`. + +### Pose intuition + +| Goal | Config | +|---|---| +| Centered, no zoom | `{ "centerX": 0.5, "centerY": 0.5, "scale": 1.0 }` | +| Slightly zoomed in on center | `{ "centerX": 0.5, "centerY": 0.5, "scale": 1.2 }` | +| Pan left to right | `from: { "centerX": 0.3, "scale": 1.2 }` → `to: { "centerX": 0.7, "scale": 1.2 }` | +| Zoom out from close-up | `from: { "scale": 1.8 }` → `to: { "scale": 1.0 }` | +| Look at top portion | `{ "centerY": 0.2, "scale": 1.3 }` | + +--- + +## Easing types + +Controls the interpolation curve applied to pose animation between `from` and `to`. + +| Value | Description | +|---|---| +| `"Linear"` | Constant speed (default) | +| `"EaseInSine"` | Slow start, fast end | +| `"EaseOutSine"` | Fast start, slow end | +| `"EaseInOutSine"` | Slow start and end, fast middle | +| `"EaseInQuad"` | Quadratic slow start | +| `"EaseOutQuad"` | Quadratic slow end | +| `"EaseInOutQuad"` | Quadratic slow start and end | +| `"EaseInCubic"` | Cubic slow start | +| `"EaseOutCubic"` | Cubic slow end | +| `"EaseInOutCubic"` | Cubic slow start and end | + +For cinematic camera motion `"EaseInOutSine"` or `"EaseInOutCubic"` give the most natural feel. + +--- + +## Subtitle lines + +Lines are displayed sequentially on top of the cutscene images. Each line shows until its duration expires (or until the player advances, if `waitForConfirm` is set). + +```json +{ + "speaker": "Аида Дженибековна", + "text": "Здравствуйте, студенты.", + "durationMs": 3000, + "waitForConfirm": false, + "luaCallback": "" +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `speaker` | string | `""` | Speaker name shown above the subtitle text. Empty = no name bar | +| `text` | string | `""` | Subtitle text. Supports Cyrillic and any codepoint in `resources/symbols.txt` | +| `durationMs` | int | `0` | How long this line is displayed in ms. `0` = auto-computed from text length (~17 chars/sec, minimum 1500 ms) | +| `waitForConfirm` | bool | `false` | When `true`, the line waits for player input (tap/click/Enter) before advancing. No timer runs | +| `luaCallback` | string | `""` | Lua function name called when this line begins. Useful for triggering SFX, spawning effects, etc. | + +Subtitle lines run on their own timer that is **independent** of the image segments. The cutscene ends when **both** subtitle lines are exhausted **and** `contentDuration` has elapsed. + +--- + +## C++ API + +### Starting a cutscene + +```cpp +// Standalone cutscene (not part of a dialogue): +dialogueSystem.startCutscene("intro_cutscene"); + +// Skip the currently playing cutscene: +dialogueSystem.skipCutscene(); +``` + +### Callbacks + +```cpp +// Called when a cutscene begins: +dialogueSystem.setOnCutsceneStarted([]() { /* hide HUD, etc. */ }); + +// Called when a cutscene ends (receives the cutscene id): +dialogueSystem.setOnCutsceneFinished([](const std::string& id) { + // id == "intro_cutscene" +}); + +// Called when a subtitle line begins (receives luaCallback value): +dialogueSystem.setOnCutsceneLineStarted([](const std::string& fn) { + scriptEngine.callActivateFunction(fn); +}); + +// Called when the opening fade-in completes (receives onFadeInCallback value): +dialogueSystem.setOnCutsceneFadeInComplete([](const std::string& fn) { + scriptEngine.callActivateFunction(fn); +}); +``` + +### Triggering from dialogue + +A dialogue node of type `CutsceneStart` embeds a cutscene mid-conversation. Dialogue resumes at `next` when the cutscene ends. + +```json +{ + "id": "node_cutscene", + "type": "CutsceneStart", + "cutsceneId": "intro_cutscene", + "next": "node_after_cutscene" +} +``` + +--- + +## Full examples + +### Minimal — static image, timed + +```json +{ + "id": "simple", + "durationMs": 4000, + "fadeOutMs": 300, + "fadeInMs": 300, + "endFadeOutMs": 300, + "endFadeInMs": 300, + "imageSegments": [ + { + "path": "resources/cutscenes/city.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 4000 + } + ] +} +``` + +### Two-layer parallax pan + +Background moves slowly left-to-right; foreground character moves faster, creating depth. + +```json +{ + "id": "classroom_intro", + "durationMs": 8000, + "fadeOutMs": 500, + "fadeInMs": 500, + "endFadeOutMs": 500, + "endFadeInMs": 500, + "imageSegments": [ + { + "path": "resources/cutscenes/classroom_bg.png", + "width": 1920, + "height": 1080, + "startMs": 0, + "endMs": 8000, + "fadeInMs": 400, + "easing": "EaseInOutSine", + "from": { "centerX": 0.4, "centerY": 0.5, "scale": 1.1 }, + "to": { "centerX": 0.6, "centerY": 0.5, "scale": 1.0 } + }, + { + "path": "resources/cutscenes/classroom_teacher.png", + "width": 1920, + "height": 1080, + "startMs": 0, + "endMs": 8000, + "easing": "EaseInOutSine", + "from": { "centerX": 0.35, "centerY": 0.5, "scale": 1.0 }, + "to": { "centerX": 0.65, "centerY": 0.5, "scale": 1.0 } + } + ], + "lines": [ + { + "speaker": "Аида Дженибековна", + "text": "Здравствуйте, студенты.", + "durationMs": 3000 + }, + { + "speaker": "Аида Дженибековна", + "text": "Рассаживайтесь.", + "durationMs": 2500 + } + ] +} +``` + +### Zoom-in reveal with a second image appearing mid-way + +```json +{ + "id": "letter_reveal", + "durationMs": 7000, + "fadeOutMs": 400, + "fadeInMs": 600, + "endFadeOutMs": 600, + "endFadeInMs": 400, + "imageSegments": [ + { + "path": "resources/cutscenes/desk_bg.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 7000 + }, + { + "path": "resources/cutscenes/letter_closeup.png", + "width": 1280, + "height": 720, + "startMs": 2000, + "endMs": 7000, + "fadeInMs": 800, + "easing": "EaseOutCubic", + "from": { "centerX": 0.5, "centerY": 0.5, "scale": 2.5 }, + "to": { "centerX": 0.5, "centerY": 0.5, "scale": 1.2 } + } + ], + "lines": [ + { + "text": "Среди бумаг на столе лежит конверт.", + "durationMs": 2500 + }, + { + "speaker": "Главный герой", + "text": "«Явитесь в деканат немедленно».", + "durationMs": 3000 + } + ] +} +``` diff --git a/cutsceneEditor/.gitignore b/cutsceneEditor/.gitignore new file mode 100644 index 0000000..f6a045b --- /dev/null +++ b/cutsceneEditor/.gitignore @@ -0,0 +1,46 @@ +.DS_STORE +node_modules +scripts/flow/*/.flowconfig +.flowconfig +*~ +*.pyc +.grunt +_SpecRunner.html +__benchmarks__ +build/ +remote-repo/ +coverage/ +.module-cache +fixtures/dom/public/react-dom.js +fixtures/dom/public/react.js +test/the-files-to-test.generated.js +*.log* +chrome-user-data +*.sublime-project +*.sublime-workspace +.idea +*.iml +.vscode +.zed +*.swp +*.swo +/tmp +/.worktrees +.claude/*.local.* + +packages/react-devtools-core/dist +packages/react-devtools-extensions/chrome/build +packages/react-devtools-extensions/chrome/*.crx +packages/react-devtools-extensions/chrome/*.pem +packages/react-devtools-extensions/firefox/build +packages/react-devtools-extensions/firefox/*.xpi +packages/react-devtools-extensions/firefox/*.pem +packages/react-devtools-extensions/shared/build +packages/react-devtools-extensions/.tempUserDataDir +packages/react-devtools-fusebox/dist +packages/react-devtools-inline/dist +packages/react-devtools-shell/dist +packages/react-devtools-timeline/dist + +resources + diff --git a/cutsceneEditor/CUTSCENES.md b/cutsceneEditor/CUTSCENES.md new file mode 100644 index 0000000..0619303 --- /dev/null +++ b/cutsceneEditor/CUTSCENES.md @@ -0,0 +1,351 @@ +# Cutscene System + +Cutscenes are defined in JSON and loaded by `CutsceneDatabase`. Each cutscene is a self-contained object with an array of animated image layers and optional subtitle lines. + +The file can contain multiple cutscenes: + +```json +{ + "cutscenes": [ + { "id": "intro", ... }, + { "id": "ending", ... } + ] +} +``` + +Cutscenes and dialogues are loaded from **separate files**: + +```cpp +dialogueSystem.loadDatabase("resources/dialogue/uni_interior.json"); // dialogues +dialogueSystem.loadCutsceneDatabase("resources/dialogue/cutscenes.json"); // cutscenes +``` + +--- + +## Cutscene object + +```json +{ + "id": "intro_cutscene", + "skippable": true, + "durationMs": 8000, + "fadeOutMs": 500, + "fadeInMs": 500, + "endFadeOutMs": 500, + "endFadeInMs": 500, + "onFadeInCallback": "", + "imageSegments": [ ... ], + "lines": [ ... ] +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `id` | string | — | Unique identifier used to start the cutscene from C++ or dialogue (**required**) | +| `skippable` | bool | `true` | Whether the player can skip by holding LMB / touch | +| `durationMs` | int | `0` | Minimum content duration in ms. The cutscene will not end before this time even if all subtitle lines have finished. `0` means duration is determined solely by subtitle lines or `imageSegments.endMs` | +| `fadeOutMs` | int | `0` | Duration of the **opening fade** — game world fades to black before the cutscene images appear | +| `fadeInMs` | int | `0` | Duration of the **opening reveal** — cutscene images fade in from black after `fadeOutMs` | +| `endFadeOutMs` | int | `0` | Duration of the **closing fade** — cutscene fades to black at the end of content | +| `endFadeInMs` | int | `0` | Duration of the **closing reveal** — game world fades back in from black | +| `onFadeInCallback` | string | `""` | Lua function name called once the opening fade-in completes (fired after `fadeOutMs + fadeInMs` ms) | +| `imageSegments` | array | `[]` | Image layers with motion — see [Image segments](#image-segments) | +| `lines` | array | `[]` | Subtitle lines shown sequentially — see [Subtitle lines](#subtitle-lines) | + +### Timing model + +The total cutscene duration is: + +``` +contentDuration = max(durationMs, max(segment.endMs for all segments)) +totalDuration = contentDuration + endFadeOutMs + endFadeInMs +``` + +The full timeline looks like this: + +``` +|-- fadeOutMs --|-- fadeInMs --|--- content plays (images + subtitles) ---|-- endFadeOutMs --|-- endFadeInMs --| + world→black black→images images→black black→world +``` + +--- + +## Image segments + +Each entry in `imageSegments` describes one image layer: when it is visible, how it fades in/out, and how it animates from a start pose to an end pose. + +```json +{ + "path": "resources/cutscenes/bg_layer.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 8000, + "fadeInMs": 300, + "fadeOutMs": 300, + "easing": "EaseInOutSine", + "from": { "centerX": 0.4, "centerY": 0.5, "scale": 1.1 }, + "to": { "centerX": 0.6, "centerY": 0.5, "scale": 1.0 } +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `path` | string | — | Path to the PNG image (**required**) | +| `width` | int | `0` | Logical width used for all UV and aspect-ratio math. `0` uses the actual texture pixel width | +| `height` | int | `0` | Logical height. `0` uses the actual texture pixel height | +| `startMs` | int | `0` | Time (ms from cutscene start) when this layer becomes active | +| `endMs` | int | `0` | Time (ms) when this layer stops being active. Must be > `startMs` | +| `fadeInMs` | int | `0` | Alpha fades from 0 → 1 over this many ms after `startMs`. `0` = instant | +| `fadeOutMs` | int | `0` | Alpha fades from 1 → 0 over this many ms before `endMs`. `0` = instant | +| `easing` | string | `"Linear"` | Easing applied to the pose interpolation — see [Easing types](#easing-types) | +| `from` | pose object | center/1.0 | Pose at `startMs` — see [Image pose](#image-pose) | +| `to` | pose object | same as `from` | Pose at `endMs`. If omitted, the layer stays at `from` the whole time | + +Multiple segments can be active at the same time. They are rendered **in declaration order** (first = bottom layer, last = top layer), which enables parallax layering. + +--- + +## Image pose + +A pose defines how an image is framed on screen at a given moment. + +```json +{ "centerX": 0.5, "centerY": 0.5, "scale": 1.0 } +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `centerX` | float | `0.5` | Normalized X position (0 = left edge of image, 1 = right edge) of the point that is placed at the horizontal center of the screen | +| `centerY` | float | `0.5` | Normalized Y position (0 = top edge, 1 = bottom edge) placed at the screen center | +| `scale` | float | `1.0` | Zoom level. `1.0` = the image fills the screen exactly (aspect-ratio corrected). `2.0` = zoomed in 2×, showing half the image area | + +The runtime interpolates all three values independently from `from` to `to` using the chosen easing. + +**Coordinate clamping:** `centerX`/`centerY` are automatically clamped so the viewport never shows area outside the image. For a zoomed-in segment (`scale > 1`) you therefore have more freedom to pan; for `scale = 1.0` the center is locked to `0.5/0.5`. + +### Pose intuition + +| Goal | Config | +|---|---| +| Centered, no zoom | `{ "centerX": 0.5, "centerY": 0.5, "scale": 1.0 }` | +| Slightly zoomed in on center | `{ "centerX": 0.5, "centerY": 0.5, "scale": 1.2 }` | +| Pan left to right | `from: { "centerX": 0.3, "scale": 1.2 }` → `to: { "centerX": 0.7, "scale": 1.2 }` | +| Zoom out from close-up | `from: { "scale": 1.8 }` → `to: { "scale": 1.0 }` | +| Look at top portion | `{ "centerY": 0.2, "scale": 1.3 }` | + +--- + +## Easing types + +Controls the interpolation curve applied to pose animation between `from` and `to`. + +| Value | Description | +|---|---| +| `"Linear"` | Constant speed (default) | +| `"EaseInSine"` | Slow start, fast end | +| `"EaseOutSine"` | Fast start, slow end | +| `"EaseInOutSine"` | Slow start and end, fast middle | +| `"EaseInQuad"` | Quadratic slow start | +| `"EaseOutQuad"` | Quadratic slow end | +| `"EaseInOutQuad"` | Quadratic slow start and end | +| `"EaseInCubic"` | Cubic slow start | +| `"EaseOutCubic"` | Cubic slow end | +| `"EaseInOutCubic"` | Cubic slow start and end | + +For cinematic camera motion `"EaseInOutSine"` or `"EaseInOutCubic"` give the most natural feel. + +--- + +## Subtitle lines + +Lines are displayed sequentially on top of the cutscene images. Each line shows until its duration expires (or until the player advances, if `waitForConfirm` is set). + +```json +{ + "speaker": "Аида Дженибековна", + "text": "Здравствуйте, студенты.", + "durationMs": 3000, + "waitForConfirm": false, + "luaCallback": "" +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `speaker` | string | `""` | Speaker name shown above the subtitle text. Empty = no name bar | +| `text` | string | `""` | Subtitle text. Supports Cyrillic and any codepoint in `resources/symbols.txt` | +| `durationMs` | int | `0` | How long this line is displayed in ms. `0` = auto-computed from text length (~17 chars/sec, minimum 1500 ms) | +| `waitForConfirm` | bool | `false` | When `true`, the line waits for player input (tap/click/Enter) before advancing. No timer runs | +| `luaCallback` | string | `""` | Lua function name called when this line begins. Useful for triggering SFX, spawning effects, etc. | + +Subtitle lines run on their own timer that is **independent** of the image segments. The cutscene ends when **both** subtitle lines are exhausted **and** `contentDuration` has elapsed. + +--- + +## C++ API + +### Starting a cutscene + +```cpp +// Standalone cutscene (not part of a dialogue): +dialogueSystem.startCutscene("intro_cutscene"); + +// Skip the currently playing cutscene: +dialogueSystem.skipCutscene(); +``` + +### Callbacks + +```cpp +// Called when a cutscene begins: +dialogueSystem.setOnCutsceneStarted([]() { /* hide HUD, etc. */ }); + +// Called when a cutscene ends (receives the cutscene id): +dialogueSystem.setOnCutsceneFinished([](const std::string& id) { + // id == "intro_cutscene" +}); + +// Called when a subtitle line begins (receives luaCallback value): +dialogueSystem.setOnCutsceneLineStarted([](const std::string& fn) { + scriptEngine.callActivateFunction(fn); +}); + +// Called when the opening fade-in completes (receives onFadeInCallback value): +dialogueSystem.setOnCutsceneFadeInComplete([](const std::string& fn) { + scriptEngine.callActivateFunction(fn); +}); +``` + +### Triggering from dialogue + +A dialogue node of type `CutsceneStart` embeds a cutscene mid-conversation. Dialogue resumes at `next` when the cutscene ends. + +```json +{ + "id": "node_cutscene", + "type": "CutsceneStart", + "cutsceneId": "intro_cutscene", + "next": "node_after_cutscene" +} +``` + +--- + +## Full examples + +### Minimal — static image, timed + +```json +{ + "id": "simple", + "durationMs": 4000, + "fadeOutMs": 300, + "fadeInMs": 300, + "endFadeOutMs": 300, + "endFadeInMs": 300, + "imageSegments": [ + { + "path": "resources/cutscenes/city.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 4000 + } + ] +} +``` + +### Two-layer parallax pan + +Background moves slowly left-to-right; foreground character moves faster, creating depth. + +```json +{ + "id": "classroom_intro", + "durationMs": 8000, + "fadeOutMs": 500, + "fadeInMs": 500, + "endFadeOutMs": 500, + "endFadeInMs": 500, + "imageSegments": [ + { + "path": "resources/cutscenes/classroom_bg.png", + "width": 1920, + "height": 1080, + "startMs": 0, + "endMs": 8000, + "fadeInMs": 400, + "easing": "EaseInOutSine", + "from": { "centerX": 0.4, "centerY": 0.5, "scale": 1.1 }, + "to": { "centerX": 0.6, "centerY": 0.5, "scale": 1.0 } + }, + { + "path": "resources/cutscenes/classroom_teacher.png", + "width": 1920, + "height": 1080, + "startMs": 0, + "endMs": 8000, + "easing": "EaseInOutSine", + "from": { "centerX": 0.35, "centerY": 0.5, "scale": 1.0 }, + "to": { "centerX": 0.65, "centerY": 0.5, "scale": 1.0 } + } + ], + "lines": [ + { + "speaker": "Аида Дженибековна", + "text": "Здравствуйте, студенты.", + "durationMs": 3000 + }, + { + "speaker": "Аида Дженибековна", + "text": "Рассаживайтесь.", + "durationMs": 2500 + } + ] +} +``` + +### Zoom-in reveal with a second image appearing mid-way + +```json +{ + "id": "letter_reveal", + "durationMs": 7000, + "fadeOutMs": 400, + "fadeInMs": 600, + "endFadeOutMs": 600, + "endFadeInMs": 400, + "imageSegments": [ + { + "path": "resources/cutscenes/desk_bg.png", + "width": 1280, + "height": 720, + "startMs": 0, + "endMs": 7000 + }, + { + "path": "resources/cutscenes/letter_closeup.png", + "width": 1280, + "height": 720, + "startMs": 2000, + "endMs": 7000, + "fadeInMs": 800, + "easing": "EaseOutCubic", + "from": { "centerX": 0.5, "centerY": 0.5, "scale": 2.5 }, + "to": { "centerX": 0.5, "centerY": 0.5, "scale": 1.2 } + } + ], + "lines": [ + { + "text": "Среди бумаг на столе лежит конверт.", + "durationMs": 2500 + }, + { + "speaker": "Главный герой", + "text": "«Явитесь в деканат немедленно».", + "durationMs": 3000 + } + ] +} +``` diff --git a/cutsceneEditor/cutscene_example.json b/cutsceneEditor/cutscene_example.json new file mode 100644 index 0000000..b64be9b --- /dev/null +++ b/cutsceneEditor/cutscene_example.json @@ -0,0 +1,65 @@ +{ + "cutscenes": [ + { + "id": "test_cutscene_01", + "background": "resources/black.png", + "durationMs": 5000, + "fadeOutMs": 500, + "fadeInMs": 500, + "endFadeOutMs": 500, + "endFadeInMs": 500, + "imageSegments": [ + { + "path": "resources/w/cutscenes/cutscene1/cutscene1_wall_x.png", + "startMs": 0, + "endMs": 8000, + "fadeInMs": 0, + "width": 1280, + "height": 720, + "from": { + "centerX": 0.3, "scale": 1.2 + }, + "to": { + "centerX": 0.7, "scale": 1.2 + }, + "easing": "Linear" + }, + { + "path": "resources/w/cutscenes/cutscene1/cutscene1_aida1_x.png", + "startMs": 0, + "endMs": 8000, + "width": 1280, + "height": 720, + "from": { + + "centerX": 0.3, + "centerY": 0.5, + "scale": 1.0 + }, + "to": { + "centerX": 0.7, + "centerY": 0.5, + "scale": 1.0 + } + } + ], + "lines": [ + { + "speaker": "Аида Дженибековна", + "text": "Здравствуйте, студенты. Кого я вижу, где вы были весь семестр?", + "durationMs": 3000 + }, + { + "speaker": "Аида Дженибековна", + "text": "В эпизоде \"Семетей\" трилогии \"Манас\", изменники Канчоро и Кыяз захватывают власть над кыргызами.", + "durationMs": 3000 + }, + { + "speaker": "Аида Дженибековна", + "text": "На сегодня лекция завершена. Домашнее задание - к практическому занятию вы должны подготовить презентации, каждый по своей теме.", + "durationMs": 2000 + } + ] + } + ] +} \ No newline at end of file diff --git a/cutsceneEditor/dist/assets/index-B96C0g1n.css b/cutsceneEditor/dist/assets/index-B96C0g1n.css new file mode 100644 index 0000000..da7d466 --- /dev/null +++ b/cutsceneEditor/dist/assets/index-B96C0g1n.css @@ -0,0 +1 @@ +*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body,#root{height:100%;width:100%;overflow:hidden}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px;background:#1a1a1a;color:#e0e0e0}button{cursor:pointer;font-family:inherit;font-size:12px}input,select,textarea{font-family:inherit;font-size:12px;background:#2a2a2a;color:#e0e0e0;border:1px solid #444;border-radius:3px;padding:3px 6px}input:focus,select:focus,textarea:focus{outline:none;border-color:#5b9bd5}label{color:#aaa;font-size:11px}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:#1a1a1a}::-webkit-scrollbar-thumb{background:#444;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#666}._app_1ibpl_1{display:flex;width:100%;height:100%;overflow:hidden}._panel_e520h_1{width:200px;min-width:180px;background:#252525;border-right:1px solid #333;display:flex;flex-direction:column;overflow:hidden}._header_e520h_11{padding:10px 12px;font-size:12px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:#888;border-bottom:1px solid #333}._actions_e520h_21{display:flex;flex-direction:column;gap:6px;padding:10px;border-bottom:1px solid #333}._btn_e520h_29{background:#333;color:#ddd;border:1px solid #444;border-radius:4px;padding:5px 8px;text-align:center;transition:background .15s}._btn_e520h_29:hover:not(:disabled){background:#3d3d3d}._btn_e520h_29:disabled{opacity:.4;cursor:default}._btnPrimary_e520h_42{background:#2a4a6e;border-color:#3a6090;color:#a8d0f0}._btnPrimary_e520h_42:hover{background:#2e5480}._list_e520h_50{flex:1;overflow-y:auto;padding:4px 0}._empty_e520h_56{padding:16px 12px;color:#555;font-size:11px;line-height:1.5}._item_e520h_63{display:flex;align-items:center;justify-content:space-between;padding:7px 12px;cursor:pointer;-webkit-user-select:none;user-select:none;border-left:3px solid transparent;transition:background .1s}._item_e520h_63:hover{background:#2e2e2e}._selected_e520h_76{background:#1e3a55;border-left-color:#5b9bd5}._itemId_e520h_81{font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}._deleteBtn_e520h_89{background:none;border:none;color:#666;padding:2px 4px;border-radius:3px;font-size:10px;opacity:0;transition:opacity .1s,color .1s}._item_e520h_63:hover ._deleteBtn_e520h_89{opacity:1}._deleteBtn_e520h_89:hover{color:#e06c6c}._panel_1kkcd_1{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0}._previewArea_1kkcd_9{flex:0 0 auto;display:flex;justify-content:center;align-items:center;background:#111;padding:8px;max-height:60%}._wrapper_1nucj_1{width:100%;display:flex;justify-content:center;align-items:center;background:#111;padding:8px;flex-shrink:0}._viewport_1nucj_11{position:relative;aspect-ratio:16 / 9;width:100%;max-height:100%;background:#000;overflow:hidden}._empty_1nucj_20{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;color:#444;font-size:13px}._subtitleBar_1nucj_30{position:absolute;bottom:0;left:0;right:0;padding:10px 20px 14px;background:linear-gradient(transparent,#000000bf);text-align:center}._speaker_1nucj_40{font-size:11px;font-weight:600;color:#f0c060;margin-bottom:4px;text-shadow:0 1px 3px rgba(0,0,0,.8)}._text_1nucj_48{font-size:14px;color:#fff;line-height:1.5;text-shadow:0 1px 4px rgba(0,0,0,.9)}._controls_1qoyb_1{display:flex;align-items:center;gap:4px;padding:6px 10px;background:#1e1e1e;border-top:1px solid #333;border-bottom:1px solid #333;flex-shrink:0}._btn_1qoyb_12{background:#2d2d2d;border:1px solid #404040;color:#ccc;border-radius:4px;padding:4px 10px;font-size:13px;transition:background .1s;min-width:32px}._btn_1qoyb_12:hover:not(:disabled){background:#3a3a3a;color:#fff}._btn_1qoyb_12:disabled{opacity:.35;cursor:default}._active_1qoyb_26{color:#5ba3e0;border-color:#4a7aaa}._time_1qoyb_31{margin-left:10px;font-size:11px;color:#888;font-variant-numeric:tabular-nums}._container_uetgi_1{display:flex;flex-direction:column;flex:1;min-height:0;background:#1a1a1a;border-top:1px solid #333;overflow:hidden}._toolbar_uetgi_11{display:flex;align-items:center;gap:4px;padding:4px 8px;background:#222;border-bottom:1px solid #333;flex-shrink:0}._tbBtn_uetgi_21{background:#2d2d2d;border:1px solid #404040;color:#bbb;border-radius:3px;padding:3px 8px;font-size:11px;transition:background .1s}._tbBtn_uetgi_21:hover:not(:disabled){background:#3a3a3a;color:#fff}._tbBtn_uetgi_21:disabled{opacity:.3;cursor:default}._spacer_uetgi_33{flex:1}._zoomLabel_uetgi_35{font-size:11px;color:#666;margin-right:2px}._scroll_uetgi_37{flex:1;overflow:auto;position:relative;cursor:default}._ruler_uetgi_45{display:flex;height:22px;background:#212121;border-bottom:1px solid #333;position:sticky;top:0;z-index:10;-webkit-user-select:none;user-select:none;cursor:crosshair}._tick_uetgi_57{position:absolute;top:0;bottom:0;width:1px;background:#444}._tickLabel_uetgi_65{position:absolute;top:3px;left:3px;font-size:9px;color:#666;white-space:nowrap}._layersArea_uetgi_75{position:relative}._playhead_uetgi_79{position:absolute;top:0;bottom:0;width:2px;background:#e05050;z-index:20;pointer-events:none}._gridLine_uetgi_89{position:absolute;top:0;bottom:0;width:1px;background:#ffffff0a;pointer-events:none}._row_uetgi_99{position:absolute;left:0;right:0;height:32px;display:flex;align-items:center;border-bottom:1px solid #2a2a2a;cursor:pointer;transition:background .1s}._row_uetgi_99:hover{background:#ffffff08}._rowSelected_uetgi_111{background:#5b9bd514}._rowLabel_uetgi_113{position:absolute;left:0;width:140px;height:100%;display:flex;align-items:center;padding:0 8px;background:#1e1e1e;border-right:1px solid #2a2a2a;z-index:5;overflow:hidden;flex-shrink:0}._layerName_uetgi_128{font-size:11px;color:#bbb;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._bar_uetgi_137{position:absolute;height:22px;border-radius:3px;cursor:grab;top:5px;border:1px solid rgba(255,255,255,.15);box-sizing:border-box;min-width:4px}._bar_uetgi_137:active{cursor:grabbing}._handle_uetgi_149{position:absolute;top:0;bottom:0;width:6px;cursor:ew-resize;z-index:2}._handleLeft_uetgi_157{left:0;border-radius:3px 0 0 3px;background:#ffffff26}._handleRight_uetgi_158{right:0;border-radius:0 3px 3px 0;background:#ffffff26}._subtitleRow_uetgi_161{position:absolute;left:0;right:0;height:14px;border-bottom:1px solid #2a2a2a}._subtitleLabel_uetgi_169{position:absolute;left:0;width:140px;height:100%;background:#1e1e1e;border-right:1px solid #2a2a2a;display:flex;align-items:center;padding:0 8px;font-size:9px;color:#555;z-index:5}._subtitleBlock_uetgi_184{position:absolute;top:2px;height:10px;background:#8a6040;border-radius:2px;border:1px solid #aa7050;opacity:.8}._panel_1uywn_1{width:260px;min-width:240px;background:#252525;border-left:1px solid #333;display:flex;flex-direction:column;overflow:hidden}._tabs_1uywn_11{display:flex;border-bottom:1px solid #333;flex-shrink:0}._tab_1uywn_11{flex:1;padding:8px 6px;background:none;border:none;color:#777;font-size:11px;border-bottom:2px solid transparent;transition:color .1s}._tab_1uywn_11:hover{color:#bbb}._tabActive_1uywn_28{color:#5b9bd5;border-bottom-color:#5b9bd5}._scroll_1uywn_33{flex:1;overflow-y:auto;padding-bottom:20px}._section_1uywn_40{padding:10px;border-bottom:1px solid #2e2e2e}._sectionTitle_1uywn_45{font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px}._sectionHeader_1uywn_54{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}._addBtn_1uywn_61{background:#2a4a6e;border:1px solid #3a6090;color:#a8d0f0;border-radius:3px;padding:2px 8px;font-size:11px}._addBtn_1uywn_61:hover{background:#2e5480}._field_1uywn_72{display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;gap:6px}._field_1uywn_72 label{flex-shrink:0;width:100px;text-align:right}._field_1uywn_72 input[type=text],._field_1uywn_72 input[type=number],._field_1uywn_72 select,._field_1uywn_72 textarea{flex:1;min-width:0;width:100%}._field_1uywn_72 textarea{resize:vertical;min-height:40px}._field_1uywn_72 input[type=checkbox]{width:auto;margin:0}._row2_1uywn_105{display:flex;gap:6px}._row2_1uywn_105 ._field_1uywn_72{flex:1;flex-direction:column;align-items:flex-start}._row2_1uywn_105 ._field_1uywn_72 label{width:auto;text-align:left;margin-bottom:2px}._poseGroup_1uywn_123{margin-top:8px;background:#1e1e1e;border-radius:4px;padding:6px 8px}._poseTitle_1uywn_130{font-size:10px;font-weight:600;color:#666;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px}._poseRow_1uywn_139{display:flex;gap:6px}._poseRow_1uywn_139 ._field_1uywn_72{flex:1;flex-direction:column;align-items:flex-start;margin-bottom:0}._poseRow_1uywn_139 ._field_1uywn_72 label{width:auto;text-align:left;margin-bottom:2px}._lineCard_1uywn_158{background:#1e1e1e;border:1px solid #2e2e2e;border-radius:4px;padding:8px;margin-bottom:6px}._lineHeader_1uywn_166{display:flex;align-items:center;gap:4px;margin-bottom:6px}._lineIndex_1uywn_173{font-size:10px;color:#666;flex:1}._iconBtn_1uywn_179{background:none;border:1px solid #3a3a3a;color:#888;border-radius:3px;padding:1px 5px;font-size:11px}._iconBtn_1uywn_179:hover:not(:disabled){background:#333;color:#ccc}._iconBtn_1uywn_179:disabled{opacity:.3;cursor:default}._iconBtnDanger_1uywn_190{color:#a05050;border-color:#5a2a2a}._iconBtnDanger_1uywn_190:hover{background:#3a2020;color:#e06c6c}._emptyLines_1uywn_197{color:#555;font-size:11px;padding:4px 0}._empty_1uywn_197{padding:20px;color:#555;font-size:11px;text-align:center} diff --git a/cutsceneEditor/dist/assets/index-D_5Tak8P.js b/cutsceneEditor/dist/assets/index-D_5Tak8P.js new file mode 100644 index 0000000..11fa176 --- /dev/null +++ b/cutsceneEditor/dist/assets/index-D_5Tak8P.js @@ -0,0 +1,40 @@ +(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const p of document.querySelectorAll('link[rel="modulepreload"]'))f(p);new MutationObserver(p=>{for(const g of p)if(g.type==="childList")for(const S of g.addedNodes)S.tagName==="LINK"&&S.rel==="modulepreload"&&f(S)}).observe(document,{childList:!0,subtree:!0});function s(p){const g={};return p.integrity&&(g.integrity=p.integrity),p.referrerPolicy&&(g.referrerPolicy=p.referrerPolicy),p.crossOrigin==="use-credentials"?g.credentials="include":p.crossOrigin==="anonymous"?g.credentials="omit":g.credentials="same-origin",g}function f(p){if(p.ep)return;p.ep=!0;const g=s(p);fetch(p.href,g)}})();function Bd(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var bu={exports:{}},Or={},eo={exports:{}},G={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var pc;function $d(){if(pc)return G;pc=1;var u=Symbol.for("react.element"),a=Symbol.for("react.portal"),s=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),p=Symbol.for("react.profiler"),g=Symbol.for("react.provider"),S=Symbol.for("react.context"),L=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),U=Symbol.for("react.memo"),ee=Symbol.for("react.lazy"),D=Symbol.iterator;function H(h){return h===null||typeof h!="object"?null:(h=D&&h[D]||h["@@iterator"],typeof h=="function"?h:null)}var fe={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},me=Object.assign,re={};function q(h,k,X){this.props=h,this.context=k,this.refs=re,this.updater=X||fe}q.prototype.isReactComponent={},q.prototype.setState=function(h,k){if(typeof h!="object"&&typeof h!="function"&&h!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,h,k,"setState")},q.prototype.forceUpdate=function(h){this.updater.enqueueForceUpdate(this,h,"forceUpdate")};function ge(){}ge.prototype=q.prototype;function Ge(h,k,X){this.props=h,this.context=k,this.refs=re,this.updater=X||fe}var Be=Ge.prototype=new ge;Be.constructor=Ge,me(Be,q.prototype),Be.isPureReactComponent=!0;var ke=Array.isArray,Ze=Object.prototype.hasOwnProperty,Ee={current:null},Ne={key:!0,ref:!0,__self:!0,__source:!0};function $e(h,k,X){var Y,Z={},b=null,oe=null;if(k!=null)for(Y in k.ref!==void 0&&(oe=k.ref),k.key!==void 0&&(b=""+k.key),k)Ze.call(k,Y)&&!Ne.hasOwnProperty(Y)&&(Z[Y]=k[Y]);var J=arguments.length-2;if(J===1)Z.children=X;else if(1>>1,k=M[h];if(0>>1;hp(Z,P))bp(oe,Z)?(M[h]=oe,M[b]=P,h=b):(M[h]=Z,M[Y]=P,h=Y);else if(bp(oe,P))M[h]=oe,M[b]=P,h=b;else break e}}return A}function p(M,A){var P=M.sortIndex-A.sortIndex;return P!==0?P:M.id-A.id}if(typeof performance=="object"&&typeof performance.now=="function"){var g=performance;u.unstable_now=function(){return g.now()}}else{var S=Date,L=S.now();u.unstable_now=function(){return S.now()-L}}var z=[],U=[],ee=1,D=null,H=3,fe=!1,me=!1,re=!1,q=typeof setTimeout=="function"?setTimeout:null,ge=typeof clearTimeout=="function"?clearTimeout:null,Ge=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Be(M){for(var A=s(U);A!==null;){if(A.callback===null)f(U);else if(A.startTime<=M)f(U),A.sortIndex=A.expirationTime,a(z,A);else break;A=s(U)}}function ke(M){if(re=!1,Be(M),!me)if(s(z)!==null)me=!0,le(Ze);else{var A=s(U);A!==null&&K(ke,A.startTime-M)}}function Ze(M,A){me=!1,re&&(re=!1,ge($e),$e=-1),fe=!0;var P=H;try{for(Be(A),D=s(z);D!==null&&(!(D.expirationTime>A)||M&&!kt());){var h=D.callback;if(typeof h=="function"){D.callback=null,H=D.priorityLevel;var k=h(D.expirationTime<=A);A=u.unstable_now(),typeof k=="function"?D.callback=k:D===s(z)&&f(z),Be(A)}else f(z);D=s(z)}if(D!==null)var X=!0;else{var Y=s(U);Y!==null&&K(ke,Y.startTime-A),X=!1}return X}finally{D=null,H=P,fe=!1}}var Ee=!1,Ne=null,$e=-1,rt=5,pt=-1;function kt(){return!(u.unstable_now()-ptM||125h?(M.sortIndex=P,a(U,M),s(z)===null&&M===s(U)&&(re?(ge($e),$e=-1):re=!0,K(ke,P-h))):(M.sortIndex=k,a(z,M),me||fe||(me=!0,le(Ze))),M},u.unstable_shouldYield=kt,u.unstable_wrapCallback=function(M){var A=H;return function(){var P=H;H=A;try{return M.apply(this,arguments)}finally{H=P}}}})(ro)),ro}var gc;function Qd(){return gc||(gc=1,no.exports=Vd()),no.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var _c;function Xd(){if(_c)return Ke;_c=1;var u=vo(),a=Qd();function s(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),z=Object.prototype.hasOwnProperty,U=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ee={},D={};function H(e){return z.call(D,e)?!0:z.call(ee,e)?!1:U.test(e)?D[e]=!0:(ee[e]=!0,!1)}function fe(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function me(e,t,n,r){if(t===null||typeof t>"u"||fe(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function re(e,t,n,r,l,i,o){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=l,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=o}var q={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){q[e]=new re(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];q[t]=new re(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){q[e]=new re(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){q[e]=new re(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){q[e]=new re(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){q[e]=new re(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){q[e]=new re(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){q[e]=new re(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){q[e]=new re(e,5,!1,e.toLowerCase(),null,!1,!1)});var ge=/[\-:]([a-z])/g;function Ge(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(ge,Ge);q[t]=new re(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(ge,Ge);q[t]=new re(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(ge,Ge);q[t]=new re(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){q[e]=new re(e,1,!1,e.toLowerCase(),null,!1,!1)}),q.xlinkHref=new re("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){q[e]=new re(e,1,!1,e.toLowerCase(),null,!0,!0)});function Be(e,t,n,r){var l=q.hasOwnProperty(t)?q[t]:null;(l!==null?l.type!==0:r||!(2c||l[o]!==i[c]){var d=` +`+l[o].replace(" at new "," at ");return e.displayName&&d.includes("")&&(d=d.replace("",e.displayName)),d}while(1<=o&&0<=c);break}}}finally{X=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?k(e):""}function Z(e){switch(e.tag){case 5:return k(e.type);case 16:return k("Lazy");case 13:return k("Suspense");case 19:return k("SuspenseList");case 0:case 2:case 15:return e=Y(e.type,!1),e;case 11:return e=Y(e.type.render,!1),e;case 1:return e=Y(e.type,!0),e;default:return""}}function b(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Ne:return"Fragment";case Ee:return"Portal";case rt:return"Profiler";case $e:return"StrictMode";case De:return"Suspense";case I:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case kt:return(e.displayName||"Context")+".Consumer";case pt:return(e._context.displayName||"Context")+".Provider";case He:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case Q:return t=e.displayName||null,t!==null?t:b(e.type)||"Memo";case le:t=e._payload,e=e._init;try{return b(e(t))}catch{}}return null}function oe(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return b(t);case 8:return t===$e?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function J(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function de(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Je(e){var t=de(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var l=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(o){r=""+o,i.call(this,o)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(o){r=""+o},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Br(e){e._valueTracker||(e._valueTracker=Je(e))}function _o(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=de(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function $r(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function ui(e,t){var n=t.checked;return P({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function wo(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=J(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function So(e,t){t=t.checked,t!=null&&Be(e,"checked",t,!1)}function oi(e,t){So(e,t);var n=J(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?si(e,t.type,n):t.hasOwnProperty("defaultValue")&&si(e,t.type,J(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function ko(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function si(e,t,n){(t!=="number"||$r(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Gn=Array.isArray;function Sn(e,t,n,r){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=Hr.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Zn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Jn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Wc=["Webkit","ms","Moz","O"];Object.keys(Jn).forEach(function(e){Wc.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Jn[t]=Jn[e]})});function Mo(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Jn.hasOwnProperty(e)&&Jn[e]?(""+t).trim():t+"px"}function Po(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,l=Mo(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,l):e[n]=l}}var Vc=P({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function fi(e,t){if(t){if(Vc[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(s(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(s(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(s(61))}if(t.style!=null&&typeof t.style!="object")throw Error(s(62))}}function di(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var pi=null;function mi(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var hi=null,kn=null,xn=null;function jo(e){if(e=_r(e)){if(typeof hi!="function")throw Error(s(280));var t=e.stateNode;t&&(t=fl(t),hi(e.stateNode,e.type,t))}}function Io(e){kn?xn?xn.push(e):xn=[e]:kn=e}function zo(){if(kn){var e=kn,t=xn;if(xn=kn=null,jo(e),t)for(e=0;e>>=0,e===0?32:31-(tf(e)/nf|0)|0}var Yr=64,Kr=4194304;function tr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Gr(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,l=e.suspendedLanes,i=e.pingedLanes,o=n&268435455;if(o!==0){var c=o&~l;c!==0?r=tr(c):(i&=o,i!==0&&(r=tr(i)))}else o=n&~l,o!==0?r=tr(o):i!==0&&(r=tr(i));if(r===0)return 0;if(t!==0&&t!==r&&(t&l)===0&&(l=r&-r,i=t&-t,l>=i||l===16&&(i&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function nr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-mt(t),e[t]=n}function of(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=cr),us=" ",os=!1;function ss(e,t){switch(e){case"keyup":return Rf.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function as(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Nn=!1;function Df(e,t){switch(e){case"compositionend":return as(t);case"keypress":return t.which!==32?null:(os=!0,us);case"textInput":return e=t.data,e===us&&os?null:e;default:return null}}function Af(e,t){if(Nn)return e==="compositionend"||!Ti&&ss(e,t)?(e=es(),el=Li=$t=null,Nn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=vs(n)}}function gs(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?gs(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function _s(){for(var e=window,t=$r();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=$r(e.document)}return t}function Fi(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Yf(e){var t=_s(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&gs(n.ownerDocument.documentElement,n)){if(r!==null&&Fi(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=n.textContent.length,i=Math.min(r.start,l);r=r.end===void 0?i:Math.min(r.end,l),!e.extend&&i>r&&(l=r,r=i,i=l),l=ys(n,i);var o=ys(n,r);l&&o&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==o.node||e.focusOffset!==o.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(o.node,o.offset)):(t.setEnd(o.node,o.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Ln=null,Di=null,mr=null,Ai=!1;function ws(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;Ai||Ln==null||Ln!==$r(r)||(r=Ln,"selectionStart"in r&&Fi(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),mr&&pr(mr,r)||(mr=r,r=sl(Di,"onSelect"),0zn||(e.current=Zi[zn],Zi[zn]=null,zn--)}function se(e,t){zn++,Zi[zn]=e.current,e.current=t}var Qt={},Te=Vt(Qt),We=Vt(!1),un=Qt;function Tn(e,t){var n=e.type.contextTypes;if(!n)return Qt;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var l={},i;for(i in n)l[i]=t[i];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function Ve(e){return e=e.childContextTypes,e!=null}function dl(){ce(We),ce(Te)}function Rs(e,t,n){if(Te.current!==Qt)throw Error(s(168));se(Te,t),se(We,n)}function Fs(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var l in r)if(!(l in t))throw Error(s(108,oe(e)||"Unknown",l));return P({},n,r)}function pl(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Qt,un=Te.current,se(Te,e),se(We,We.current),!0}function Ds(e,t,n){var r=e.stateNode;if(!r)throw Error(s(169));n?(e=Fs(e,t,un),r.__reactInternalMemoizedMergedChildContext=e,ce(We),ce(Te),se(Te,e)):ce(We),se(We,n)}var jt=null,ml=!1,Ji=!1;function As(e){jt===null?jt=[e]:jt.push(e)}function id(e){ml=!0,As(e)}function Xt(){if(!Ji&&jt!==null){Ji=!0;var e=0,t=ue;try{var n=jt;for(ue=1;e>=o,l-=o,It=1<<32-mt(t)+l|n<W?(Pe=$,$=null):Pe=$.sibling;var ne=x(v,$,_[W],N);if(ne===null){$===null&&($=Pe);break}e&&$&&ne.alternate===null&&t(v,$),m=i(ne,m,W),B===null?F=ne:B.sibling=ne,B=ne,$=Pe}if(W===_.length)return n(v,$),pe&&sn(v,W),F;if($===null){for(;W<_.length;W++)$=E(v,_[W],N),$!==null&&(m=i($,m,W),B===null?F=$:B.sibling=$,B=$);return pe&&sn(v,W),F}for($=r(v,$);W<_.length;W++)Pe=j($,v,W,_[W],N),Pe!==null&&(e&&Pe.alternate!==null&&$.delete(Pe.key===null?W:Pe.key),m=i(Pe,m,W),B===null?F=Pe:B.sibling=Pe,B=Pe);return e&&$.forEach(function(tn){return t(v,tn)}),pe&&sn(v,W),F}function R(v,m,_,N){var F=A(_);if(typeof F!="function")throw Error(s(150));if(_=F.call(_),_==null)throw Error(s(151));for(var B=F=null,$=m,W=m=0,Pe=null,ne=_.next();$!==null&&!ne.done;W++,ne=_.next()){$.index>W?(Pe=$,$=null):Pe=$.sibling;var tn=x(v,$,ne.value,N);if(tn===null){$===null&&($=Pe);break}e&&$&&tn.alternate===null&&t(v,$),m=i(tn,m,W),B===null?F=tn:B.sibling=tn,B=tn,$=Pe}if(ne.done)return n(v,$),pe&&sn(v,W),F;if($===null){for(;!ne.done;W++,ne=_.next())ne=E(v,ne.value,N),ne!==null&&(m=i(ne,m,W),B===null?F=ne:B.sibling=ne,B=ne);return pe&&sn(v,W),F}for($=r(v,$);!ne.done;W++,ne=_.next())ne=j($,v,W,ne.value,N),ne!==null&&(e&&ne.alternate!==null&&$.delete(ne.key===null?W:ne.key),m=i(ne,m,W),B===null?F=ne:B.sibling=ne,B=ne);return e&&$.forEach(function(Ud){return t(v,Ud)}),pe&&sn(v,W),F}function we(v,m,_,N){if(typeof _=="object"&&_!==null&&_.type===Ne&&_.key===null&&(_=_.props.children),typeof _=="object"&&_!==null){switch(_.$$typeof){case Ze:e:{for(var F=_.key,B=m;B!==null;){if(B.key===F){if(F=_.type,F===Ne){if(B.tag===7){n(v,B.sibling),m=l(B,_.props.children),m.return=v,v=m;break e}}else if(B.elementType===F||typeof F=="object"&&F!==null&&F.$$typeof===le&&Vs(F)===B.type){n(v,B.sibling),m=l(B,_.props),m.ref=wr(v,B,_),m.return=v,v=m;break e}n(v,B);break}else t(v,B);B=B.sibling}_.type===Ne?(m=vn(_.props.children,v.mode,N,_.key),m.return=v,v=m):(N=Hl(_.type,_.key,_.props,null,v.mode,N),N.ref=wr(v,m,_),N.return=v,v=N)}return o(v);case Ee:e:{for(B=_.key;m!==null;){if(m.key===B)if(m.tag===4&&m.stateNode.containerInfo===_.containerInfo&&m.stateNode.implementation===_.implementation){n(v,m.sibling),m=l(m,_.children||[]),m.return=v,v=m;break e}else{n(v,m);break}else t(v,m);m=m.sibling}m=Ku(_,v.mode,N),m.return=v,v=m}return o(v);case le:return B=_._init,we(v,m,B(_._payload),N)}if(Gn(_))return O(v,m,_,N);if(A(_))return R(v,m,_,N);gl(v,_)}return typeof _=="string"&&_!==""||typeof _=="number"?(_=""+_,m!==null&&m.tag===6?(n(v,m.sibling),m=l(m,_),m.return=v,v=m):(n(v,m),m=Yu(_,v.mode,N),m.return=v,v=m),o(v)):n(v,m)}return we}var Dn=Qs(!0),Xs=Qs(!1),_l=Vt(null),wl=null,An=null,ru=null;function lu(){ru=An=wl=null}function iu(e){var t=_l.current;ce(_l),e._currentValue=t}function uu(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Un(e,t){wl=e,ru=An=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Qe=!0),e.firstContext=null)}function ut(e){var t=e._currentValue;if(ru!==e)if(e={context:e,memoizedValue:t,next:null},An===null){if(wl===null)throw Error(s(308));An=e,wl.dependencies={lanes:0,firstContext:e}}else An=An.next=e;return t}var an=null;function ou(e){an===null?an=[e]:an.push(e)}function Ys(e,t,n,r){var l=t.interleaved;return l===null?(n.next=n,ou(t)):(n.next=l.next,l.next=n),t.interleaved=n,Tt(e,r)}function Tt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var Yt=!1;function su(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Ks(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ot(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Kt(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(te&2)!==0){var l=r.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),r.pending=t,Tt(e,n)}return l=r.interleaved,l===null?(t.next=t,ou(r)):(t.next=l.next,l.next=t),r.interleaved=t,Tt(e,n)}function Sl(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ki(e,n)}}function Gs(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var l=null,i=null;if(n=n.firstBaseUpdate,n!==null){do{var o={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};i===null?l=i=o:i=i.next=o,n=n.next}while(n!==null);i===null?l=i=t:i=i.next=t}else l=i=t;n={baseState:r.baseState,firstBaseUpdate:l,lastBaseUpdate:i,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function kl(e,t,n,r){var l=e.updateQueue;Yt=!1;var i=l.firstBaseUpdate,o=l.lastBaseUpdate,c=l.shared.pending;if(c!==null){l.shared.pending=null;var d=c,w=d.next;d.next=null,o===null?i=w:o.next=w,o=d;var C=e.alternate;C!==null&&(C=C.updateQueue,c=C.lastBaseUpdate,c!==o&&(c===null?C.firstBaseUpdate=w:c.next=w,C.lastBaseUpdate=d))}if(i!==null){var E=l.baseState;o=0,C=w=d=null,c=i;do{var x=c.lane,j=c.eventTime;if((r&x)===x){C!==null&&(C=C.next={eventTime:j,lane:0,tag:c.tag,payload:c.payload,callback:c.callback,next:null});e:{var O=e,R=c;switch(x=t,j=n,R.tag){case 1:if(O=R.payload,typeof O=="function"){E=O.call(j,E,x);break e}E=O;break e;case 3:O.flags=O.flags&-65537|128;case 0:if(O=R.payload,x=typeof O=="function"?O.call(j,E,x):O,x==null)break e;E=P({},E,x);break e;case 2:Yt=!0}}c.callback!==null&&c.lane!==0&&(e.flags|=64,x=l.effects,x===null?l.effects=[c]:x.push(c))}else j={eventTime:j,lane:x,tag:c.tag,payload:c.payload,callback:c.callback,next:null},C===null?(w=C=j,d=E):C=C.next=j,o|=x;if(c=c.next,c===null){if(c=l.shared.pending,c===null)break;x=c,c=x.next,x.next=null,l.lastBaseUpdate=x,l.shared.pending=null}}while(!0);if(C===null&&(d=E),l.baseState=d,l.firstBaseUpdate=w,l.lastBaseUpdate=C,t=l.shared.interleaved,t!==null){l=t;do o|=l.lane,l=l.next;while(l!==t)}else i===null&&(l.shared.lanes=0);dn|=o,e.lanes=o,e.memoizedState=E}}function Zs(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=pu.transition;pu.transition={};try{e(!1),t()}finally{ue=n,pu.transition=r}}function ha(){return ot().memoizedState}function ad(e,t,n){var r=qt(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},va(e))ya(t,n);else if(n=Ys(e,t,n,r),n!==null){var l=Ue();wt(n,e,r,l),ga(n,t,r)}}function cd(e,t,n){var r=qt(e),l={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(va(e))ya(t,l);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var o=t.lastRenderedState,c=i(o,n);if(l.hasEagerState=!0,l.eagerState=c,ht(c,o)){var d=t.interleaved;d===null?(l.next=l,ou(t)):(l.next=d.next,d.next=l),t.interleaved=l;return}}catch{}finally{}n=Ys(e,t,l,r),n!==null&&(l=Ue(),wt(n,e,r,l),ga(n,t,r))}}function va(e){var t=e.alternate;return e===ve||t!==null&&t===ve}function ya(e,t){Cr=El=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function ga(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,ki(e,n)}}var Ml={readContext:ut,useCallback:Oe,useContext:Oe,useEffect:Oe,useImperativeHandle:Oe,useInsertionEffect:Oe,useLayoutEffect:Oe,useMemo:Oe,useReducer:Oe,useRef:Oe,useState:Oe,useDebugValue:Oe,useDeferredValue:Oe,useTransition:Oe,useMutableSource:Oe,useSyncExternalStore:Oe,useId:Oe,unstable_isNewReconciler:!1},fd={readContext:ut,useCallback:function(e,t){return Nt().memoizedState=[e,t===void 0?null:t],e},useContext:ut,useEffect:oa,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Nl(4194308,4,ca.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Nl(4194308,4,e,t)},useInsertionEffect:function(e,t){return Nl(4,2,e,t)},useMemo:function(e,t){var n=Nt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Nt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ad.bind(null,ve,e),[r.memoizedState,e]},useRef:function(e){var t=Nt();return e={current:e},t.memoizedState=e},useState:ia,useDebugValue:wu,useDeferredValue:function(e){return Nt().memoizedState=e},useTransition:function(){var e=ia(!1),t=e[0];return e=sd.bind(null,e[1]),Nt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=ve,l=Nt();if(pe){if(n===void 0)throw Error(s(407));n=n()}else{if(n=t(),Me===null)throw Error(s(349));(fn&30)!==0||ea(r,t,n)}l.memoizedState=n;var i={value:n,getSnapshot:t};return l.queue=i,oa(na.bind(null,r,i,e),[e]),r.flags|=2048,Lr(9,ta.bind(null,r,i,n,t),void 0,null),n},useId:function(){var e=Nt(),t=Me.identifierPrefix;if(pe){var n=zt,r=It;n=(r&~(1<<32-mt(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=Er++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=o.createElement(n,{is:r.is}):(e=o.createElement(n),n==="select"&&(o=e,r.multiple?o.multiple=!0:r.size&&(o.size=r.size))):e=o.createElementNS(e,n),e[Ct]=t,e[gr]=r,Aa(e,t,!1,!1),t.stateNode=e;e:{switch(o=di(n,r),n){case"dialog":ae("cancel",e),ae("close",e),l=r;break;case"iframe":case"object":case"embed":ae("load",e),l=r;break;case"video":case"audio":for(l=0;lVn&&(t.flags|=128,r=!0,Mr(i,!1),t.lanes=4194304)}else{if(!r)if(e=xl(o),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Mr(i,!0),i.tail===null&&i.tailMode==="hidden"&&!o.alternate&&!pe)return Re(t),null}else 2*_e()-i.renderingStartTime>Vn&&n!==1073741824&&(t.flags|=128,r=!0,Mr(i,!1),t.lanes=4194304);i.isBackwards?(o.sibling=t.child,t.child=o):(n=i.last,n!==null?n.sibling=o:t.child=o,i.last=o)}return i.tail!==null?(t=i.tail,i.rendering=t,i.tail=t.sibling,i.renderingStartTime=_e(),t.sibling=null,n=he.current,se(he,r?n&1|2:n&1),t):(Re(t),null);case 22:case 23:return Vu(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(tt&1073741824)!==0&&(Re(t),t.subtreeFlags&6&&(t.flags|=8192)):Re(t),null;case 24:return null;case 25:return null}throw Error(s(156,t.tag))}function _d(e,t){switch(bi(t),t.tag){case 1:return Ve(t.type)&&dl(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Bn(),ce(We),ce(Te),du(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return cu(t),null;case 13:if(ce(he),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(s(340));Fn()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return ce(he),null;case 4:return Bn(),null;case 10:return iu(t.type._context),null;case 22:case 23:return Vu(),null;case 24:return null;default:return null}}var zl=!1,Fe=!1,wd=typeof WeakSet=="function"?WeakSet:Set,T=null;function Hn(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ye(e,t,r)}else n.current=null}function zu(e,t,n){try{n()}catch(r){ye(e,t,r)}}var $a=!1;function Sd(e,t){if(Vi=qr,e=_s(),Fi(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var l=r.anchorOffset,i=r.focusNode;r=r.focusOffset;try{n.nodeType,i.nodeType}catch{n=null;break e}var o=0,c=-1,d=-1,w=0,C=0,E=e,x=null;t:for(;;){for(var j;E!==n||l!==0&&E.nodeType!==3||(c=o+l),E!==i||r!==0&&E.nodeType!==3||(d=o+r),E.nodeType===3&&(o+=E.nodeValue.length),(j=E.firstChild)!==null;)x=E,E=j;for(;;){if(E===e)break t;if(x===n&&++w===l&&(c=o),x===i&&++C===r&&(d=o),(j=E.nextSibling)!==null)break;E=x,x=E.parentNode}E=j}n=c===-1||d===-1?null:{start:c,end:d}}else n=null}n=n||{start:0,end:0}}else n=null;for(Qi={focusedElem:e,selectionRange:n},qr=!1,T=t;T!==null;)if(t=T,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,T=e;else for(;T!==null;){t=T;try{var O=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(O!==null){var R=O.memoizedProps,we=O.memoizedState,v=t.stateNode,m=v.getSnapshotBeforeUpdate(t.elementType===t.type?R:yt(t.type,R),we);v.__reactInternalSnapshotBeforeUpdate=m}break;case 3:var _=t.stateNode.containerInfo;_.nodeType===1?_.textContent="":_.nodeType===9&&_.documentElement&&_.removeChild(_.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(s(163))}}catch(N){ye(t,t.return,N)}if(e=t.sibling,e!==null){e.return=t.return,T=e;break}T=t.return}return O=$a,$a=!1,O}function Pr(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var l=r=r.next;do{if((l.tag&e)===e){var i=l.destroy;l.destroy=void 0,i!==void 0&&zu(t,n,i)}l=l.next}while(l!==r)}}function Tl(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function Tu(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function Ha(e){var t=e.alternate;t!==null&&(e.alternate=null,Ha(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[Ct],delete t[gr],delete t[Gi],delete t[rd],delete t[ld])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function Wa(e){return e.tag===5||e.tag===3||e.tag===4}function Va(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Wa(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ou(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=cl));else if(r!==4&&(e=e.child,e!==null))for(Ou(e,t,n),e=e.sibling;e!==null;)Ou(e,t,n),e=e.sibling}function Ru(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ru(e,t,n),e=e.sibling;e!==null;)Ru(e,t,n),e=e.sibling}var je=null,gt=!1;function Gt(e,t,n){for(n=n.child;n!==null;)Qa(e,t,n),n=n.sibling}function Qa(e,t,n){if(xt&&typeof xt.onCommitFiberUnmount=="function")try{xt.onCommitFiberUnmount(Xr,n)}catch{}switch(n.tag){case 5:Fe||Hn(n,t);case 6:var r=je,l=gt;je=null,Gt(e,t,n),je=r,gt=l,je!==null&&(gt?(e=je,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):je.removeChild(n.stateNode));break;case 18:je!==null&&(gt?(e=je,n=n.stateNode,e.nodeType===8?Ki(e.parentNode,n):e.nodeType===1&&Ki(e,n),or(e)):Ki(je,n.stateNode));break;case 4:r=je,l=gt,je=n.stateNode.containerInfo,gt=!0,Gt(e,t,n),je=r,gt=l;break;case 0:case 11:case 14:case 15:if(!Fe&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){l=r=r.next;do{var i=l,o=i.destroy;i=i.tag,o!==void 0&&((i&2)!==0||(i&4)!==0)&&zu(n,t,o),l=l.next}while(l!==r)}Gt(e,t,n);break;case 1:if(!Fe&&(Hn(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(c){ye(n,t,c)}Gt(e,t,n);break;case 21:Gt(e,t,n);break;case 22:n.mode&1?(Fe=(r=Fe)||n.memoizedState!==null,Gt(e,t,n),Fe=r):Gt(e,t,n);break;default:Gt(e,t,n)}}function Xa(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new wd),t.forEach(function(r){var l=jd.bind(null,e,r);n.has(r)||(n.add(r),r.then(l,l))})}}function _t(e,t){var n=t.deletions;if(n!==null)for(var r=0;rl&&(l=o),r&=~i}if(r=l,r=_e()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*xd(r/1960))-r,10e?16:e,Jt===null)var r=!1;else{if(e=Jt,Jt=null,Al=0,(te&6)!==0)throw Error(s(331));var l=te;for(te|=4,T=e.current;T!==null;){var i=T,o=i.child;if((T.flags&16)!==0){var c=i.deletions;if(c!==null){for(var d=0;d_e()-Au?mn(e,0):Du|=n),Ye(e,t)}function ic(e,t){t===0&&((e.mode&1)===0?t=1:(t=Kr,Kr<<=1,(Kr&130023424)===0&&(Kr=4194304)));var n=Ue();e=Tt(e,t),e!==null&&(nr(e,t,n),Ye(e,n))}function Pd(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),ic(e,n)}function jd(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,l=e.memoizedState;l!==null&&(n=l.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(s(314))}r!==null&&r.delete(t),ic(e,n)}var uc;uc=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||We.current)Qe=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Qe=!1,yd(e,t,n);Qe=(e.flags&131072)!==0}else Qe=!1,pe&&(t.flags&1048576)!==0&&Us(t,vl,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Il(e,t),e=t.pendingProps;var l=Tn(t,Te.current);Un(t,n),l=hu(null,t,r,e,l,n);var i=vu();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Ve(r)?(i=!0,pl(t)):i=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,su(t),l.updater=Pl,t.stateNode=l,l._reactInternals=t,ku(t,r,e,n),t=Nu(null,t,r,!0,i,n)):(t.tag=0,pe&&i&&qi(t),Ae(null,t,l,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Il(e,t),e=t.pendingProps,l=r._init,r=l(r._payload),t.type=r,l=t.tag=zd(r),e=yt(r,e),l){case 0:t=Eu(null,t,r,e,n);break e;case 1:t=za(null,t,r,e,n);break e;case 11:t=La(null,t,r,e,n);break e;case 14:t=Ma(null,t,r,yt(r.type,e),n);break e}throw Error(s(306,r,""))}return t;case 0:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:yt(r,l),Eu(e,t,r,l,n);case 1:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:yt(r,l),za(e,t,r,l,n);case 3:e:{if(Ta(t),e===null)throw Error(s(387));r=t.pendingProps,i=t.memoizedState,l=i.element,Ks(e,t),kl(t,r,null,n);var o=t.memoizedState;if(r=o.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:o.cache,pendingSuspenseBoundaries:o.pendingSuspenseBoundaries,transitions:o.transitions},t.updateQueue.baseState=i,t.memoizedState=i,t.flags&256){l=$n(Error(s(423)),t),t=Oa(e,t,r,n,l);break e}else if(r!==l){l=$n(Error(s(424)),t),t=Oa(e,t,r,n,l);break e}else for(et=Wt(t.stateNode.containerInfo.firstChild),be=t,pe=!0,vt=null,n=Xs(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Fn(),r===l){t=Rt(e,t,n);break e}Ae(e,t,r,n)}t=t.child}return t;case 5:return Js(t),e===null&&tu(t),r=t.type,l=t.pendingProps,i=e!==null?e.memoizedProps:null,o=l.children,Xi(r,l)?o=null:i!==null&&Xi(r,i)&&(t.flags|=32),Ia(e,t),Ae(e,t,o,n),t.child;case 6:return e===null&&tu(t),null;case 13:return Ra(e,t,n);case 4:return au(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=Dn(t,null,r,n):Ae(e,t,r,n),t.child;case 11:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:yt(r,l),La(e,t,r,l,n);case 7:return Ae(e,t,t.pendingProps,n),t.child;case 8:return Ae(e,t,t.pendingProps.children,n),t.child;case 12:return Ae(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,l=t.pendingProps,i=t.memoizedProps,o=l.value,se(_l,r._currentValue),r._currentValue=o,i!==null)if(ht(i.value,o)){if(i.children===l.children&&!We.current){t=Rt(e,t,n);break e}}else for(i=t.child,i!==null&&(i.return=t);i!==null;){var c=i.dependencies;if(c!==null){o=i.child;for(var d=c.firstContext;d!==null;){if(d.context===r){if(i.tag===1){d=Ot(-1,n&-n),d.tag=2;var w=i.updateQueue;if(w!==null){w=w.shared;var C=w.pending;C===null?d.next=d:(d.next=C.next,C.next=d),w.pending=d}}i.lanes|=n,d=i.alternate,d!==null&&(d.lanes|=n),uu(i.return,n,t),c.lanes|=n;break}d=d.next}}else if(i.tag===10)o=i.type===t.type?null:i.child;else if(i.tag===18){if(o=i.return,o===null)throw Error(s(341));o.lanes|=n,c=o.alternate,c!==null&&(c.lanes|=n),uu(o,n,t),o=i.sibling}else o=i.child;if(o!==null)o.return=i;else for(o=i;o!==null;){if(o===t){o=null;break}if(i=o.sibling,i!==null){i.return=o.return,o=i;break}o=o.return}i=o}Ae(e,t,l.children,n),t=t.child}return t;case 9:return l=t.type,r=t.pendingProps.children,Un(t,n),l=ut(l),r=r(l),t.flags|=1,Ae(e,t,r,n),t.child;case 14:return r=t.type,l=yt(r,t.pendingProps),l=yt(r.type,l),Ma(e,t,r,l,n);case 15:return Pa(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,l=t.pendingProps,l=t.elementType===r?l:yt(r,l),Il(e,t),t.tag=1,Ve(r)?(e=!0,pl(t)):e=!1,Un(t,n),wa(t,r,l),ku(t,r,l,n),Nu(null,t,r,!0,e,n);case 19:return Da(e,t,n);case 22:return ja(e,t,n)}throw Error(s(156,t.tag))};function oc(e,t){return Bo(e,t)}function Id(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function at(e,t,n,r){return new Id(e,t,n,r)}function Xu(e){return e=e.prototype,!(!e||!e.isReactComponent)}function zd(e){if(typeof e=="function")return Xu(e)?1:0;if(e!=null){if(e=e.$$typeof,e===He)return 11;if(e===Q)return 14}return 2}function en(e,t){var n=e.alternate;return n===null?(n=at(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Hl(e,t,n,r,l,i){var o=2;if(r=e,typeof e=="function")Xu(e)&&(o=1);else if(typeof e=="string")o=5;else e:switch(e){case Ne:return vn(n.children,l,i,t);case $e:o=8,l|=8;break;case rt:return e=at(12,n,t,l|2),e.elementType=rt,e.lanes=i,e;case De:return e=at(13,n,t,l),e.elementType=De,e.lanes=i,e;case I:return e=at(19,n,t,l),e.elementType=I,e.lanes=i,e;case K:return Wl(n,l,i,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case pt:o=10;break e;case kt:o=9;break e;case He:o=11;break e;case Q:o=14;break e;case le:o=16,r=null;break e}throw Error(s(130,e==null?e:typeof e,""))}return t=at(o,n,t,l),t.elementType=e,t.type=r,t.lanes=i,t}function vn(e,t,n,r){return e=at(7,e,r,t),e.lanes=n,e}function Wl(e,t,n,r){return e=at(22,e,r,t),e.elementType=K,e.lanes=n,e.stateNode={isHidden:!1},e}function Yu(e,t,n){return e=at(6,e,null,t),e.lanes=n,e}function Ku(e,t,n){return t=at(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Td(e,t,n,r,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Si(0),this.expirationTimes=Si(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Si(0),this.identifierPrefix=r,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function Gu(e,t,n,r,l,i,o,c,d){return e=new Td(e,t,n,c,d),t===1?(t=1,i===!0&&(t|=8)):t=0,i=at(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},su(i),e}function Od(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(a){console.error(a)}}return u(),to.exports=Xd(),to.exports}var Sc;function Kd(){if(Sc)return Jl;Sc=1;var u=Yd();return Jl.createRoot=u.createRoot,Jl.hydrateRoot=u.hydrateRoot,Jl}var Gd=Kd();const Zd="_app_1ibpl_1",Jd={app:Zd},kc=u=>{let a;const s=new Set,f=(U,ee)=>{const D=typeof U=="function"?U(a):U;if(!Object.is(D,a)){const H=a;a=ee??(typeof D!="object"||D===null)?D:Object.assign({},a,D),s.forEach(fe=>fe(a,H))}},p=()=>a,L={setState:f,getState:p,getInitialState:()=>z,subscribe:U=>(s.add(U),()=>s.delete(U))},z=a=u(f,p,L);return L},qd=(u=>u?kc(u):kc),bd=u=>u;function ep(u,a=bd){const s=Zl.useSyncExternalStore(u.subscribe,Zl.useCallback(()=>a(u.getState()),[u,a]),Zl.useCallback(()=>a(u.getInitialState()),[u,a]));return Zl.useDebugValue(s),s}const tp=u=>{const a=qd(u),s=f=>ep(a,f);return Object.assign(s,a),s},np=(u=>tp);var Dc=Symbol.for("immer-nothing"),xc=Symbol.for("immer-draftable"),nt=Symbol.for("immer-state");function St(u,...a){throw new Error(`[Immer] minified error nr: ${u}. Full error at: https://bit.ly/3cXEKWf`)}var Fr=Object.getPrototypeOf;function Kn(u){return!!u&&!!u[nt]}function gn(u){var a;return u?Ac(u)||Array.isArray(u)||!!u[xc]||!!((a=u.constructor)!=null&&a[xc])||Ur(u)||li(u):!1}var rp=Object.prototype.constructor.toString(),Cc=new WeakMap;function Ac(u){if(!u||typeof u!="object")return!1;const a=Object.getPrototypeOf(u);if(a===null||a===Object.prototype)return!0;const s=Object.hasOwnProperty.call(a,"constructor")&&a.constructor;if(s===Object)return!0;if(typeof s!="function")return!1;let f=Cc.get(s);return f===void 0&&(f=Function.toString.call(s),Cc.set(s,f)),f===rp}function bl(u,a,s=!0){ri(u)===0?(s?Reflect.ownKeys(u):Object.keys(u)).forEach(p=>{a(p,u[p],u)}):u.forEach((f,p)=>a(p,f,u))}function ri(u){const a=u[nt];return a?a.type_:Array.isArray(u)?1:Ur(u)?2:li(u)?3:0}function ao(u,a){return ri(u)===2?u.has(a):Object.prototype.hasOwnProperty.call(u,a)}function Uc(u,a,s){const f=ri(u);f===2?u.set(a,s):f===3?u.add(s):u[a]=s}function lp(u,a){return u===a?u!==0||1/u===1/a:u!==u&&a!==a}function Ur(u){return u instanceof Map}function li(u){return u instanceof Set}function yn(u){return u.copy_||u.base_}function co(u,a){if(Ur(u))return new Map(u);if(li(u))return new Set(u);if(Array.isArray(u))return Array.prototype.slice.call(u);const s=Ac(u);if(a===!0||a==="class_only"&&!s){const f=Object.getOwnPropertyDescriptors(u);delete f[nt];let p=Reflect.ownKeys(f);for(let g=0;g1&&Object.defineProperties(u,{set:ql,add:ql,clear:ql,delete:ql}),Object.freeze(u),a&&Object.values(u).forEach(s=>yo(s,!0))),u}function ip(){St(2)}var ql={value:ip};function ii(u){return u===null||typeof u!="object"?!0:Object.isFrozen(u)}var up={};function _n(u){const a=up[u];return a||St(0,u),a}var Dr;function Bc(){return Dr}function op(u,a){return{drafts_:[],parent_:u,immer_:a,canAutoFreeze_:!0,unfinalizedDrafts_:0}}function Ec(u,a){a&&(_n("Patches"),u.patches_=[],u.inversePatches_=[],u.patchListener_=a)}function fo(u){po(u),u.drafts_.forEach(sp),u.drafts_=null}function po(u){u===Dr&&(Dr=u.parent_)}function Nc(u){return Dr=op(Dr,u)}function sp(u){const a=u[nt];a.type_===0||a.type_===1?a.revoke_():a.revoked_=!0}function Lc(u,a){a.unfinalizedDrafts_=a.drafts_.length;const s=a.drafts_[0];return u!==void 0&&u!==s?(s[nt].modified_&&(fo(a),St(4)),gn(u)&&(u=ei(a,u),a.parent_||ti(a,u)),a.patches_&&_n("Patches").generateReplacementPatches_(s[nt].base_,u,a.patches_,a.inversePatches_)):u=ei(a,s,[]),fo(a),a.patches_&&a.patchListener_(a.patches_,a.inversePatches_),u!==Dc?u:void 0}function ei(u,a,s){if(ii(a))return a;const f=u.immer_.shouldUseStrictIteration(),p=a[nt];if(!p)return bl(a,(g,S)=>Mc(u,p,a,g,S,s),f),a;if(p.scope_!==u)return a;if(!p.modified_)return ti(u,p.base_,!0),p.base_;if(!p.finalized_){p.finalized_=!0,p.scope_.unfinalizedDrafts_--;const g=p.copy_;let S=g,L=!1;p.type_===3&&(S=new Set(g),g.clear(),L=!0),bl(S,(z,U)=>Mc(u,p,g,z,U,s,L),f),ti(u,g,!1),s&&u.patches_&&_n("Patches").generatePatches_(p,s,u.patches_,u.inversePatches_)}return p.copy_}function Mc(u,a,s,f,p,g,S){if(p==null||typeof p!="object"&&!S)return;const L=ii(p);if(!(L&&!S)){if(Kn(p)){const z=g&&a&&a.type_!==3&&!ao(a.assigned_,f)?g.concat(f):void 0,U=ei(u,p,z);if(Uc(s,f,U),Kn(U))u.canAutoFreeze_=!1;else return}else S&&s.add(p);if(gn(p)&&!L){if(!u.immer_.autoFreeze_&&u.unfinalizedDrafts_<1||a&&a.base_&&a.base_[f]===p&&L)return;ei(u,p),(!a||!a.scope_.parent_)&&typeof f!="symbol"&&(Ur(s)?s.has(f):Object.prototype.propertyIsEnumerable.call(s,f))&&ti(u,p)}}}function ti(u,a,s=!1){!u.parent_&&u.immer_.autoFreeze_&&u.canAutoFreeze_&&yo(a,s)}function ap(u,a){const s=Array.isArray(u),f={type_:s?1:0,scope_:a?a.scope_:Bc(),modified_:!1,finalized_:!1,assigned_:{},parent_:a,base_:u,draft_:null,copy_:null,revoke_:null,isManual_:!1};let p=f,g=go;s&&(p=[f],g=Ar);const{revoke:S,proxy:L}=Proxy.revocable(p,g);return f.draft_=L,f.revoke_=S,L}var go={get(u,a){if(a===nt)return u;const s=yn(u);if(!ao(s,a))return cp(u,s,a);const f=s[a];return u.finalized_||!gn(f)?f:f===lo(u.base_,a)?(io(u),u.copy_[a]=ho(f,u)):f},has(u,a){return a in yn(u)},ownKeys(u){return Reflect.ownKeys(yn(u))},set(u,a,s){const f=$c(yn(u),a);if(f!=null&&f.set)return f.set.call(u.draft_,s),!0;if(!u.modified_){const p=lo(yn(u),a),g=p==null?void 0:p[nt];if(g&&g.base_===s)return u.copy_[a]=s,u.assigned_[a]=!1,!0;if(lp(s,p)&&(s!==void 0||ao(u.base_,a)))return!0;io(u),mo(u)}return u.copy_[a]===s&&(s!==void 0||a in u.copy_)||Number.isNaN(s)&&Number.isNaN(u.copy_[a])||(u.copy_[a]=s,u.assigned_[a]=!0),!0},deleteProperty(u,a){return lo(u.base_,a)!==void 0||a in u.base_?(u.assigned_[a]=!1,io(u),mo(u)):delete u.assigned_[a],u.copy_&&delete u.copy_[a],!0},getOwnPropertyDescriptor(u,a){const s=yn(u),f=Reflect.getOwnPropertyDescriptor(s,a);return f&&{writable:!0,configurable:u.type_!==1||a!=="length",enumerable:f.enumerable,value:s[a]}},defineProperty(){St(11)},getPrototypeOf(u){return Fr(u.base_)},setPrototypeOf(){St(12)}},Ar={};bl(go,(u,a)=>{Ar[u]=function(){return arguments[0]=arguments[0][0],a.apply(this,arguments)}});Ar.deleteProperty=function(u,a){return Ar.set.call(this,u,a,void 0)};Ar.set=function(u,a,s){return go.set.call(this,u[0],a,s,u[0])};function lo(u,a){const s=u[nt];return(s?yn(s):u)[a]}function cp(u,a,s){var p;const f=$c(a,s);return f?"value"in f?f.value:(p=f.get)==null?void 0:p.call(u.draft_):void 0}function $c(u,a){if(!(a in u))return;let s=Fr(u);for(;s;){const f=Object.getOwnPropertyDescriptor(s,a);if(f)return f;s=Fr(s)}}function mo(u){u.modified_||(u.modified_=!0,u.parent_&&mo(u.parent_))}function io(u){u.copy_||(u.copy_=co(u.base_,u.scope_.immer_.useStrictShallowCopy_))}var fp=class{constructor(u){this.autoFreeze_=!0,this.useStrictShallowCopy_=!1,this.useStrictIteration_=!0,this.produce=(a,s,f)=>{if(typeof a=="function"&&typeof s!="function"){const g=s;s=a;const S=this;return function(z=g,...U){return S.produce(z,ee=>s.call(this,ee,...U))}}typeof s!="function"&&St(6),f!==void 0&&typeof f!="function"&&St(7);let p;if(gn(a)){const g=Nc(this),S=ho(a,void 0);let L=!0;try{p=s(S),L=!1}finally{L?fo(g):po(g)}return Ec(g,f),Lc(p,g)}else if(!a||typeof a!="object"){if(p=s(a),p===void 0&&(p=a),p===Dc&&(p=void 0),this.autoFreeze_&&yo(p,!0),f){const g=[],S=[];_n("Patches").generateReplacementPatches_(a,p,g,S),f(g,S)}return p}else St(1,a)},this.produceWithPatches=(a,s)=>{if(typeof a=="function")return(S,...L)=>this.produceWithPatches(S,z=>a(z,...L));let f,p;return[this.produce(a,s,(S,L)=>{f=S,p=L}),f,p]},typeof(u==null?void 0:u.autoFreeze)=="boolean"&&this.setAutoFreeze(u.autoFreeze),typeof(u==null?void 0:u.useStrictShallowCopy)=="boolean"&&this.setUseStrictShallowCopy(u.useStrictShallowCopy),typeof(u==null?void 0:u.useStrictIteration)=="boolean"&&this.setUseStrictIteration(u.useStrictIteration)}createDraft(u){gn(u)||St(8),Kn(u)&&(u=dp(u));const a=Nc(this),s=ho(u,void 0);return s[nt].isManual_=!0,po(a),s}finishDraft(u,a){const s=u&&u[nt];(!s||!s.isManual_)&&St(9);const{scope_:f}=s;return Ec(f,a),Lc(void 0,f)}setAutoFreeze(u){this.autoFreeze_=u}setUseStrictShallowCopy(u){this.useStrictShallowCopy_=u}setUseStrictIteration(u){this.useStrictIteration_=u}shouldUseStrictIteration(){return this.useStrictIteration_}applyPatches(u,a){let s;for(s=a.length-1;s>=0;s--){const p=a[s];if(p.path.length===0&&p.op==="replace"){u=p.value;break}}s>-1&&(a=a.slice(s+1));const f=_n("Patches").applyPatches_;return Kn(u)?f(u,a):this.produce(u,p=>f(p,a))}};function ho(u,a){const s=Ur(u)?_n("MapSet").proxyMap_(u,a):li(u)?_n("MapSet").proxySet_(u,a):ap(u,a);return(a?a.scope_:Bc()).drafts_.push(s),s}function dp(u){return Kn(u)||St(10,u),Hc(u)}function Hc(u){if(!gn(u)||ii(u))return u;const a=u[nt];let s,f=!0;if(a){if(!a.modified_)return a.base_;a.finalized_=!0,s=co(u,a.scope_.immer_.useStrictShallowCopy_),f=a.scope_.immer_.shouldUseStrictIteration()}else s=co(u,!0);return bl(s,(p,g)=>{Uc(s,p,Hc(g))},f),a&&(a.finalized_=!1),s}var pp=new fp,mp=pp.produce;const hp=u=>(a,s,f)=>(f.setState=(p,g,...S)=>{const L=typeof p=="function"?mp(p):p;return a(L,g,...S)},u(f.setState,s,f)),vp=hp;function yp(u){return{id:u,skippable:!0,durationMs:5e3,fadeOutMs:500,fadeInMs:500,endFadeOutMs:500,endFadeInMs:500,onFadeInCallback:"",imageSegments:[],lines:[]}}function gp(){return{path:"",width:1280,height:720,startMs:0,endMs:5e3,fadeInMs:0,fadeOutMs:0,easing:"Linear",from:{centerX:.5,centerY:.5,scale:1},to:{centerX:.5,centerY:.5,scale:1}}}function _p(){return{speaker:"",text:"",durationMs:3e3,waitForConfirm:!1,luaCallback:""}}function ct(u,a){return!u||!a?null:u.cutscenes.find(s=>s.id===a)??null}const dt=np()(vp((u,a)=>({file:null,selectedCutsceneId:null,selectedLayerIndex:null,playState:"stopped",currentTimeMs:0,loadFile:s=>u(f=>{var p;f.file=s,f.selectedCutsceneId=((p=s.cutscenes[0])==null?void 0:p.id)??null,f.selectedLayerIndex=null,f.playState="stopped",f.currentTimeMs=0}),getExportData:()=>a().file,addCutscene:()=>u(s=>{s.file||(s.file={cutscenes:[]});let f="cutscene_new",p=1;const g=new Set(s.file.cutscenes.map(L=>L.id));for(;g.has(`${f}_${p}`);)p++;const S=`${f}_${p}`;s.file.cutscenes.push(yp(S)),s.selectedCutsceneId=S,s.selectedLayerIndex=null}),deleteCutscene:s=>u(f=>{var g;if(!f.file)return;const p=f.file.cutscenes.findIndex(S=>S.id===s);p!==-1&&(f.file.cutscenes.splice(p,1),f.selectedCutsceneId===s&&(f.selectedCutsceneId=((g=f.file.cutscenes[0])==null?void 0:g.id)??null,f.selectedLayerIndex=null))}),selectCutscene:s=>u(f=>{f.selectedCutsceneId=s,f.selectedLayerIndex=null,f.playState="stopped",f.currentTimeMs=0}),updateCutscene:(s,f)=>u(p=>{if(!p.file)return;const g=p.file.cutscenes.find(S=>S.id===s);g&&Object.assign(g,f)}),selectLayer:s=>u(f=>{f.selectedLayerIndex=s}),addLayer:()=>u(s=>{const f=ct(s.file,s.selectedCutsceneId);f&&(f.imageSegments.push(gp()),s.selectedLayerIndex=f.imageSegments.length-1)}),removeLayer:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);p&&(p.imageSegments.splice(s,1),f.selectedLayerIndex===s?f.selectedLayerIndex=null:f.selectedLayerIndex!==null&&f.selectedLayerIndex>s&&f.selectedLayerIndex--)}),moveLayerUp:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);if(!p||s<=0)return;const g=p.imageSegments;[g[s-1],g[s]]=[g[s],g[s-1]],f.selectedLayerIndex===s?f.selectedLayerIndex=s-1:f.selectedLayerIndex===s-1&&(f.selectedLayerIndex=s)}),moveLayerDown:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);if(!p)return;const g=p.imageSegments;s>=g.length-1||([g[s],g[s+1]]=[g[s+1],g[s]],f.selectedLayerIndex===s?f.selectedLayerIndex=s+1:f.selectedLayerIndex===s+1&&(f.selectedLayerIndex=s))}),updateLayer:(s,f)=>u(p=>{const g=ct(p.file,p.selectedCutsceneId);g&&Object.assign(g.imageSegments[s],f)}),updateLayerFrom:(s,f)=>u(p=>{const g=ct(p.file,p.selectedCutsceneId);g&&Object.assign(g.imageSegments[s].from,f)}),updateLayerTo:(s,f)=>u(p=>{const g=ct(p.file,p.selectedCutsceneId);g&&Object.assign(g.imageSegments[s].to,f)}),addLine:()=>u(s=>{const f=ct(s.file,s.selectedCutsceneId);f&&f.lines.push(_p())}),removeLine:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);p&&p.lines.splice(s,1)}),moveLineUp:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);if(!p||s<=0)return;const g=p.lines;[g[s-1],g[s]]=[g[s],g[s-1]]}),moveLineDown:s=>u(f=>{const p=ct(f.file,f.selectedCutsceneId);if(!p)return;const g=p.lines;s>=g.length-1||([g[s],g[s+1]]=[g[s+1],g[s]])}),updateLine:(s,f)=>u(p=>{const g=ct(p.file,p.selectedCutsceneId);g&&Object.assign(g.lines[s],f)}),setPlayState:s=>u(f=>{f.playState=s}),setCurrentTime:s=>u(f=>{f.currentTimeMs=s})})));function wn(){return dt(u=>{var a;return((a=u.file)==null?void 0:a.cutscenes.find(s=>s.id===u.selectedCutsceneId))??null})}function wp(u){const a=u.from??{},s=u.to??{};return{path:String(u.path??""),width:Number(u.width??1280),height:Number(u.height??720),startMs:Number(u.startMs??0),endMs:Number(u.endMs??5e3),fadeInMs:Number(u.fadeInMs??0),fadeOutMs:Number(u.fadeOutMs??0),easing:String(u.easing??"Linear"),from:{centerX:Number(a.centerX??.5),centerY:Number(a.centerY??.5),scale:Number(a.scale??1)},to:{centerX:Number(s.centerX??a.centerX??.5),centerY:Number(s.centerY??a.centerY??.5),scale:Number(s.scale??a.scale??1)}}}function Sp(u){return{speaker:String(u.speaker??""),text:String(u.text??""),durationMs:Number(u.durationMs??0),waitForConfirm:!!(u.waitForConfirm??!1),luaCallback:String(u.luaCallback??"")}}function kp(u){const a=Array.isArray(u.imageSegments)?u.imageSegments.map(wp):[],s=Array.isArray(u.lines)?u.lines.map(Sp):[];return{id:String(u.id??"untitled"),skippable:u.skippable!==!1,durationMs:Number(u.durationMs??0),fadeOutMs:Number(u.fadeOutMs??0),fadeInMs:Number(u.fadeInMs??0),endFadeOutMs:Number(u.endFadeOutMs??0),endFadeInMs:Number(u.endFadeInMs??0),onFadeInCallback:String(u.onFadeInCallback??""),imageSegments:a,lines:s}}function xp(u){const a=u;return{cutscenes:Array.isArray(a.cutscenes)?a.cutscenes.map(kp):[]}}function Cp(u,a="cutscenes.json"){const s=JSON.stringify(u,null,4),f=new Blob([s],{type:"application/json"}),p=URL.createObjectURL(f),g=document.createElement("a");g.href=p,g.download=a,g.click(),URL.revokeObjectURL(p)}const Ep="_panel_e520h_1",Np="_header_e520h_11",Lp="_actions_e520h_21",Mp="_btn_e520h_29",Pp="_btnPrimary_e520h_42 _btn_e520h_29",jp="_list_e520h_50",Ip="_empty_e520h_56",zp="_item_e520h_63",Tp="_selected_e520h_76",Op="_itemId_e520h_81",Rp="_deleteBtn_e520h_89",ft={panel:Ep,header:Np,actions:Lp,btn:Mp,btnPrimary:Pp,list:jp,empty:Ip,item:zp,selected:Tp,itemId:Op,deleteBtn:Rp};function Fp(){const u=ze.useRef(null),{file:a,selectedCutsceneId:s,loadFile:f,addCutscene:p,deleteCutscene:g,selectCutscene:S,getExportData:L}=dt();function z(D){var me;const H=(me=D.target.files)==null?void 0:me[0];if(!H)return;const fe=new FileReader;fe.onload=re=>{try{const q=JSON.parse(re.target.result);f(xp(q))}catch{alert("Invalid JSON file")}},fe.readAsText(H),D.target.value=""}function U(){const D=L();D&&Cp(D)}function ee(D){confirm(`Delete cutscene "${D}"?`)&&g(D)}return y.jsxs("div",{className:ft.panel,children:[y.jsx("div",{className:ft.header,children:"Cutscenes"}),y.jsxs("div",{className:ft.actions,children:[y.jsx("button",{className:ft.btnPrimary,onClick:()=>{var D;return(D=u.current)==null?void 0:D.click()},children:"Load JSON"}),y.jsx("input",{ref:u,type:"file",accept:".json",style:{display:"none"},onChange:z}),y.jsx("button",{className:ft.btn,onClick:U,disabled:!a,children:"Save JSON"}),y.jsx("button",{className:ft.btn,onClick:p,children:"+ New"})]}),y.jsx("div",{className:ft.list,children:!a||a.cutscenes.length===0?y.jsx("div",{className:ft.empty,children:"No cutscenes. Load a JSON or create new."}):a.cutscenes.map(D=>y.jsxs("div",{className:`${ft.item} ${D.id===s?ft.selected:""}`,onClick:()=>S(D.id),children:[y.jsx("span",{className:ft.itemId,children:D.id}),y.jsx("button",{className:ft.deleteBtn,onClick:H=>{H.stopPropagation(),ee(D.id)},title:"Delete",children:"✕"})]},D.id))})]})}const Dp="_panel_1kkcd_1",Ap="_previewArea_1kkcd_9",Pc={panel:Dp,previewArea:Ap},Up=["Linear","EaseInSine","EaseOutSine","EaseInOutSine","EaseInQuad","EaseOutQuad","EaseInOutQuad","EaseInCubic","EaseOutCubic","EaseInOutCubic"];function Bp(u,a){const s=Math.PI;switch(a){case"Linear":return u;case"EaseInSine":return 1-Math.cos(u*s/2);case"EaseOutSine":return Math.sin(u*s/2);case"EaseInOutSine":return-(Math.cos(s*u)-1)/2;case"EaseInQuad":return u*u;case"EaseOutQuad":return 1-(1-u)*(1-u);case"EaseInOutQuad":return u<.5?2*u*u:1-Math.pow(-2*u+2,2)/2;case"EaseInCubic":return u*u*u;case"EaseOutCubic":return 1-Math.pow(1-u,3);case"EaseInOutCubic":return u<.5?4*u*u*u:1-Math.pow(-2*u+2,3)/2;default:return u}}function ni(u,a,s){return Math.max(a,Math.min(s,u))}function uo(u,a,s){return u+(a-u)*s}function $p(u,a){if(au.endMs)return null;const s=u.endMs-u.startMs,f=a-u.startMs,p=s>0?ni(f/s,0,1):1,g=Bp(p,u.easing),S={centerX:uo(u.from.centerX,u.to.centerX,g),centerY:uo(u.from.centerY,u.to.centerY,g),scale:uo(u.from.scale,u.to.scale,g)};let L=1;return u.fadeInMs>0&&f0&&f>s-u.fadeOutMs&&(L=(s-f)/u.fadeOutMs),{alpha:ni(L,0,1),pose:S}}function Hp(u,a,s,f,p){const g=f/a,S=p/s,L=Math.max(g,S),z=a*L*u.scale,U=s*L*u.scale,ee=(z-f)/2,D=(U-p)/2,H=(.5-u.centerX)*z,fe=(.5-u.centerY)*U,me=ni(H,-ee,ee),re=ni(fe,-D,D);return{position:"absolute",top:"50%",left:"50%",width:z,height:U,transform:`translate(calc(-50% + ${me}px), calc(-50% + ${re}px))`,objectFit:"cover"}}const Wp="_wrapper_1nucj_1",Vp="_viewport_1nucj_11",Qp="_empty_1nucj_20",Xp="_subtitleBar_1nucj_30",Yp="_speaker_1nucj_40",Kp="_text_1nucj_48",Xn={wrapper:Wp,viewport:Vp,empty:Qp,subtitleBar:Xp,speaker:Yp,text:Kp},jc=1280,Ic=720;function Gp(u,a){if(!u)return null;let s=0;for(const f of u.lines){const p=f.durationMs>0?f.durationMs:Math.max(1500,Math.round(f.text.length/17*1e3));if(a>=s&&aS.currentTimeMs),p=wn();ze.useEffect(()=>{const S=u.current;if(!S)return;const L=new ResizeObserver(z=>{const U=z[0];U&&s({w:U.contentRect.width,h:U.contentRect.height})});return L.observe(S),()=>L.disconnect()},[]);const g=Gp(p,f);return y.jsx("div",{className:Xn.wrapper,children:y.jsx("div",{className:Xn.viewport,ref:u,children:p?y.jsxs(y.Fragment,{children:[p.imageSegments.map((S,L)=>{const z=$p(S,f);if(!z)return null;const U=Hp(z.pose,S.width||jc,S.height||Ic,a.w,a.h);return y.jsx("img",{src:`/${S.path}`,alt:"",style:{...U,opacity:z.alpha},draggable:!1},L)}),g&&y.jsxs("div",{className:Xn.subtitleBar,children:[g.speaker&&y.jsx("div",{className:Xn.speaker,children:g.speaker}),y.jsx("div",{className:Xn.text,children:g.text})]})]}):y.jsx("div",{className:Xn.empty,children:"Select or create a cutscene"})})})}function Jp(){const{playState:u,currentTimeMs:a,setPlayState:s,setCurrentTime:f}=dt(),p=wn(),g=ze.useRef(null),S=ze.useRef(null);return ze.useEffect(()=>{if(u!=="playing"){S.current=null,g.current!==null&&(cancelAnimationFrame(g.current),g.current=null);return}function L(){if(!p)return 5e3;const U=p.imageSegments.reduce((D,H)=>Math.max(D,H.endMs),0);return Math.max(p.durationMs,U)+p.endFadeOutMs+p.endFadeInMs}function z(U){S.current===null&&(S.current=U);const ee=U-S.current;S.current=U;const D=dt.getState().currentTimeMs+ee,H=L();if(D>=H){f(H),s("stopped");return}f(D),g.current=requestAnimationFrame(z)}return g.current=requestAnimationFrame(z),()=>{g.current!==null&&cancelAnimationFrame(g.current)}},[u,p,s,f]),null}const qp="_controls_1qoyb_1",bp="_btn_1qoyb_12",em="_active_1qoyb_26",tm="_time_1qoyb_31",nn={controls:qp,btn:bp,active:em,time:tm};function zc(u){const a=Math.floor(u/1e3),s=Math.floor(u%1e3/10).toString().padStart(2,"0");return`${a}.${s}s`}function nm(){Jp();const{playState:u,currentTimeMs:a,setPlayState:s,setCurrentTime:f}=dt(),p=wn(),g=p?Math.max(p.durationMs,p.imageSegments.reduce((U,ee)=>Math.max(U,ee.endMs),0))+p.endFadeOutMs+p.endFadeInMs:0;function S(){(u==="stopped"||a>=g)&&f(0),s("playing")}function L(){s("paused")}function z(){s("stopped"),f(0)}return y.jsxs("div",{className:nn.controls,children:[y.jsx("button",{className:nn.btn,onClick:z,title:"Stop",disabled:!p,children:"⏹"}),y.jsx("button",{className:nn.btn,onClick:()=>f(0),title:"Rewind",disabled:!p,children:"⏮"}),u==="playing"?y.jsx("button",{className:`${nn.btn} ${nn.active}`,onClick:L,title:"Pause",disabled:!p,children:"⏸"}):y.jsx("button",{className:`${nn.btn} ${nn.active}`,onClick:S,title:"Play",disabled:!p,children:"▶"}),y.jsxs("div",{className:nn.time,children:[zc(a)," / ",zc(g)]})]})}const rm="_container_uetgi_1",lm="_toolbar_uetgi_11",im="_tbBtn_uetgi_21",um="_spacer_uetgi_33",om="_zoomLabel_uetgi_35",sm="_scroll_uetgi_37",am="_ruler_uetgi_45",cm="_tick_uetgi_57",fm="_tickLabel_uetgi_65",dm="_layersArea_uetgi_75",pm="_playhead_uetgi_79",mm="_gridLine_uetgi_89",hm="_row_uetgi_99",vm="_rowSelected_uetgi_111",ym="_rowLabel_uetgi_113",gm="_layerName_uetgi_128",_m="_bar_uetgi_137",wm="_handle_uetgi_149",Sm="_handleLeft_uetgi_157",km="_handleRight_uetgi_158",xm="_subtitleRow_uetgi_161",Cm="_subtitleLabel_uetgi_169",Em="_subtitleBlock_uetgi_184",ie={container:rm,toolbar:lm,tbBtn:im,spacer:um,zoomLabel:om,scroll:sm,ruler:am,tick:cm,tickLabel:fm,layersArea:dm,playhead:pm,gridLine:mm,row:hm,rowSelected:vm,rowLabel:ym,layerName:gm,bar:_m,handle:wm,handleLeft:Sm,handleRight:km,subtitleRow:xm,subtitleLabel:Cm,subtitleBlock:Em},Mt=140,oo=32,Nm=14,Tc=20,Oc=400,Rc=["#4a7fc1","#c17a4a","#4ac17a","#c14a7a","#7a4ac1","#c1b44a","#4ac1c1"];function Lm(u){return Rc[u%Rc.length]}function Mm(u){return u.split("/").pop()??u}function Pm(){const u=wn(),{selectedLayerIndex:a,currentTimeMs:s,playState:f}=dt(I=>({selectedLayerIndex:I.selectedLayerIndex,currentTimeMs:I.currentTimeMs,playState:I.playState})),{selectLayer:p,addLayer:g,removeLayer:S,moveLayerUp:L,moveLayerDown:z,updateLayer:U,setCurrentTime:ee,setPlayState:D}=dt(),[H,fe]=ze.useState(60),me=ze.useRef(null),re=ze.useRef(!1),q=ze.useRef(null),ge=ze.useCallback(I=>I/1e3*H,[H]),Ge=ze.useCallback(I=>Math.max(0,Math.round(I/H*1e3)),[H]),Be=u?Math.max(u.durationMs,u.imageSegments.reduce((I,Q)=>Math.max(I,Q.endMs),0),1e4)+2e3:12e3,ke=Math.ceil(ge(Be)),Ze=H>=100?500:H>=50?1e3:2e3,Ee=H>=100?1e3:H>=50?2e3:4e3,Ne=[];for(let I=0;I<=Be;I+=Ze)Ne.push(I);ze.useEffect(()=>{const I=me.current;if(!I)return;function Q(le){!le.ctrlKey&&!le.metaKey||(le.preventDefault(),fe(K=>Math.min(Oc,Math.max(Tc,K*(le.deltaY<0?1.15:.87)))))}return I.addEventListener("wheel",Q,{passive:!1}),()=>I.removeEventListener("wheel",Q)},[]),ze.useEffect(()=>{if(f!=="playing")return;const I=me.current;if(!I)return;const Q=ge(s)+Mt,{scrollLeft:le,clientWidth:K}=I;Q>le+K-40&&(I.scrollLeft=Q-K+80)},[s,f,ge]);function $e(I){var A;if(I.button!==0)return;re.current=!0;const Q=I.currentTarget.getBoundingClientRect(),le=I.clientX-Q.left-Mt+(((A=me.current)==null?void 0:A.scrollLeft)??0);ee(Ge(le)),f==="playing"&&D("paused");function K(P){var k;const h=P.clientX-Q.left-Mt+(((k=me.current)==null?void 0:k.scrollLeft)??0);ee(Ge(h))}function M(){re.current=!1,window.removeEventListener("mousemove",K),window.removeEventListener("mouseup",M)}window.addEventListener("mousemove",K),window.addEventListener("mouseup",M)}function rt(I,Q,le){I.preventDefault(),I.stopPropagation(),p(Q);const K=u.imageSegments[Q];q.current={type:le,layerIndex:Q,startX:I.clientX,startMs:K.startMs,endMs:K.endMs,durationMs:K.endMs-K.startMs};function M(P){if(!q.current)return;const h=P.clientX-q.current.startX,k=Math.round(h/H*1e3),{type:X,layerIndex:Y,startMs:Z,endMs:b,durationMs:oe}=q.current;if(X==="move"){const J=Math.max(0,Z+k);U(Y,{startMs:J,endMs:J+oe})}else if(X==="left"){const J=Math.max(0,Math.min(b-100,Z+k));U(Y,{startMs:J})}else{const J=Math.max(Z+100,b+k);U(Y,{endMs:J})}}function A(){q.current=null,window.removeEventListener("mousemove",M),window.removeEventListener("mouseup",A)}window.addEventListener("mousemove",M),window.addEventListener("mouseup",A)}const pt=ge(s)+Mt;let kt=[];if(u){let I=0;for(const Q of u.lines){const le=Q.durationMs>0?Q.durationMs:Math.max(1500,Math.round(Q.text.length/17*1e3));kt.push({x:ge(I),w:ge(le),text:Q.text||"…"}),I+=le}}const He=(u==null?void 0:u.imageSegments.length)??0,De=He*oo+Nm+20;return y.jsxs("div",{className:ie.container,children:[y.jsxs("div",{className:ie.toolbar,children:[y.jsx("button",{className:ie.tbBtn,onClick:g,disabled:!u,title:"Add layer",children:"+ Layer"}),a!==null&&u&&y.jsxs(y.Fragment,{children:[y.jsx("button",{className:ie.tbBtn,onClick:()=>L(a),disabled:a<=0,title:"Move up",children:"↑"}),y.jsx("button",{className:ie.tbBtn,onClick:()=>z(a),disabled:a>=He-1,title:"Move down",children:"↓"}),y.jsx("button",{className:ie.tbBtn,onClick:()=>S(a),title:"Remove layer",style:{color:"#e06c6c"},children:"✕ Layer"})]}),y.jsx("div",{className:ie.spacer}),y.jsx("span",{className:ie.zoomLabel,children:"Zoom:"}),y.jsx("button",{className:ie.tbBtn,onClick:()=>fe(I=>Math.max(Tc,I*.75)),children:"−"}),y.jsx("button",{className:ie.tbBtn,onClick:()=>fe(I=>Math.min(Oc,I*1.33)),children:"+"}),y.jsx("button",{className:ie.tbBtn,onClick:()=>fe(60),children:"Reset"})]}),y.jsxs("div",{className:ie.scroll,ref:me,children:[y.jsxs("div",{className:ie.ruler,style:{width:Mt+ke},onMouseDown:$e,children:[y.jsx("div",{style:{width:Mt,flexShrink:0}}),y.jsx("div",{style:{position:"relative",flex:1},children:Ne.map(I=>y.jsx("div",{className:ie.tick,style:{left:ge(I)},children:I%Ee===0&&y.jsxs("span",{className:ie.tickLabel,children:[I/1e3,"s"]})},I))})]}),y.jsxs("div",{className:ie.layersArea,style:{width:Mt+ke,minHeight:De},children:[y.jsx("div",{className:ie.playhead,style:{left:pt}}),Ne.filter(I=>I%Ee===0).map(I=>y.jsx("div",{className:ie.gridLine,style:{left:Mt+ge(I)}},I)),u==null?void 0:u.imageSegments.map((I,Q)=>{const le=a===Q,K=Lm(Q),M=Mt+ge(I.startMs),A=Math.max(4,ge(I.endMs)-ge(I.startMs));return y.jsxs("div",{className:`${ie.row} ${le?ie.rowSelected:""}`,style:{top:Q*oo},onClick:()=>p(Q),children:[y.jsx("div",{className:ie.rowLabel,style:{borderLeft:`3px solid ${K}`},children:y.jsx("span",{className:ie.layerName,children:Mm(I.path)||`Layer ${Q+1}`})}),y.jsxs("div",{className:ie.bar,style:{left:M,width:A,background:K+(le?"cc":"88")},onMouseDown:P=>rt(P,Q,"move"),children:[y.jsx("div",{className:`${ie.handle} ${ie.handleLeft}`,onMouseDown:P=>rt(P,Q,"left")}),y.jsx("div",{className:`${ie.handle} ${ie.handleRight}`,onMouseDown:P=>rt(P,Q,"right")})]})]},Q)}),u&&y.jsxs("div",{className:ie.subtitleRow,style:{top:He*oo},children:[y.jsx("div",{className:ie.subtitleLabel,children:"Subtitles"}),kt.map((I,Q)=>y.jsx("div",{className:ie.subtitleBlock,style:{left:Mt+I.x,width:Math.max(2,I.w)},title:I.text},Q))]})]})]})]})}function jm(){return y.jsxs("div",{className:Pc.panel,children:[y.jsx("div",{className:Pc.previewArea,children:y.jsx(Zp,{})}),y.jsx(nm,{}),y.jsx(Pm,{})]})}const Im="_panel_1uywn_1",zm="_tabs_1uywn_11",Tm="_tab_1uywn_11",Om="_tabActive_1uywn_28",Rm="_scroll_1uywn_33",Fm="_section_1uywn_40",Dm="_sectionTitle_1uywn_45",Am="_sectionHeader_1uywn_54",Um="_addBtn_1uywn_61",Bm="_field_1uywn_72",$m="_row2_1uywn_105",Hm="_poseGroup_1uywn_123",Wm="_poseTitle_1uywn_130",Vm="_poseRow_1uywn_139",Qm="_lineCard_1uywn_158",Xm="_lineHeader_1uywn_166",Ym="_lineIndex_1uywn_173",Km="_iconBtn_1uywn_179",Gm="_iconBtnDanger_1uywn_190 _iconBtn_1uywn_179",Zm="_emptyLines_1uywn_197",Jm="_empty_1uywn_197",V={panel:Im,tabs:zm,tab:Tm,tabActive:Om,scroll:Rm,section:Fm,sectionTitle:Dm,sectionHeader:Am,addBtn:Um,field:Bm,row2:$m,poseGroup:Hm,poseTitle:Wm,poseRow:Vm,lineCard:Qm,lineHeader:Xm,lineIndex:Ym,iconBtn:Km,iconBtnDanger:Gm,emptyLines:Zm,empty:Jm};function Rr({label:u,value:a,onChange:s,min:f}){return y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:u}),y.jsx("input",{type:"number",value:a,min:f,onChange:p=>s(Number(p.target.value))})]})}function qm(){const u=wn(),a=dt(f=>f.updateCutscene);if(!u)return y.jsx("div",{className:V.empty,children:"No cutscene selected"});function s(f){a(u.id,f)}return y.jsxs("div",{className:V.section,children:[y.jsx("div",{className:V.sectionTitle,children:"Cutscene"}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"ID"}),y.jsx("input",{type:"text",value:u.id,onChange:f=>s({id:f.target.value})})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Skippable"}),y.jsx("input",{type:"checkbox",checked:u.skippable,onChange:f=>s({skippable:f.target.checked})})]}),y.jsx(Rr,{label:"Duration (ms)",value:u.durationMs,onChange:f=>s({durationMs:f}),min:0}),y.jsx(Rr,{label:"Fade out (ms)",value:u.fadeOutMs,onChange:f=>s({fadeOutMs:f}),min:0}),y.jsx(Rr,{label:"Fade in (ms)",value:u.fadeInMs,onChange:f=>s({fadeInMs:f}),min:0}),y.jsx(Rr,{label:"End fade out (ms)",value:u.endFadeOutMs,onChange:f=>s({endFadeOutMs:f}),min:0}),y.jsx(Rr,{label:"End fade in (ms)",value:u.endFadeInMs,onChange:f=>s({endFadeInMs:f}),min:0}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"onFadeIn callback"}),y.jsx("input",{type:"text",value:u.onFadeInCallback,onChange:f=>s({onFadeInCallback:f.target.value}),placeholder:"lua function name"})]})]})}const so=["resources/w/cutscenes/cutscene1/cutscene1_wall_x.png","resources/w/cutscenes/cutscene1/cutscene1_aida1_x.png","resources/w/cutscenes/cutscene1/cutscene1_aida2_x.png","resources/w/cutscenes/cutscene1/cutscene1_aida3_x.png","resources/w/cutscenes/cutscene1/cutscene1_heads_x.png","resources/w/cutscenes/cutscene2/scr1.png","resources/w/cutscenes/cutscene2/scr2.png","resources/w/cutscenes/cutscene2/scr3.png","resources/w/cutscenes/cutscene2/scr4.png"];function Yn({label:u,value:a,onChange:s,min:f,step:p}){return y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:u}),y.jsx("input",{type:"number",value:a,min:f,step:p??1,onChange:g=>s(Number(g.target.value))})]})}function Fc({label:u,pose:a,onChange:s}){return y.jsxs("div",{className:V.poseGroup,children:[y.jsx("div",{className:V.poseTitle,children:u}),y.jsxs("div",{className:V.poseRow,children:[y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"centerX"}),y.jsx("input",{type:"number",value:a.centerX,step:.01,min:0,max:1,onChange:f=>s({centerX:Number(f.target.value)})})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"centerY"}),y.jsx("input",{type:"number",value:a.centerY,step:.01,min:0,max:1,onChange:f=>s({centerY:Number(f.target.value)})})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"scale"}),y.jsx("input",{type:"number",value:a.scale,step:.05,min:.1,onChange:f=>s({scale:Number(f.target.value)})})]})]})]})}function bm(){const u=wn(),{selectedLayerIndex:a,updateLayer:s,updateLayerFrom:f,updateLayerTo:p}=dt(S=>({selectedLayerIndex:S.selectedLayerIndex,updateLayer:S.updateLayer,updateLayerFrom:S.updateLayerFrom,updateLayerTo:S.updateLayerTo}));if(!u||a===null)return null;const g=u.imageSegments[a];return g?y.jsxs("div",{className:V.section,children:[y.jsxs("div",{className:V.sectionTitle,children:["Layer ",a+1]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Image"}),y.jsxs("select",{value:so.includes(g.path)?g.path:"__custom__",onChange:S=>{S.target.value!=="__custom__"&&s(a,{path:S.target.value})},children:[so.map(S=>y.jsx("option",{value:S,children:S.split("/").pop()},S)),!so.includes(g.path)&&y.jsx("option",{value:"__custom__",children:"(custom)"})]})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Path (manual)"}),y.jsx("input",{type:"text",value:g.path,onChange:S=>s(a,{path:S.target.value}),placeholder:"resources/..."})]}),y.jsxs("div",{className:V.row2,children:[y.jsx(Yn,{label:"Width",value:g.width,onChange:S=>s(a,{width:S}),min:1}),y.jsx(Yn,{label:"Height",value:g.height,onChange:S=>s(a,{height:S}),min:1})]}),y.jsxs("div",{className:V.row2,children:[y.jsx(Yn,{label:"Start (ms)",value:g.startMs,onChange:S=>s(a,{startMs:S}),min:0}),y.jsx(Yn,{label:"End (ms)",value:g.endMs,onChange:S=>s(a,{endMs:S}),min:0})]}),y.jsxs("div",{className:V.row2,children:[y.jsx(Yn,{label:"Fade in (ms)",value:g.fadeInMs,onChange:S=>s(a,{fadeInMs:S}),min:0}),y.jsx(Yn,{label:"Fade out (ms)",value:g.fadeOutMs,onChange:S=>s(a,{fadeOutMs:S}),min:0})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Easing"}),y.jsx("select",{value:g.easing,onChange:S=>s(a,{easing:S.target.value}),children:Up.map(S=>y.jsx("option",{value:S,children:S},S))})]}),y.jsx(Fc,{label:"From",pose:g.from,onChange:S=>f(a,S)}),y.jsx(Fc,{label:"To",pose:g.to,onChange:S=>p(a,S)})]}):null}function eh(){const u=wn(),{addLine:a,removeLine:s,moveLineUp:f,moveLineDown:p,updateLine:g}=dt(S=>({addLine:S.addLine,removeLine:S.removeLine,moveLineUp:S.moveLineUp,moveLineDown:S.moveLineDown,updateLine:S.updateLine}));return u?y.jsxs("div",{className:V.section,children:[y.jsxs("div",{className:V.sectionHeader,children:[y.jsx("div",{className:V.sectionTitle,children:"Subtitle Lines"}),y.jsx("button",{className:V.addBtn,onClick:a,children:"+ Add"})]}),u.lines.length===0&&y.jsx("div",{className:V.emptyLines,children:"No lines. Click + Add."}),u.lines.map((S,L)=>y.jsxs("div",{className:V.lineCard,children:[y.jsxs("div",{className:V.lineHeader,children:[y.jsxs("span",{className:V.lineIndex,children:["#",L+1]}),y.jsx("button",{className:V.iconBtn,onClick:()=>f(L),disabled:L===0,title:"Move up",children:"↑"}),y.jsx("button",{className:V.iconBtn,onClick:()=>p(L),disabled:L===u.lines.length-1,title:"Move down",children:"↓"}),y.jsx("button",{className:V.iconBtnDanger,onClick:()=>s(L),title:"Remove",children:"✕"})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Speaker"}),y.jsx("input",{type:"text",value:S.speaker,onChange:z=>g(L,{speaker:z.target.value}),placeholder:"(none)"})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Text"}),y.jsx("textarea",{value:S.text,rows:2,onChange:z=>g(L,{text:z.target.value})})]}),y.jsxs("div",{className:V.row2,children:[y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Duration (ms)"}),y.jsx("input",{type:"number",value:S.durationMs,min:0,onChange:z=>g(L,{durationMs:Number(z.target.value)})})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Wait confirm"}),y.jsx("input",{type:"checkbox",checked:S.waitForConfirm,onChange:z=>g(L,{waitForConfirm:z.target.checked})})]})]}),y.jsxs("div",{className:V.field,children:[y.jsx("label",{children:"Lua callback"}),y.jsx("input",{type:"text",value:S.luaCallback,onChange:z=>g(L,{luaCallback:z.target.value}),placeholder:"function name"})]})]},L))]}):null}function th(){const{selectedLayerIndex:u,selectLayer:a}=dt(s=>({selectedLayerIndex:s.selectedLayerIndex,selectLayer:s.selectLayer}));return y.jsxs("div",{className:V.panel,children:[y.jsxs("div",{className:V.tabs,children:[y.jsx("button",{className:`${V.tab} ${u===null?V.tabActive:""}`,onClick:()=>a(null),children:"Cutscene"}),u!==null&&y.jsxs("button",{className:`${V.tab} ${V.tabActive}`,children:["Layer ",u+1]})]}),y.jsxs("div",{className:V.scroll,children:[u!==null?y.jsx(bm,{}):y.jsx(qm,{}),y.jsx(eh,{})]})]})}function nh(){return y.jsxs("div",{className:Jd.app,children:[y.jsx(Fp,{}),y.jsx(jm,{}),y.jsx(th,{})]})}Gd.createRoot(document.getElementById("root")).render(y.jsx(ze.StrictMode,{children:y.jsx(nh,{})})); diff --git a/cutsceneEditor/dist/index.html b/cutsceneEditor/dist/index.html new file mode 100644 index 0000000..1083693 --- /dev/null +++ b/cutsceneEditor/dist/index.html @@ -0,0 +1,13 @@ + + + + + + Cutscene Editor + + + + +
+ + diff --git a/cutsceneEditor/index.html b/cutsceneEditor/index.html new file mode 100644 index 0000000..6cfdd06 --- /dev/null +++ b/cutsceneEditor/index.html @@ -0,0 +1,12 @@ + + + + + + Cutscene Editor + + +
+ + + diff --git a/cutsceneEditor/package-lock.json b/cutsceneEditor/package-lock.json new file mode 100644 index 0000000..32631a3 --- /dev/null +++ b/cutsceneEditor/package-lock.json @@ -0,0 +1,1890 @@ +{ + "name": "cutscene-editor", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cutscene-editor", + "version": "0.1.0", + "dependencies": { + "immer": "^10.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^5.0.3" + }, + "devDependencies": { + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.34", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz", + "integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001797", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", + "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.368", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz", + "integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", + "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/cutsceneEditor/package.json b/cutsceneEditor/package.json new file mode 100644 index 0000000..c12c809 --- /dev/null +++ b/cutsceneEditor/package.json @@ -0,0 +1,24 @@ +{ + "name": "cutscene-editor", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "immer": "^10.1.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zustand": "^5.0.3" + }, + "devDependencies": { + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.2", + "vite": "^6.0.5" + } +} diff --git a/cutsceneEditor/src/App.module.css b/cutsceneEditor/src/App.module.css new file mode 100644 index 0000000..5c9790d --- /dev/null +++ b/cutsceneEditor/src/App.module.css @@ -0,0 +1,6 @@ +.app { + display: flex; + width: 100%; + height: 100%; + overflow: hidden; +} diff --git a/cutsceneEditor/src/App.tsx b/cutsceneEditor/src/App.tsx new file mode 100644 index 0000000..1b5dfef --- /dev/null +++ b/cutsceneEditor/src/App.tsx @@ -0,0 +1,14 @@ +import styles from './App.module.css'; +import LeftPanel from './components/LeftPanel/LeftPanel'; +import CenterPanel from './components/CenterPanel/CenterPanel'; +import RightPanel from './components/RightPanel/RightPanel'; + +export default function App() { + return ( +
+ + + +
+ ); +} diff --git a/cutsceneEditor/src/components/CenterPanel/CenterPanel.module.css b/cutsceneEditor/src/components/CenterPanel/CenterPanel.module.css new file mode 100644 index 0000000..bc14e06 --- /dev/null +++ b/cutsceneEditor/src/components/CenterPanel/CenterPanel.module.css @@ -0,0 +1,18 @@ +.panel { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + min-width: 0; +} + +.previewArea { + flex: 0 1 420px; + min-height: 120px; + display: flex; + justify-content: center; + align-items: center; + background: #111; + padding: 8px; + overflow: hidden; +} diff --git a/cutsceneEditor/src/components/CenterPanel/CenterPanel.tsx b/cutsceneEditor/src/components/CenterPanel/CenterPanel.tsx new file mode 100644 index 0000000..5269cb7 --- /dev/null +++ b/cutsceneEditor/src/components/CenterPanel/CenterPanel.tsx @@ -0,0 +1,16 @@ +import styles from './CenterPanel.module.css'; +import Preview from '../Preview/Preview'; +import Controls from '../Controls/Controls'; +import Timeline from '../Timeline/Timeline'; + +export default function CenterPanel() { + return ( +
+
+ +
+ + +
+ ); +} diff --git a/cutsceneEditor/src/components/Controls/Controls.module.css b/cutsceneEditor/src/components/Controls/Controls.module.css new file mode 100644 index 0000000..42c8d37 --- /dev/null +++ b/cutsceneEditor/src/components/Controls/Controls.module.css @@ -0,0 +1,94 @@ +.controls { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + background: #1e1e1e; + border-top: 1px solid #333; + border-bottom: 1px solid #333; + flex-shrink: 0; +} + +.btn { + background: #2d2d2d; + border: 1px solid #404040; + color: #ccc; + border-radius: 4px; + padding: 4px 10px; + font-size: 13px; + transition: background 0.1s; + min-width: 32px; + flex-shrink: 0; +} + +.btn:hover:not(:disabled) { background: #3a3a3a; color: #fff; } +.btn:disabled { opacity: 0.35; cursor: default; } + +.active { + color: #5ba3e0; + border-color: #4a7aaa; +} + +/* ── Scrubber ───────────────────────────────────────────────── */ +.scrubber { + flex: 1; + min-width: 0; + height: 4px; + -webkit-appearance: none; + appearance: none; + border-radius: 2px; + outline: none; + cursor: pointer; + border: none; + padding: 0; + /* filled portion via CSS variable set inline */ + background: linear-gradient( + to right, + #5b9bd5 0%, + #5b9bd5 var(--progress, 0%), + #3a3a3a var(--progress, 0%), + #3a3a3a 100% + ); +} + +.scrubber:disabled { + opacity: 0.3; + cursor: default; +} + +.scrubber::-webkit-slider-thumb { + -webkit-appearance: none; + width: 12px; + height: 12px; + border-radius: 50%; + background: #5b9bd5; + cursor: pointer; + border: 2px solid #1e1e1e; + transition: transform 0.1s; +} +.scrubber:not(:disabled)::-webkit-slider-thumb:hover { + transform: scale(1.3); +} + +.scrubber::-moz-range-thumb { + width: 12px; + height: 12px; + border-radius: 50%; + background: #5b9bd5; + cursor: pointer; + border: 2px solid #1e1e1e; +} + +.scrubber::-moz-range-track { + height: 4px; + border-radius: 2px; + background: #3a3a3a; +} + +.time { + flex-shrink: 0; + font-size: 11px; + color: #888; + font-variant-numeric: tabular-nums; + white-space: nowrap; +} diff --git a/cutsceneEditor/src/components/Controls/Controls.tsx b/cutsceneEditor/src/components/Controls/Controls.tsx new file mode 100644 index 0000000..3f2294a --- /dev/null +++ b/cutsceneEditor/src/components/Controls/Controls.tsx @@ -0,0 +1,67 @@ +import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore'; +import { usePlayback } from '../../hooks/usePlayback'; +import styles from './Controls.module.css'; + +function formatMs(ms: number) { + const s = Math.floor(ms / 1000); + const frac = Math.floor((ms % 1000) / 10).toString().padStart(2, '0'); + return `${s}.${frac}s`; +} + +export default function Controls() { + usePlayback(); + + const { playState, currentTimeMs, setPlayState, setCurrentTime } = useCutsceneStore(); + const cutscene = useSelectedCutscene(); + + const totalMs = cutscene + ? Math.max( + cutscene.durationMs, + cutscene.imageSegments.reduce((m, s) => Math.max(m, s.endMs), 0) + ) + cutscene.endFadeOutMs + cutscene.endFadeInMs + : 0; + + function play() { + if (playState === 'stopped' || currentTimeMs >= totalMs) setCurrentTime(0); + setPlayState('playing'); + } + + function pause() { setPlayState('paused'); } + function stop() { setPlayState('stopped'); setCurrentTime(0); } + + function handleScrub(e: React.ChangeEvent) { + const ms = Number(e.target.value); + setCurrentTime(ms); + if (playState === 'playing') setPlayState('paused'); + } + + const progress = totalMs > 0 ? currentTimeMs / totalMs : 0; + + return ( +
+ + + {playState === 'playing' ? ( + + ) : ( + + )} + + + +
+ {formatMs(currentTimeMs)} / {formatMs(totalMs)} +
+
+ ); +} diff --git a/cutsceneEditor/src/components/LeftPanel/LeftPanel.module.css b/cutsceneEditor/src/components/LeftPanel/LeftPanel.module.css new file mode 100644 index 0000000..4675621 --- /dev/null +++ b/cutsceneEditor/src/components/LeftPanel/LeftPanel.module.css @@ -0,0 +1,100 @@ +.panel { + width: 200px; + min-width: 180px; + background: #252525; + border-right: 1px solid #333; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.header { + padding: 10px 12px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + color: #888; + border-bottom: 1px solid #333; +} + +.actions { + display: flex; + flex-direction: column; + gap: 6px; + padding: 10px; + border-bottom: 1px solid #333; +} + +.btn { + background: #333; + color: #ddd; + border: 1px solid #444; + border-radius: 4px; + padding: 5px 8px; + text-align: center; + transition: background 0.15s; +} + +.btn:hover:not(:disabled) { background: #3d3d3d; } +.btn:disabled { opacity: 0.4; cursor: default; } + +.btnPrimary { + composes: btn; + background: #2a4a6e; + border-color: #3a6090; + color: #a8d0f0; +} +.btnPrimary:hover { background: #2e5480; } + +.list { + flex: 1; + overflow-y: auto; + padding: 4px 0; +} + +.empty { + padding: 16px 12px; + color: #555; + font-size: 11px; + line-height: 1.5; +} + +.item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 7px 12px; + cursor: pointer; + user-select: none; + border-left: 3px solid transparent; + transition: background 0.1s; +} + +.item:hover { background: #2e2e2e; } + +.selected { + background: #1e3a55; + border-left-color: #5b9bd5; +} + +.itemId { + font-size: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; +} + +.deleteBtn { + background: none; + border: none; + color: #666; + padding: 2px 4px; + border-radius: 3px; + font-size: 10px; + opacity: 0; + transition: opacity 0.1s, color 0.1s; +} +.item:hover .deleteBtn { opacity: 1; } +.deleteBtn:hover { color: #e06c6c; } diff --git a/cutsceneEditor/src/components/LeftPanel/LeftPanel.tsx b/cutsceneEditor/src/components/LeftPanel/LeftPanel.tsx new file mode 100644 index 0000000..6419f35 --- /dev/null +++ b/cutsceneEditor/src/components/LeftPanel/LeftPanel.tsx @@ -0,0 +1,76 @@ +import { useRef } from 'react'; +import { useCutsceneStore } from '../../store/cutsceneStore'; +import { parseFile, triggerDownload } from '../../utils/fileIO'; +import styles from './LeftPanel.module.css'; + +export default function LeftPanel() { + const fileInputRef = useRef(null); + const { file, selectedCutsceneId, loadFile, addCutscene, deleteCutscene, selectCutscene, getExportData } = useCutsceneStore(); + + function handleFileChange(e: React.ChangeEvent) { + const f = e.target.files?.[0]; + if (!f) return; + const reader = new FileReader(); + reader.onload = (ev) => { + try { + const json = JSON.parse(ev.target!.result as string); + loadFile(parseFile(json)); + } catch { + alert('Invalid JSON file'); + } + }; + reader.readAsText(f); + e.target.value = ''; + } + + function handleSave() { + const data = getExportData(); + if (data) triggerDownload(data); + } + + function handleDelete(id: string) { + if (confirm(`Delete cutscene "${id}"?`)) deleteCutscene(id); + } + + return ( +
+
Cutscenes
+ +
+ + + + +
+ +
+ {!file || file.cutscenes.length === 0 ? ( +
No cutscenes. Load a JSON or create new.
+ ) : ( + file.cutscenes.map(c => ( +
selectCutscene(c.id)} + > + {c.id} + +
+ )) + )} +
+
+ ); +} diff --git a/cutsceneEditor/src/components/Preview/Preview.module.css b/cutsceneEditor/src/components/Preview/Preview.module.css new file mode 100644 index 0000000..41774d9 --- /dev/null +++ b/cutsceneEditor/src/components/Preview/Preview.module.css @@ -0,0 +1,51 @@ +.wrapper { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.viewport { + position: relative; + aspect-ratio: 16 / 9; + width: 100%; + max-height: 100%; + background: #000; + overflow: hidden; +} + +.empty { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: #444; + font-size: 13px; +} + +.subtitleBar { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 10px 20px 14px; + background: linear-gradient(transparent, rgba(0,0,0,0.75)); + text-align: center; +} + +.speaker { + font-size: 11px; + font-weight: 600; + color: #f0c060; + margin-bottom: 4px; + text-shadow: 0 1px 3px rgba(0,0,0,0.8); +} + +.text { + font-size: 14px; + color: #fff; + line-height: 1.5; + text-shadow: 0 1px 4px rgba(0,0,0,0.9); +} diff --git a/cutsceneEditor/src/components/Preview/Preview.tsx b/cutsceneEditor/src/components/Preview/Preview.tsx new file mode 100644 index 0000000..0916026 --- /dev/null +++ b/cutsceneEditor/src/components/Preview/Preview.tsx @@ -0,0 +1,85 @@ +import { useRef, useEffect, useState } from 'react'; +import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore'; +import { computeSegmentState, poseToStyle } from '../../utils/rendering'; +import styles from './Preview.module.css'; + +const LOGICAL_W = 1280; +const LOGICAL_H = 720; + +function computeSubtitle(cutscene: ReturnType, currentMs: number) { + if (!cutscene) return null; + let elapsed = 0; + for (const line of cutscene.lines) { + const dur = line.durationMs > 0 + ? line.durationMs + : Math.max(1500, Math.round((line.text.length / 17) * 1000)); + if (currentMs >= elapsed && currentMs < elapsed + dur) return line; + elapsed += dur; + } + return null; +} + +export default function Preview() { + const containerRef = useRef(null); + const [containerSize, setContainerSize] = useState({ w: LOGICAL_W, h: LOGICAL_H }); + + const currentTimeMs = useCutsceneStore(s => s.currentTimeMs); + const cutscene = useSelectedCutscene(); + + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const ro = new ResizeObserver(entries => { + const e = entries[0]; + if (e) setContainerSize({ w: e.contentRect.width, h: e.contentRect.height }); + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + const subtitle = computeSubtitle(cutscene, currentTimeMs); + + return ( +
+
+ {!cutscene ? ( +
Select or create a cutscene
+ ) : ( + <> + {cutscene.imageSegments.map((seg, i) => { + const state = computeSegmentState(seg, currentTimeMs); + if (!state) return null; + + const imgStyle = poseToStyle( + state.pose, + seg.width || LOGICAL_W, + seg.height || LOGICAL_H, + containerSize.w, + containerSize.h, + ); + + return ( + + ); + })} + + {subtitle && ( +
+ {subtitle.speaker && ( +
{subtitle.speaker}
+ )} +
{subtitle.text}
+
+ )} + + )} +
+
+ ); +} diff --git a/cutsceneEditor/src/components/RightPanel/CutsceneProperties.tsx b/cutsceneEditor/src/components/RightPanel/CutsceneProperties.tsx new file mode 100644 index 0000000..793a4b3 --- /dev/null +++ b/cutsceneEditor/src/components/RightPanel/CutsceneProperties.tsx @@ -0,0 +1,72 @@ +import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore'; +import type { Cutscene } from '../../types/cutscene'; +import styles from './RightPanel.module.css'; + +type CutscenePatch = Partial>; + +function NumField({ label, value, onChange, min }: { + label: string; value: number; onChange: (v: number) => void; min?: number; +}) { + return ( +
+ + onChange(Number(e.target.value))} + /> +
+ ); +} + +export default function CutsceneProperties() { + const cutscene = useSelectedCutscene(); + const updateCutscene = useCutsceneStore(s => s.updateCutscene); + + if (!cutscene) return
No cutscene selected
; + + function upd(patch: CutscenePatch) { + updateCutscene(cutscene!.id, patch); + } + + return ( +
+
Cutscene
+ +
+ + upd({ id: e.target.value })} + /> +
+ +
+ + upd({ skippable: e.target.checked })} + /> +
+ + upd({ durationMs: v })} min={0} /> + upd({ fadeOutMs: v })} min={0} /> + upd({ fadeInMs: v })} min={0} /> + upd({ endFadeOutMs: v })} min={0} /> + upd({ endFadeInMs: v })} min={0} /> + +
+ + upd({ onFadeInCallback: e.target.value })} + placeholder="lua function name" + /> +
+
+ ); +} diff --git a/cutsceneEditor/src/components/RightPanel/LayerProperties.tsx b/cutsceneEditor/src/components/RightPanel/LayerProperties.tsx new file mode 100644 index 0000000..a618ad2 --- /dev/null +++ b/cutsceneEditor/src/components/RightPanel/LayerProperties.tsx @@ -0,0 +1,132 @@ +import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore'; +import { useShallow } from 'zustand/react/shallow'; +import { AVAILABLE_IMAGES } from '../../constants/images'; +import { EASING_OPTIONS } from '../../constants/easings'; +import type { ImagePose } from '../../types/cutscene'; +import styles from './RightPanel.module.css'; + +function NumField({ label, value, onChange, min, step }: { + label: string; value: number; onChange: (v: number) => void; min?: number; step?: number; +}) { + return ( +
+ + onChange(Number(e.target.value))} + /> +
+ ); +} + +function PoseFields({ label, pose, onChange }: { + label: string; + pose: ImagePose; + onChange: (patch: Partial) => void; +}) { + return ( +
+
{label}
+
+
+ + onChange({ centerX: Number(e.target.value) })} /> +
+
+ + onChange({ centerY: Number(e.target.value) })} /> +
+
+ + onChange({ scale: Number(e.target.value) })} /> +
+
+
+ ); +} + +export default function LayerProperties() { + const cutscene = useSelectedCutscene(); + const { selectedLayerIndex, updateLayer, updateLayerFrom, updateLayerTo } = useCutsceneStore(useShallow(s => ({ + selectedLayerIndex: s.selectedLayerIndex, + updateLayer: s.updateLayer, + updateLayerFrom: s.updateLayerFrom, + updateLayerTo: s.updateLayerTo, + }))); + + if (!cutscene || selectedLayerIndex === null) return null; + const seg = cutscene.imageSegments[selectedLayerIndex]; + if (!seg) return null; + + return ( +
+
Layer {selectedLayerIndex + 1}
+ +
+ + +
+ +
+ + updateLayer(selectedLayerIndex, { path: e.target.value })} + placeholder="resources/..." + /> +
+ +
+ updateLayer(selectedLayerIndex, { width: v })} min={1} /> + updateLayer(selectedLayerIndex, { height: v })} min={1} /> +
+ +
+ updateLayer(selectedLayerIndex, { startMs: v })} min={0} /> + updateLayer(selectedLayerIndex, { endMs: v })} min={0} /> +
+ +
+ updateLayer(selectedLayerIndex, { fadeInMs: v })} min={0} /> + updateLayer(selectedLayerIndex, { fadeOutMs: v })} min={0} /> +
+ +
+ + +
+ + updateLayerFrom(selectedLayerIndex, patch)} + /> + updateLayerTo(selectedLayerIndex, patch)} + /> +
+ ); +} diff --git a/cutsceneEditor/src/components/RightPanel/RightPanel.module.css b/cutsceneEditor/src/components/RightPanel/RightPanel.module.css new file mode 100644 index 0000000..68075eb --- /dev/null +++ b/cutsceneEditor/src/components/RightPanel/RightPanel.module.css @@ -0,0 +1,208 @@ +.panel { + width: 260px; + min-width: 240px; + background: #252525; + border-left: 1px solid #333; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.tabs { + display: flex; + border-bottom: 1px solid #333; + flex-shrink: 0; +} + +.tab { + flex: 1; + padding: 8px 6px; + background: none; + border: none; + color: #777; + font-size: 11px; + border-bottom: 2px solid transparent; + transition: color 0.1s; +} +.tab:hover { color: #bbb; } +.tabActive { + color: #5b9bd5; + border-bottom-color: #5b9bd5; +} + +.scroll { + flex: 1; + overflow-y: auto; + padding-bottom: 20px; +} + +/* ── Sections ──────────────────────────────────────────────────── */ +.section { + padding: 10px; + border-bottom: 1px solid #2e2e2e; +} + +.sectionTitle { + font-size: 11px; + font-weight: 600; + color: #888; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 8px; +} + +.sectionHeader { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; +} + +.addBtn { + background: #2a4a6e; + border: 1px solid #3a6090; + color: #a8d0f0; + border-radius: 3px; + padding: 2px 8px; + font-size: 11px; +} +.addBtn:hover { background: #2e5480; } + +/* ── Fields ────────────────────────────────────────────────────── */ +.field { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 5px; + gap: 6px; +} + +.field label { + flex-shrink: 0; + width: 100px; + text-align: right; +} + +.field input[type="text"], +.field input[type="number"], +.field select, +.field textarea { + flex: 1; + min-width: 0; + width: 100%; +} + +.field textarea { + resize: vertical; + min-height: 40px; +} + +.field input[type="checkbox"] { + width: auto; + margin: 0; +} + +.row2 { + display: flex; + gap: 6px; +} + +.row2 .field { + flex: 1; + flex-direction: column; + align-items: flex-start; +} + +.row2 .field label { + width: auto; + text-align: left; + margin-bottom: 2px; +} + +/* ── Pose groups ───────────────────────────────────────────────── */ +.poseGroup { + margin-top: 8px; + background: #1e1e1e; + border-radius: 4px; + padding: 6px 8px; +} + +.poseTitle { + font-size: 10px; + font-weight: 600; + color: #666; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 5px; +} + +.poseRow { + display: flex; + gap: 6px; +} + +.poseRow .field { + flex: 1; + flex-direction: column; + align-items: flex-start; + margin-bottom: 0; +} + +.poseRow .field label { + width: auto; + text-align: left; + margin-bottom: 2px; +} + +/* ── Subtitle line cards ───────────────────────────────────────── */ +.lineCard { + background: #1e1e1e; + border: 1px solid #2e2e2e; + border-radius: 4px; + padding: 8px; + margin-bottom: 6px; +} + +.lineHeader { + display: flex; + align-items: center; + gap: 4px; + margin-bottom: 6px; +} + +.lineIndex { + font-size: 10px; + color: #666; + flex: 1; +} + +.iconBtn { + background: none; + border: 1px solid #3a3a3a; + color: #888; + border-radius: 3px; + padding: 1px 5px; + font-size: 11px; +} +.iconBtn:hover:not(:disabled) { background: #333; color: #ccc; } +.iconBtn:disabled { opacity: 0.3; cursor: default; } + +.iconBtnDanger { + composes: iconBtn; + color: #a05050; + border-color: #5a2a2a; +} +.iconBtnDanger:hover { background: #3a2020; color: #e06c6c; } + +.emptyLines { + color: #555; + font-size: 11px; + padding: 4px 0; +} + +.empty { + padding: 20px; + color: #555; + font-size: 11px; + text-align: center; +} diff --git a/cutsceneEditor/src/components/RightPanel/RightPanel.tsx b/cutsceneEditor/src/components/RightPanel/RightPanel.tsx new file mode 100644 index 0000000..bf29b41 --- /dev/null +++ b/cutsceneEditor/src/components/RightPanel/RightPanel.tsx @@ -0,0 +1,41 @@ +import { useCutsceneStore } from '../../store/cutsceneStore'; +import { useShallow } from 'zustand/react/shallow'; +import CutsceneProperties from './CutsceneProperties'; +import LayerProperties from './LayerProperties'; +import SubtitleLines from './SubtitleLines'; +import styles from './RightPanel.module.css'; + +export default function RightPanel() { + const { selectedLayerIndex, selectLayer } = useCutsceneStore(useShallow(s => ({ + selectedLayerIndex: s.selectedLayerIndex, + selectLayer: s.selectLayer, + }))); + + return ( +
+ {/* Tab strip */} +
+ + {selectedLayerIndex !== null && ( + + )} +
+ +
+ {selectedLayerIndex !== null ? ( + + ) : ( + + )} + +
+
+ ); +} diff --git a/cutsceneEditor/src/components/RightPanel/SubtitleLines.tsx b/cutsceneEditor/src/components/RightPanel/SubtitleLines.tsx new file mode 100644 index 0000000..d57ef66 --- /dev/null +++ b/cutsceneEditor/src/components/RightPanel/SubtitleLines.tsx @@ -0,0 +1,89 @@ +import { useCutsceneStore, useSelectedCutscene } from '../../store/cutsceneStore'; +import { useShallow } from 'zustand/react/shallow'; +import styles from './RightPanel.module.css'; + +export default function SubtitleLines() { + const cutscene = useSelectedCutscene(); + const { addLine, removeLine, moveLineUp, moveLineDown, updateLine } = useCutsceneStore(useShallow(s => ({ + addLine: s.addLine, + removeLine: s.removeLine, + moveLineUp: s.moveLineUp, + moveLineDown: s.moveLineDown, + updateLine: s.updateLine, + }))); + + if (!cutscene) return null; + + return ( +
+
+
Subtitle Lines
+ +
+ + {cutscene.lines.length === 0 && ( +
No lines. Click + Add.
+ )} + + {cutscene.lines.map((line, i) => ( +
+
+ #{i + 1} + + + +
+ +
+ + updateLine(i, { speaker: e.target.value })} + placeholder="(none)" + /> +
+ +
+ +