@mevdragon/vidfarm-devcli 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/account-pages-legacy.js +7 -5
- package/dist/src/account-pages-legacy.js.map +1 -1
- package/dist/src/account-pages.js +1 -10
- package/dist/src/account-pages.js.map +1 -1
- package/dist/src/app.js +106 -418
- package/dist/src/app.js.map +1 -1
- package/dist/src/cli.js +5 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/dev-app-legacy.js +1 -1
- package/dist/src/dev-serve.js +169 -0
- package/dist/src/dev-serve.js.map +1 -0
- package/dist/src/editor-chat.js +10 -9
- package/dist/src/editor-chat.js.map +1 -1
- package/dist/src/editor-dark-theme.js +1128 -0
- package/dist/src/editor-dark-theme.js.map +1 -0
- package/dist/src/frontend/homepage-view.js +4 -4
- package/dist/src/frontend/homepage-view.js.map +1 -1
- package/dist/src/frontend/page-runtime-client.js +23 -2
- package/dist/src/frontend/page-runtime-client.js.map +1 -1
- package/dist/src/frontend/template-editor-chat.js +28 -35
- package/dist/src/frontend/template-editor-chat.js.map +1 -1
- package/dist/src/homepage.js +1 -1
- package/dist/src/homepage.js.map +1 -1
- package/dist/src/page-shell.js +27 -1105
- package/dist/src/page-shell.js.map +1 -1
- package/dist/src/primitive-registry.js +3 -3
- package/dist/src/primitive-registry.js.map +1 -1
- package/dist/src/services/fork-access.js +6 -7
- package/dist/src/services/fork-access.js.map +1 -1
- package/dist/src/services/fork-manifest.js +43 -0
- package/dist/src/services/fork-manifest.js.map +1 -0
- package/dist/src/services/serverless-records.js +2 -0
- package/dist/src/services/serverless-records.js.map +1 -1
- package/dist/src/services/storage.js +3 -2
- package/dist/src/services/storage.js.map +1 -1
- package/dist/src/template-editor-pages.js +3 -4136
- package/dist/src/template-editor-pages.js.map +1 -1
- package/dist/src/template-editor-shell.js +42 -53
- package/dist/src/template-editor-shell.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/homepage-client-app.js +4 -4
- package/public/assets/page-runtime-client-app.js +29 -29
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { renderTemplateEditorChrome, TEMPLATE_EDITOR_SHELL_SCRIPT, TEMPLATE_EDITOR_SHELL_STYLES } from "./template-editor-shell.js";
|
|
1
|
+
import { renderBrandLockup, renderPageShell, renderPrimaryNav } from "./page-shell.js";
|
|
3
2
|
export function renderCustomInpaintPage(input) {
|
|
4
3
|
return renderPageShell({
|
|
5
|
-
title: "
|
|
4
|
+
title: "Inpaint",
|
|
6
5
|
mainClass: "is-wide",
|
|
7
6
|
style: `
|
|
8
7
|
.custom-inpaint-frame {
|
|
@@ -889,7 +888,7 @@ export function renderCustomInpaintPage(input) {
|
|
|
889
888
|
<section class="custom-inpaint-workbench">
|
|
890
889
|
<aside class="custom-inpaint-panel">
|
|
891
890
|
<div class="custom-inpaint-panel-head">
|
|
892
|
-
<h1>
|
|
891
|
+
<h1>Inpaint</h1>
|
|
893
892
|
<button type="button" class="editor-history-button" data-custom-inpaint-start-new>Start New</button>
|
|
894
893
|
</div>
|
|
895
894
|
<p>Upload a source image, paint replacement regions, then create a reusable image edit for a timeline layer.</p>
|
|
@@ -2489,4136 +2488,4 @@ export function renderCustomInpaintPage(input) {
|
|
|
2489
2488
|
`
|
|
2490
2489
|
});
|
|
2491
2490
|
}
|
|
2492
|
-
export function renderTemplateEditorDocsPage(input) {
|
|
2493
|
-
const backHref = withAccountQuery("/discover", input.account.userId);
|
|
2494
|
-
const hasHistoryAccess = input.account.isLoggedIn;
|
|
2495
|
-
const preview = input.template.previewUrl
|
|
2496
|
-
? renderPreview(input.template.previewUrl, `${input.template.title} preview`)
|
|
2497
|
-
: `<div class="editor-preview editor-preview-empty">No preview asset</div>`;
|
|
2498
|
-
const soundMetadata = renderTemplateSound(input.template.sound);
|
|
2499
|
-
return renderPageShell({
|
|
2500
|
-
title: `${input.template.title} timeline workspace`,
|
|
2501
|
-
mainClass: "is-wide",
|
|
2502
|
-
runtimeBoot: input.editorChat ? {
|
|
2503
|
-
pageKind: "legacy",
|
|
2504
|
-
title: `${input.template.title} timeline workspace`,
|
|
2505
|
-
userId: input.account.userId ?? null,
|
|
2506
|
-
editorChat: input.editorChat
|
|
2507
|
-
} : undefined,
|
|
2508
|
-
style: `
|
|
2509
|
-
${TEMPLATE_EDITOR_SHELL_STYLES}
|
|
2510
|
-
|
|
2511
|
-
.editor-workbench,
|
|
2512
|
-
.editor-main,
|
|
2513
|
-
.editor-shell {
|
|
2514
|
-
padding-bottom: 0;
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
.editor-shell {
|
|
2518
|
-
display: grid;
|
|
2519
|
-
gap: 18px;
|
|
2520
|
-
min-height: 0;
|
|
2521
|
-
grid-template-rows: auto auto minmax(0, 1fr);
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
.editor-hero,
|
|
2525
|
-
.editor-section {
|
|
2526
|
-
border: 1px solid rgba(102, 82, 43, 0.14);
|
|
2527
|
-
border-radius: 28px;
|
|
2528
|
-
background: rgba(255, 251, 244, 0.78);
|
|
2529
|
-
box-shadow: 0 14px 34px rgba(15, 23, 42, 0.04);
|
|
2530
|
-
}
|
|
2531
|
-
|
|
2532
|
-
.editor-panel {
|
|
2533
|
-
border: 1px solid rgba(102, 82, 43, 0.14);
|
|
2534
|
-
border-radius: 28px;
|
|
2535
|
-
background: rgba(255, 251, 244, 0.78);
|
|
2536
|
-
box-shadow: 0 14px 34px rgba(15, 23, 42, 0.04);
|
|
2537
|
-
overflow: hidden;
|
|
2538
|
-
}
|
|
2539
|
-
|
|
2540
|
-
.editor-panel > summary {
|
|
2541
|
-
display: flex;
|
|
2542
|
-
align-items: center;
|
|
2543
|
-
justify-content: space-between;
|
|
2544
|
-
gap: 16px;
|
|
2545
|
-
padding: 18px 22px;
|
|
2546
|
-
cursor: pointer;
|
|
2547
|
-
list-style: none;
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
.editor-panel > summary::-webkit-details-marker {
|
|
2551
|
-
display: none;
|
|
2552
|
-
}
|
|
2553
|
-
|
|
2554
|
-
.editor-panel-summary {
|
|
2555
|
-
display: grid;
|
|
2556
|
-
gap: 4px;
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
.editor-panel-summary h2 {
|
|
2560
|
-
margin: 0;
|
|
2561
|
-
font-size: 1.55rem;
|
|
2562
|
-
letter-spacing: -0.04em;
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
|
-
.editor-panel-summary p {
|
|
2566
|
-
margin: 0;
|
|
2567
|
-
color: #617087;
|
|
2568
|
-
line-height: 1.6;
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
.editor-panel-toggle {
|
|
2572
|
-
display: inline-flex;
|
|
2573
|
-
align-items: center;
|
|
2574
|
-
gap: 10px;
|
|
2575
|
-
color: #445066;
|
|
2576
|
-
font-size: 0.82rem;
|
|
2577
|
-
font-weight: 700;
|
|
2578
|
-
letter-spacing: 0.08em;
|
|
2579
|
-
text-transform: uppercase;
|
|
2580
|
-
white-space: nowrap;
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
.editor-panel-toggle::before {
|
|
2584
|
-
content: "";
|
|
2585
|
-
width: 11px;
|
|
2586
|
-
height: 11px;
|
|
2587
|
-
border-right: 2px solid currentColor;
|
|
2588
|
-
border-bottom: 2px solid currentColor;
|
|
2589
|
-
transform: rotate(45deg) translateY(-2px);
|
|
2590
|
-
transition: transform 180ms ease;
|
|
2591
|
-
}
|
|
2592
|
-
|
|
2593
|
-
.editor-panel[open] .editor-panel-toggle::before {
|
|
2594
|
-
transform: rotate(225deg) translateY(-1px);
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2597
|
-
.editor-panel-body {
|
|
2598
|
-
padding: 0 22px 22px;
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
.editor-panel-body > .editor-hero,
|
|
2602
|
-
.editor-panel-body > .editor-section {
|
|
2603
|
-
border: 0;
|
|
2604
|
-
border-radius: 0;
|
|
2605
|
-
background: transparent;
|
|
2606
|
-
box-shadow: none;
|
|
2607
|
-
padding: 0;
|
|
2608
|
-
}
|
|
2609
|
-
|
|
2610
|
-
.editor-hero {
|
|
2611
|
-
display: grid;
|
|
2612
|
-
grid-template-columns: minmax(280px, 420px) minmax(0, 1fr);
|
|
2613
|
-
gap: 22px;
|
|
2614
|
-
padding: 22px;
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
.editor-preview {
|
|
2618
|
-
width: 100%;
|
|
2619
|
-
min-height: 320px;
|
|
2620
|
-
max-height: 560px;
|
|
2621
|
-
object-fit: contain;
|
|
2622
|
-
border-radius: 24px;
|
|
2623
|
-
border: 1px solid rgba(209, 219, 233, 0.96);
|
|
2624
|
-
background: rgba(255, 255, 255, 0.92);
|
|
2625
|
-
}
|
|
2626
|
-
|
|
2627
|
-
.editor-preview-empty {
|
|
2628
|
-
display: grid;
|
|
2629
|
-
place-items: center;
|
|
2630
|
-
color: #74829c;
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
.editor-copy {
|
|
2634
|
-
display: grid;
|
|
2635
|
-
align-content: start;
|
|
2636
|
-
gap: 18px;
|
|
2637
|
-
min-width: 0;
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
.editor-kicker {
|
|
2641
|
-
color: #8d99ae;
|
|
2642
|
-
font-size: 0.85rem;
|
|
2643
|
-
text-transform: uppercase;
|
|
2644
|
-
letter-spacing: 0.12em;
|
|
2645
|
-
}
|
|
2646
|
-
|
|
2647
|
-
.editor-title {
|
|
2648
|
-
margin: 0;
|
|
2649
|
-
font-size: clamp(2rem, 3.6vw, 3.1rem);
|
|
2650
|
-
line-height: 0.94;
|
|
2651
|
-
letter-spacing: -0.06em;
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
.editor-copy p {
|
|
2655
|
-
margin: 0;
|
|
2656
|
-
color: #5f6c83;
|
|
2657
|
-
font-size: 1rem;
|
|
2658
|
-
line-height: 1.7;
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
.editor-sound {
|
|
2662
|
-
display: flex;
|
|
2663
|
-
flex-wrap: wrap;
|
|
2664
|
-
align-items: center;
|
|
2665
|
-
gap: 10px;
|
|
2666
|
-
width: fit-content;
|
|
2667
|
-
max-width: 100%;
|
|
2668
|
-
padding: 12px 14px;
|
|
2669
|
-
border: 1px solid rgba(191, 164, 109, 0.26);
|
|
2670
|
-
border-radius: 18px;
|
|
2671
|
-
background: rgba(255, 255, 255, 0.66);
|
|
2672
|
-
color: #314055;
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
.editor-sound-label {
|
|
2676
|
-
color: #8d99ae;
|
|
2677
|
-
font-size: 0.75rem;
|
|
2678
|
-
font-weight: 800;
|
|
2679
|
-
letter-spacing: 0.12em;
|
|
2680
|
-
text-transform: uppercase;
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
.editor-sound-title {
|
|
2684
|
-
min-width: 0;
|
|
2685
|
-
color: #263245;
|
|
2686
|
-
font-weight: 800;
|
|
2687
|
-
line-height: 1.35;
|
|
2688
|
-
overflow-wrap: anywhere;
|
|
2689
|
-
}
|
|
2690
|
-
|
|
2691
|
-
.editor-sound a {
|
|
2692
|
-
color: inherit;
|
|
2693
|
-
text-decoration: none;
|
|
2694
|
-
}
|
|
2695
|
-
|
|
2696
|
-
.editor-sound a:hover {
|
|
2697
|
-
text-decoration: underline;
|
|
2698
|
-
text-underline-offset: 4px;
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
.editor-pills,
|
|
2702
|
-
.editor-route-list {
|
|
2703
|
-
display: flex;
|
|
2704
|
-
flex-wrap: wrap;
|
|
2705
|
-
gap: 10px;
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
.editor-toolbar {
|
|
2709
|
-
display: flex;
|
|
2710
|
-
flex-wrap: wrap;
|
|
2711
|
-
gap: 10px;
|
|
2712
|
-
margin-top: 14px;
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
.editor-copy-button {
|
|
2716
|
-
appearance: none;
|
|
2717
|
-
border: 1px solid rgba(102, 82, 43, 0.18);
|
|
2718
|
-
background: rgba(255, 255, 255, 0.88);
|
|
2719
|
-
color: #2f3c4f;
|
|
2720
|
-
border-radius: 999px;
|
|
2721
|
-
padding: 10px 14px;
|
|
2722
|
-
font: inherit;
|
|
2723
|
-
font-size: 0.88rem;
|
|
2724
|
-
font-weight: 700;
|
|
2725
|
-
cursor: pointer;
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
.editor-copy-button:hover {
|
|
2729
|
-
border-color: rgba(102, 82, 43, 0.3);
|
|
2730
|
-
background: #fff;
|
|
2731
|
-
}
|
|
2732
|
-
|
|
2733
|
-
.editor-pill,
|
|
2734
|
-
.editor-route-method {
|
|
2735
|
-
display: inline-flex;
|
|
2736
|
-
align-items: center;
|
|
2737
|
-
justify-content: center;
|
|
2738
|
-
min-height: 42px;
|
|
2739
|
-
padding: 0 16px;
|
|
2740
|
-
border-radius: 999px;
|
|
2741
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
2742
|
-
background: rgba(255, 255, 255, 0.9);
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
.editor-pill[data-difficulty] {
|
|
2746
|
-
border-color: rgba(219, 193, 122, 0.44);
|
|
2747
|
-
background: rgba(219, 193, 122, 0.18);
|
|
2748
|
-
color: #6f5514;
|
|
2749
|
-
font-weight: 800;
|
|
2750
|
-
}
|
|
2751
|
-
|
|
2752
|
-
.editor-pill[data-difficulty="hard"],
|
|
2753
|
-
.editor-pill[data-difficulty="expert"] {
|
|
2754
|
-
border-color: rgba(134, 92, 255, 0.3);
|
|
2755
|
-
background: rgba(134, 92, 255, 0.1);
|
|
2756
|
-
color: #5942a8;
|
|
2757
|
-
}
|
|
2758
|
-
|
|
2759
|
-
.editor-grid {
|
|
2760
|
-
display: grid;
|
|
2761
|
-
grid-template-columns: minmax(0, 1fr);
|
|
2762
|
-
gap: 18px;
|
|
2763
|
-
min-height: 0;
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
.editor-section {
|
|
2767
|
-
display: grid;
|
|
2768
|
-
align-content: start;
|
|
2769
|
-
gap: 16px;
|
|
2770
|
-
padding: 22px;
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
|
-
.editor-section h2,
|
|
2774
|
-
.editor-section h3 {
|
|
2775
|
-
margin: 0;
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
|
-
.editor-section h2 {
|
|
2779
|
-
font-size: 1.55rem;
|
|
2780
|
-
letter-spacing: -0.04em;
|
|
2781
|
-
}
|
|
2782
|
-
|
|
2783
|
-
.editor-section h3 {
|
|
2784
|
-
font-size: 1rem;
|
|
2785
|
-
color: #243041;
|
|
2786
|
-
}
|
|
2787
|
-
|
|
2788
|
-
.editor-section-header {
|
|
2789
|
-
display: grid;
|
|
2790
|
-
gap: 6px;
|
|
2791
|
-
}
|
|
2792
|
-
|
|
2793
|
-
.editor-section-header p,
|
|
2794
|
-
.editor-note {
|
|
2795
|
-
margin: 0;
|
|
2796
|
-
color: #617087;
|
|
2797
|
-
line-height: 1.7;
|
|
2798
|
-
}
|
|
2799
|
-
|
|
2800
|
-
.editor-route-card {
|
|
2801
|
-
display: grid;
|
|
2802
|
-
gap: 10px;
|
|
2803
|
-
padding: 16px;
|
|
2804
|
-
border-radius: 20px;
|
|
2805
|
-
border: 1px solid rgba(191, 164, 109, 0.22);
|
|
2806
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2807
|
-
}
|
|
2808
|
-
|
|
2809
|
-
.editor-route-head {
|
|
2810
|
-
display: flex;
|
|
2811
|
-
flex-wrap: wrap;
|
|
2812
|
-
align-items: center;
|
|
2813
|
-
gap: 10px;
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
|
-
.editor-route-method {
|
|
2817
|
-
min-height: 30px;
|
|
2818
|
-
padding: 0 10px;
|
|
2819
|
-
font-size: 0.77rem;
|
|
2820
|
-
font-weight: 800;
|
|
2821
|
-
}
|
|
2822
|
-
|
|
2823
|
-
.editor-route-method[data-method="POST"] {
|
|
2824
|
-
color: #7a4b16;
|
|
2825
|
-
}
|
|
2826
|
-
|
|
2827
|
-
.editor-route-method[data-method="GET"] {
|
|
2828
|
-
color: #1e5c52;
|
|
2829
|
-
}
|
|
2830
|
-
|
|
2831
|
-
.editor-route-path {
|
|
2832
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
2833
|
-
font-size: 0.82rem;
|
|
2834
|
-
line-height: 1.5;
|
|
2835
|
-
word-break: break-all;
|
|
2836
|
-
color: #324156;
|
|
2837
|
-
}
|
|
2838
|
-
|
|
2839
|
-
.editor-code-block {
|
|
2840
|
-
margin: 0;
|
|
2841
|
-
padding: 14px 16px;
|
|
2842
|
-
border-radius: 18px;
|
|
2843
|
-
background: rgba(37, 45, 61, 0.97);
|
|
2844
|
-
color: #f7efe1;
|
|
2845
|
-
overflow: auto;
|
|
2846
|
-
font-size: 0.82rem;
|
|
2847
|
-
line-height: 1.55;
|
|
2848
|
-
}
|
|
2849
|
-
|
|
2850
|
-
.editor-quickstart-grid {
|
|
2851
|
-
display: grid;
|
|
2852
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2853
|
-
gap: 14px;
|
|
2854
|
-
}
|
|
2855
|
-
|
|
2856
|
-
.editor-quickstart-field {
|
|
2857
|
-
display: grid;
|
|
2858
|
-
gap: 8px;
|
|
2859
|
-
min-width: 0;
|
|
2860
|
-
}
|
|
2861
|
-
|
|
2862
|
-
.editor-quickstart-field-head {
|
|
2863
|
-
display: flex;
|
|
2864
|
-
align-items: center;
|
|
2865
|
-
justify-content: flex-start;
|
|
2866
|
-
gap: 8px;
|
|
2867
|
-
min-width: 0;
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
.editor-quickstart-field-head span {
|
|
2871
|
-
color: #324156;
|
|
2872
|
-
font-size: 0.82rem;
|
|
2873
|
-
font-weight: 800;
|
|
2874
|
-
}
|
|
2875
|
-
|
|
2876
|
-
.editor-quickstart-copy {
|
|
2877
|
-
appearance: none;
|
|
2878
|
-
box-sizing: border-box;
|
|
2879
|
-
display: inline-flex;
|
|
2880
|
-
align-items: center;
|
|
2881
|
-
justify-content: center;
|
|
2882
|
-
width: 36px;
|
|
2883
|
-
min-width: 36px;
|
|
2884
|
-
height: 36px;
|
|
2885
|
-
min-height: 36px;
|
|
2886
|
-
flex: 0 0 36px;
|
|
2887
|
-
gap: 0;
|
|
2888
|
-
padding: 0;
|
|
2889
|
-
border-radius: 999px;
|
|
2890
|
-
border: 1px solid rgba(171, 138, 68, 0.48);
|
|
2891
|
-
background: #dbc17a;
|
|
2892
|
-
color: #243041;
|
|
2893
|
-
cursor: pointer;
|
|
2894
|
-
line-height: 1;
|
|
2895
|
-
box-shadow:
|
|
2896
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.58),
|
|
2897
|
-
0 6px 14px rgba(102, 82, 43, 0.12);
|
|
2898
|
-
transition:
|
|
2899
|
-
transform 120ms ease,
|
|
2900
|
-
border-color 120ms ease,
|
|
2901
|
-
background 120ms ease,
|
|
2902
|
-
color 120ms ease;
|
|
2903
|
-
}
|
|
2904
|
-
|
|
2905
|
-
.editor-quickstart-copy:hover {
|
|
2906
|
-
border-color: rgba(102, 82, 43, 0.44);
|
|
2907
|
-
background: #e4cb84;
|
|
2908
|
-
color: #243041;
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
.editor-quickstart-copy:active {
|
|
2912
|
-
transform: scale(0.94);
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
.editor-quickstart-copy::after {
|
|
2916
|
-
content: "✓";
|
|
2917
|
-
display: none;
|
|
2918
|
-
color: currentColor;
|
|
2919
|
-
font-size: 1rem;
|
|
2920
|
-
font-weight: 900;
|
|
2921
|
-
line-height: 1;
|
|
2922
|
-
}
|
|
2923
|
-
|
|
2924
|
-
.editor-quickstart-copy[data-copying="true"] {
|
|
2925
|
-
border-color: rgba(102, 82, 43, 0.5);
|
|
2926
|
-
background: #ead89d;
|
|
2927
|
-
color: #243041;
|
|
2928
|
-
transform: scale(0.96);
|
|
2929
|
-
}
|
|
2930
|
-
|
|
2931
|
-
.editor-quickstart-copy[data-copied="true"] {
|
|
2932
|
-
border-color: rgba(30, 92, 82, 0.42);
|
|
2933
|
-
background: #b9e1cf;
|
|
2934
|
-
color: #1e5c52;
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
.editor-quickstart-copy[data-copy-error="true"] {
|
|
2938
|
-
border-color: rgba(155, 45, 45, 0.42);
|
|
2939
|
-
background: #f0c5bd;
|
|
2940
|
-
color: #8d2f24;
|
|
2941
|
-
}
|
|
2942
|
-
|
|
2943
|
-
.editor-quickstart-copy[data-copy-error="true"]::after {
|
|
2944
|
-
content: "!";
|
|
2945
|
-
display: block;
|
|
2946
|
-
}
|
|
2947
|
-
|
|
2948
|
-
.editor-quickstart-copy[data-copied="true"]::after {
|
|
2949
|
-
display: block;
|
|
2950
|
-
}
|
|
2951
|
-
|
|
2952
|
-
.editor-quickstart-copy[data-copy-error="true"] svg,
|
|
2953
|
-
.editor-quickstart-copy[data-copied="true"] svg {
|
|
2954
|
-
display: none;
|
|
2955
|
-
}
|
|
2956
|
-
|
|
2957
|
-
.editor-quickstart-copy svg {
|
|
2958
|
-
display: block;
|
|
2959
|
-
width: 17px;
|
|
2960
|
-
height: 17px;
|
|
2961
|
-
stroke: currentColor;
|
|
2962
|
-
}
|
|
2963
|
-
|
|
2964
|
-
.editor-quickstart-textarea {
|
|
2965
|
-
width: 100%;
|
|
2966
|
-
min-height: 190px;
|
|
2967
|
-
resize: vertical;
|
|
2968
|
-
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
2969
|
-
border-radius: 18px;
|
|
2970
|
-
background: rgba(255, 255, 255, 0.72);
|
|
2971
|
-
color: #324156;
|
|
2972
|
-
padding: 14px 15px;
|
|
2973
|
-
font: 0.82rem/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
2974
|
-
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.76);
|
|
2975
|
-
}
|
|
2976
|
-
|
|
2977
|
-
.editor-quickstart-textarea:focus {
|
|
2978
|
-
outline: 2px solid rgba(219, 193, 122, 0.38);
|
|
2979
|
-
outline-offset: 2px;
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
.editor-quickstart-samples {
|
|
2983
|
-
display: grid;
|
|
2984
|
-
gap: 8px;
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
|
-
.editor-quickstart-samples > h3 {
|
|
2988
|
-
margin: 2px 0 0;
|
|
2989
|
-
}
|
|
2990
|
-
|
|
2991
|
-
.editor-sample-prompt-row {
|
|
2992
|
-
display: flex;
|
|
2993
|
-
gap: 10px;
|
|
2994
|
-
overflow-x: auto;
|
|
2995
|
-
overflow-y: hidden;
|
|
2996
|
-
padding: 2px 0 8px;
|
|
2997
|
-
scrollbar-width: thin;
|
|
2998
|
-
}
|
|
2999
|
-
|
|
3000
|
-
.editor-sample-prompt {
|
|
3001
|
-
flex: 0 0 min(360px, 86vw);
|
|
3002
|
-
}
|
|
3003
|
-
|
|
3004
|
-
.editor-sample-prompt .editor-quickstart-textarea {
|
|
3005
|
-
min-height: 150px;
|
|
3006
|
-
resize: none;
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
.editor-examples-row {
|
|
3010
|
-
display: flex;
|
|
3011
|
-
gap: 12px;
|
|
3012
|
-
overflow-x: auto;
|
|
3013
|
-
overflow-y: hidden;
|
|
3014
|
-
padding: 2px 0 8px;
|
|
3015
|
-
scrollbar-width: thin;
|
|
3016
|
-
}
|
|
3017
|
-
|
|
3018
|
-
.editor-example-card {
|
|
3019
|
-
flex: 0 0 min(240px, 76vw);
|
|
3020
|
-
display: grid;
|
|
3021
|
-
gap: 10px;
|
|
3022
|
-
padding: 10px;
|
|
3023
|
-
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
3024
|
-
border-radius: 18px;
|
|
3025
|
-
background: rgba(255, 255, 255, 0.72);
|
|
3026
|
-
}
|
|
3027
|
-
|
|
3028
|
-
.editor-example-media {
|
|
3029
|
-
position: relative;
|
|
3030
|
-
width: 100%;
|
|
3031
|
-
aspect-ratio: 9 / 12;
|
|
3032
|
-
padding: 0;
|
|
3033
|
-
border: 1px solid rgba(209, 219, 233, 0.96);
|
|
3034
|
-
border-radius: 14px;
|
|
3035
|
-
overflow: hidden;
|
|
3036
|
-
background: rgba(37, 45, 61, 0.95);
|
|
3037
|
-
cursor: zoom-in;
|
|
3038
|
-
}
|
|
3039
|
-
|
|
3040
|
-
.editor-example-media img,
|
|
3041
|
-
.editor-example-media video {
|
|
3042
|
-
width: 100%;
|
|
3043
|
-
height: 100%;
|
|
3044
|
-
display: block;
|
|
3045
|
-
object-fit: cover;
|
|
3046
|
-
}
|
|
3047
|
-
|
|
3048
|
-
.editor-example-note {
|
|
3049
|
-
margin: 0;
|
|
3050
|
-
color: #617087;
|
|
3051
|
-
font-size: 0.82rem;
|
|
3052
|
-
line-height: 1.45;
|
|
3053
|
-
}
|
|
3054
|
-
|
|
3055
|
-
.editor-example-empty {
|
|
3056
|
-
padding: 18px;
|
|
3057
|
-
border-radius: 18px;
|
|
3058
|
-
border: 1px dashed rgba(191, 164, 109, 0.34);
|
|
3059
|
-
background: rgba(255, 255, 255, 0.56);
|
|
3060
|
-
color: #617087;
|
|
3061
|
-
}
|
|
3062
|
-
|
|
3063
|
-
.editor-history-toolbar {
|
|
3064
|
-
display: flex;
|
|
3065
|
-
flex-wrap: wrap;
|
|
3066
|
-
align-items: center;
|
|
3067
|
-
justify-content: space-between;
|
|
3068
|
-
gap: 12px;
|
|
3069
|
-
margin-bottom: 18px;
|
|
3070
|
-
}
|
|
3071
|
-
|
|
3072
|
-
.editor-history-toolbar p {
|
|
3073
|
-
margin: 0;
|
|
3074
|
-
}
|
|
3075
|
-
|
|
3076
|
-
.editor-history-chip-row {
|
|
3077
|
-
display: flex;
|
|
3078
|
-
flex-wrap: wrap;
|
|
3079
|
-
gap: 8px;
|
|
3080
|
-
}
|
|
3081
|
-
|
|
3082
|
-
.editor-history-toolbar-actions,
|
|
3083
|
-
.editor-history-group-actions {
|
|
3084
|
-
display: flex;
|
|
3085
|
-
flex-wrap: wrap;
|
|
3086
|
-
align-items: center;
|
|
3087
|
-
gap: 8px;
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
.editor-history-toolbar-actions {
|
|
3091
|
-
justify-content: flex-end;
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
.editor-history-button,
|
|
3095
|
-
.editor-history-remove {
|
|
3096
|
-
display: inline-flex;
|
|
3097
|
-
align-items: center;
|
|
3098
|
-
justify-content: center;
|
|
3099
|
-
min-height: 32px;
|
|
3100
|
-
padding: 0 12px;
|
|
3101
|
-
border-radius: 999px;
|
|
3102
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
3103
|
-
background: rgba(255, 255, 255, 0.92);
|
|
3104
|
-
color: #324156;
|
|
3105
|
-
font-size: 0.78rem;
|
|
3106
|
-
font-weight: 700;
|
|
3107
|
-
cursor: pointer;
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
.editor-history-remove {
|
|
3111
|
-
color: #8d4a3b;
|
|
3112
|
-
}
|
|
3113
|
-
|
|
3114
|
-
.editor-history-button.is-primary {
|
|
3115
|
-
background: #dbc17a;
|
|
3116
|
-
color: #243041;
|
|
3117
|
-
}
|
|
3118
|
-
|
|
3119
|
-
.editor-history-refresh {
|
|
3120
|
-
width: 30px;
|
|
3121
|
-
min-height: 30px;
|
|
3122
|
-
padding: 0;
|
|
3123
|
-
border-radius: 999px;
|
|
3124
|
-
}
|
|
3125
|
-
|
|
3126
|
-
.editor-history-refresh svg {
|
|
3127
|
-
width: 15px;
|
|
3128
|
-
height: 15px;
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
.editor-history-refresh[data-loading="true"] svg {
|
|
3132
|
-
animation: editor-history-refresh-spin 0.8s linear infinite;
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
|
-
.editor-history-menu-wrap {
|
|
3136
|
-
position: relative;
|
|
3137
|
-
display: inline-flex;
|
|
3138
|
-
}
|
|
3139
|
-
|
|
3140
|
-
.editor-history-menu-button {
|
|
3141
|
-
width: 30px;
|
|
3142
|
-
min-height: 30px;
|
|
3143
|
-
padding: 0;
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
.editor-history-menu {
|
|
3147
|
-
position: absolute;
|
|
3148
|
-
right: 0;
|
|
3149
|
-
top: calc(100% + 6px);
|
|
3150
|
-
z-index: 20;
|
|
3151
|
-
display: none;
|
|
3152
|
-
min-width: 148px;
|
|
3153
|
-
padding: 6px;
|
|
3154
|
-
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
3155
|
-
border-radius: 10px;
|
|
3156
|
-
background: rgba(255, 252, 247, 0.98);
|
|
3157
|
-
box-shadow: 0 14px 34px rgba(37, 45, 61, 0.16);
|
|
3158
|
-
}
|
|
3159
|
-
|
|
3160
|
-
.editor-history-menu-wrap[data-open="true"] .editor-history-menu {
|
|
3161
|
-
display: grid;
|
|
3162
|
-
gap: 2px;
|
|
3163
|
-
}
|
|
3164
|
-
|
|
3165
|
-
.editor-history-menu button {
|
|
3166
|
-
all: unset;
|
|
3167
|
-
padding: 8px 9px;
|
|
3168
|
-
border-radius: 7px;
|
|
3169
|
-
color: #324156;
|
|
3170
|
-
font-size: 0.78rem;
|
|
3171
|
-
font-weight: 700;
|
|
3172
|
-
cursor: pointer;
|
|
3173
|
-
}
|
|
3174
|
-
|
|
3175
|
-
.editor-history-menu button:hover {
|
|
3176
|
-
background: rgba(219, 193, 122, 0.16);
|
|
3177
|
-
}
|
|
3178
|
-
|
|
3179
|
-
.editor-history-menu button[data-danger="true"] {
|
|
3180
|
-
color: #8d4a3b;
|
|
3181
|
-
}
|
|
3182
|
-
|
|
3183
|
-
.editor-history-group-previews {
|
|
3184
|
-
display: flex;
|
|
3185
|
-
flex-wrap: nowrap;
|
|
3186
|
-
gap: 6px;
|
|
3187
|
-
margin-top: 10px;
|
|
3188
|
-
overflow: hidden;
|
|
3189
|
-
}
|
|
3190
|
-
|
|
3191
|
-
.editor-history-group-previews.is-expanded {
|
|
3192
|
-
max-width: min(100%, calc(100vw - 160px));
|
|
3193
|
-
overflow-x: auto;
|
|
3194
|
-
overflow-y: hidden;
|
|
3195
|
-
padding-bottom: 5px;
|
|
3196
|
-
scrollbar-width: thin;
|
|
3197
|
-
}
|
|
3198
|
-
|
|
3199
|
-
.editor-history-group-preview {
|
|
3200
|
-
position: relative;
|
|
3201
|
-
width: 52px;
|
|
3202
|
-
height: 52px;
|
|
3203
|
-
padding: 0;
|
|
3204
|
-
border-radius: 9px;
|
|
3205
|
-
border: 1px solid rgba(191, 164, 109, 0.26);
|
|
3206
|
-
background: rgba(255, 255, 255, 0.74);
|
|
3207
|
-
overflow: hidden;
|
|
3208
|
-
flex: 0 0 auto;
|
|
3209
|
-
cursor: zoom-in;
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
|
-
.editor-history-group-preview img,
|
|
3213
|
-
.editor-history-group-preview video {
|
|
3214
|
-
width: 100%;
|
|
3215
|
-
height: 100%;
|
|
3216
|
-
object-fit: cover;
|
|
3217
|
-
display: block;
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
.editor-history-group-preview-more {
|
|
3221
|
-
display: inline-flex;
|
|
3222
|
-
align-items: center;
|
|
3223
|
-
justify-content: center;
|
|
3224
|
-
padding: 0;
|
|
3225
|
-
width: 52px;
|
|
3226
|
-
height: 52px;
|
|
3227
|
-
border-radius: 9px;
|
|
3228
|
-
border: 1px dashed rgba(191, 164, 109, 0.32);
|
|
3229
|
-
color: #617087;
|
|
3230
|
-
font-size: 0.75rem;
|
|
3231
|
-
font-weight: 800;
|
|
3232
|
-
flex: 0 0 auto;
|
|
3233
|
-
cursor: pointer;
|
|
3234
|
-
}
|
|
3235
|
-
|
|
3236
|
-
button.editor-history-group-preview-more:hover,
|
|
3237
|
-
button.editor-history-group-preview-more:focus-visible {
|
|
3238
|
-
border-style: solid;
|
|
3239
|
-
color: #324156;
|
|
3240
|
-
background: rgba(255, 255, 255, 0.9);
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
.editor-history-chip {
|
|
3244
|
-
display: inline-flex;
|
|
3245
|
-
align-items: center;
|
|
3246
|
-
min-height: 30px;
|
|
3247
|
-
padding: 0 10px;
|
|
3248
|
-
border-radius: 999px;
|
|
3249
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
3250
|
-
background: rgba(255, 255, 255, 0.9);
|
|
3251
|
-
color: #3b465a;
|
|
3252
|
-
font-size: 0.78rem;
|
|
3253
|
-
font-weight: 700;
|
|
3254
|
-
}
|
|
3255
|
-
|
|
3256
|
-
.editor-history-chip.is-empty {
|
|
3257
|
-
color: #74829c;
|
|
3258
|
-
border-style: dashed;
|
|
3259
|
-
}
|
|
3260
|
-
|
|
3261
|
-
.editor-history-groups {
|
|
3262
|
-
display: grid;
|
|
3263
|
-
gap: 12px;
|
|
3264
|
-
}
|
|
3265
|
-
|
|
3266
|
-
.editor-history-empty {
|
|
3267
|
-
padding: 18px;
|
|
3268
|
-
border-radius: 18px;
|
|
3269
|
-
border: 1px dashed rgba(191, 164, 109, 0.34);
|
|
3270
|
-
background: rgba(255, 255, 255, 0.56);
|
|
3271
|
-
color: #617087;
|
|
3272
|
-
}
|
|
3273
|
-
|
|
3274
|
-
.editor-history-group {
|
|
3275
|
-
border: 1px solid rgba(191, 164, 109, 0.22);
|
|
3276
|
-
border-radius: 20px;
|
|
3277
|
-
background: rgba(255, 255, 255, 0.62);
|
|
3278
|
-
overflow: hidden;
|
|
3279
|
-
}
|
|
3280
|
-
|
|
3281
|
-
.editor-history-group > summary {
|
|
3282
|
-
display: flex;
|
|
3283
|
-
align-items: center;
|
|
3284
|
-
justify-content: space-between;
|
|
3285
|
-
gap: 12px;
|
|
3286
|
-
padding: 14px 16px;
|
|
3287
|
-
cursor: pointer;
|
|
3288
|
-
list-style: none;
|
|
3289
|
-
}
|
|
3290
|
-
|
|
3291
|
-
.editor-history-group > summary::-webkit-details-marker {
|
|
3292
|
-
display: none;
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3295
|
-
.editor-history-group-main {
|
|
3296
|
-
display: grid;
|
|
3297
|
-
gap: 4px;
|
|
3298
|
-
min-width: 0;
|
|
3299
|
-
}
|
|
3300
|
-
|
|
3301
|
-
.editor-history-group-main h3 {
|
|
3302
|
-
margin: 0;
|
|
3303
|
-
font-size: 1rem;
|
|
3304
|
-
color: #243041;
|
|
3305
|
-
word-break: break-all;
|
|
3306
|
-
}
|
|
3307
|
-
|
|
3308
|
-
.editor-history-group-main p {
|
|
3309
|
-
margin: 0;
|
|
3310
|
-
color: #74829c;
|
|
3311
|
-
font-size: 0.82rem;
|
|
3312
|
-
}
|
|
3313
|
-
|
|
3314
|
-
.editor-history-group-toggle {
|
|
3315
|
-
color: #617087;
|
|
3316
|
-
font-size: 0.8rem;
|
|
3317
|
-
font-weight: 700;
|
|
3318
|
-
letter-spacing: 0.08em;
|
|
3319
|
-
text-transform: uppercase;
|
|
3320
|
-
}
|
|
3321
|
-
|
|
3322
|
-
.editor-history-active {
|
|
3323
|
-
display: inline-flex;
|
|
3324
|
-
align-items: center;
|
|
3325
|
-
min-height: 26px;
|
|
3326
|
-
padding: 0 9px;
|
|
3327
|
-
border-radius: 999px;
|
|
3328
|
-
background: rgba(219, 193, 122, 0.2);
|
|
3329
|
-
color: #7a4b16;
|
|
3330
|
-
font-size: 0.74rem;
|
|
3331
|
-
font-weight: 800;
|
|
3332
|
-
letter-spacing: 0.08em;
|
|
3333
|
-
text-transform: uppercase;
|
|
3334
|
-
}
|
|
3335
|
-
|
|
3336
|
-
@keyframes editor-history-refresh-spin {
|
|
3337
|
-
to {
|
|
3338
|
-
transform: rotate(360deg);
|
|
3339
|
-
}
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
.editor-history-group-body {
|
|
3343
|
-
display: grid;
|
|
3344
|
-
gap: 10px;
|
|
3345
|
-
padding: 0 16px 16px;
|
|
3346
|
-
}
|
|
3347
|
-
|
|
3348
|
-
.editor-history-card {
|
|
3349
|
-
display: grid;
|
|
3350
|
-
gap: 8px;
|
|
3351
|
-
padding: 10px;
|
|
3352
|
-
border-radius: 12px;
|
|
3353
|
-
border: 1px solid rgba(191, 164, 109, 0.18);
|
|
3354
|
-
background: rgba(255, 251, 244, 0.92);
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
.editor-history-card-head {
|
|
3358
|
-
display: flex;
|
|
3359
|
-
flex-wrap: wrap;
|
|
3360
|
-
justify-content: space-between;
|
|
3361
|
-
gap: 10px;
|
|
3362
|
-
}
|
|
3363
|
-
|
|
3364
|
-
.editor-history-card-main {
|
|
3365
|
-
display: grid;
|
|
3366
|
-
gap: 6px;
|
|
3367
|
-
min-width: 0;
|
|
3368
|
-
}
|
|
3369
|
-
|
|
3370
|
-
.editor-history-card-meta {
|
|
3371
|
-
display: flex;
|
|
3372
|
-
flex-wrap: wrap;
|
|
3373
|
-
gap: 8px;
|
|
3374
|
-
}
|
|
3375
|
-
|
|
3376
|
-
.editor-history-method,
|
|
3377
|
-
.editor-history-status {
|
|
3378
|
-
display: inline-flex;
|
|
3379
|
-
align-items: center;
|
|
3380
|
-
min-height: 28px;
|
|
3381
|
-
padding: 0 10px;
|
|
3382
|
-
border-radius: 999px;
|
|
3383
|
-
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
3384
|
-
background: rgba(255, 255, 255, 0.92);
|
|
3385
|
-
font-size: 0.78rem;
|
|
3386
|
-
font-weight: 800;
|
|
3387
|
-
}
|
|
3388
|
-
|
|
3389
|
-
.editor-history-method {
|
|
3390
|
-
color: #7a4b16;
|
|
3391
|
-
}
|
|
3392
|
-
|
|
3393
|
-
.editor-history-status.is-success {
|
|
3394
|
-
color: #1e5c52;
|
|
3395
|
-
}
|
|
3396
|
-
|
|
3397
|
-
.editor-history-status.is-error {
|
|
3398
|
-
color: #9b2d2d;
|
|
3399
|
-
}
|
|
3400
|
-
|
|
3401
|
-
.editor-history-card-path {
|
|
3402
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
3403
|
-
font-size: 0.8rem;
|
|
3404
|
-
line-height: 1.55;
|
|
3405
|
-
color: #324156;
|
|
3406
|
-
word-break: break-all;
|
|
3407
|
-
}
|
|
3408
|
-
|
|
3409
|
-
.editor-history-card-time {
|
|
3410
|
-
color: #74829c;
|
|
3411
|
-
font-size: 0.8rem;
|
|
3412
|
-
white-space: nowrap;
|
|
3413
|
-
}
|
|
3414
|
-
|
|
3415
|
-
.editor-history-card-previews {
|
|
3416
|
-
display: flex;
|
|
3417
|
-
flex-wrap: wrap;
|
|
3418
|
-
gap: 8px;
|
|
3419
|
-
}
|
|
3420
|
-
|
|
3421
|
-
.editor-history-card-preview {
|
|
3422
|
-
position: relative;
|
|
3423
|
-
width: 112px;
|
|
3424
|
-
height: 112px;
|
|
3425
|
-
display: inline-flex;
|
|
3426
|
-
align-items: center;
|
|
3427
|
-
justify-content: center;
|
|
3428
|
-
overflow: hidden;
|
|
3429
|
-
padding: 0;
|
|
3430
|
-
border-radius: 18px;
|
|
3431
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
3432
|
-
background: rgba(255, 255, 255, 0.94);
|
|
3433
|
-
color: #324156;
|
|
3434
|
-
text-decoration: none;
|
|
3435
|
-
cursor: pointer;
|
|
3436
|
-
}
|
|
3437
|
-
|
|
3438
|
-
.editor-history-video-play {
|
|
3439
|
-
position: absolute;
|
|
3440
|
-
left: 50%;
|
|
3441
|
-
top: 50%;
|
|
3442
|
-
width: 34px;
|
|
3443
|
-
height: 34px;
|
|
3444
|
-
transform: translate(-50%, -50%);
|
|
3445
|
-
display: inline-flex;
|
|
3446
|
-
align-items: center;
|
|
3447
|
-
justify-content: center;
|
|
3448
|
-
border-radius: 999px;
|
|
3449
|
-
background: rgba(18, 24, 34, 0.72);
|
|
3450
|
-
box-shadow: 0 8px 24px rgba(12, 18, 28, 0.22);
|
|
3451
|
-
pointer-events: none;
|
|
3452
|
-
}
|
|
3453
|
-
|
|
3454
|
-
.editor-history-group-preview .editor-history-video-play {
|
|
3455
|
-
width: 24px;
|
|
3456
|
-
height: 24px;
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
.editor-history-video-play::before {
|
|
3460
|
-
content: "";
|
|
3461
|
-
display: block;
|
|
3462
|
-
width: 0;
|
|
3463
|
-
height: 0;
|
|
3464
|
-
margin-left: 3px;
|
|
3465
|
-
border-top: 7px solid transparent;
|
|
3466
|
-
border-bottom: 7px solid transparent;
|
|
3467
|
-
border-left: 11px solid #fff8ee;
|
|
3468
|
-
}
|
|
3469
|
-
|
|
3470
|
-
.editor-history-group-preview .editor-history-video-play::before {
|
|
3471
|
-
margin-left: 2px;
|
|
3472
|
-
border-top-width: 5px;
|
|
3473
|
-
border-bottom-width: 5px;
|
|
3474
|
-
border-left-width: 8px;
|
|
3475
|
-
}
|
|
3476
|
-
|
|
3477
|
-
.editor-history-card-preview img,
|
|
3478
|
-
.editor-history-card-preview video {
|
|
3479
|
-
width: 100%;
|
|
3480
|
-
height: 100%;
|
|
3481
|
-
object-fit: cover;
|
|
3482
|
-
display: block;
|
|
3483
|
-
}
|
|
3484
|
-
|
|
3485
|
-
.editor-history-card-preview-label {
|
|
3486
|
-
position: absolute;
|
|
3487
|
-
left: 8px;
|
|
3488
|
-
right: 8px;
|
|
3489
|
-
bottom: 8px;
|
|
3490
|
-
min-height: 22px;
|
|
3491
|
-
display: inline-flex;
|
|
3492
|
-
align-items: center;
|
|
3493
|
-
justify-content: center;
|
|
3494
|
-
padding: 0 8px;
|
|
3495
|
-
border-radius: 999px;
|
|
3496
|
-
background: rgba(24, 30, 42, 0.72);
|
|
3497
|
-
color: #fff8ee;
|
|
3498
|
-
font-size: 0.72rem;
|
|
3499
|
-
font-weight: 700;
|
|
3500
|
-
letter-spacing: 0.02em;
|
|
3501
|
-
}
|
|
3502
|
-
|
|
3503
|
-
.editor-history-card-preview-fallback {
|
|
3504
|
-
padding: 0 10px;
|
|
3505
|
-
text-align: center;
|
|
3506
|
-
font-size: 0.78rem;
|
|
3507
|
-
font-weight: 700;
|
|
3508
|
-
line-height: 1.4;
|
|
3509
|
-
}
|
|
3510
|
-
|
|
3511
|
-
.editor-history-card-grid {
|
|
3512
|
-
display: grid;
|
|
3513
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3514
|
-
gap: 12px;
|
|
3515
|
-
}
|
|
3516
|
-
|
|
3517
|
-
.editor-history-card .vf-editor-chat-http-card {
|
|
3518
|
-
background: rgba(248, 244, 235, 0.74);
|
|
3519
|
-
}
|
|
3520
|
-
|
|
3521
|
-
.editor-history-card-foot {
|
|
3522
|
-
display: flex;
|
|
3523
|
-
justify-content: flex-end;
|
|
3524
|
-
color: #7b879b;
|
|
3525
|
-
font-size: 0.72rem;
|
|
3526
|
-
line-height: 1.3;
|
|
3527
|
-
}
|
|
3528
|
-
|
|
3529
|
-
.editor-history-sample {
|
|
3530
|
-
display: grid;
|
|
3531
|
-
gap: 8px;
|
|
3532
|
-
}
|
|
3533
|
-
|
|
3534
|
-
.editor-history-sample h4 {
|
|
3535
|
-
margin: 0;
|
|
3536
|
-
font-size: 0.82rem;
|
|
3537
|
-
color: #445066;
|
|
3538
|
-
text-transform: uppercase;
|
|
3539
|
-
letter-spacing: 0.08em;
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
|
-
.editor-history-sample pre {
|
|
3543
|
-
margin: 0;
|
|
3544
|
-
min-height: 90px;
|
|
3545
|
-
max-height: 280px;
|
|
3546
|
-
overflow: auto;
|
|
3547
|
-
padding: 12px;
|
|
3548
|
-
border-radius: 14px;
|
|
3549
|
-
background: rgba(37, 45, 61, 0.96);
|
|
3550
|
-
color: #f7efe1;
|
|
3551
|
-
font-size: 0.78rem;
|
|
3552
|
-
line-height: 1.5;
|
|
3553
|
-
}
|
|
3554
|
-
|
|
3555
|
-
.editor-history-sample-note {
|
|
3556
|
-
color: #74829c;
|
|
3557
|
-
font-size: 0.78rem;
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
.editor-modal-backdrop {
|
|
3561
|
-
position: fixed;
|
|
3562
|
-
inset: 0;
|
|
3563
|
-
display: none;
|
|
3564
|
-
align-items: center;
|
|
3565
|
-
justify-content: center;
|
|
3566
|
-
padding: 24px;
|
|
3567
|
-
background: rgba(24, 30, 42, 0.52);
|
|
3568
|
-
backdrop-filter: blur(10px);
|
|
3569
|
-
z-index: 1200;
|
|
3570
|
-
}
|
|
3571
|
-
|
|
3572
|
-
.editor-modal-backdrop[data-open="true"] {
|
|
3573
|
-
display: flex;
|
|
3574
|
-
}
|
|
3575
|
-
|
|
3576
|
-
.editor-modal {
|
|
3577
|
-
width: min(100%, 460px);
|
|
3578
|
-
display: grid;
|
|
3579
|
-
gap: 16px;
|
|
3580
|
-
padding: 22px;
|
|
3581
|
-
border-radius: 26px;
|
|
3582
|
-
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
3583
|
-
background: rgba(255, 251, 244, 0.98);
|
|
3584
|
-
box-shadow: 0 24px 64px rgba(15, 23, 42, 0.22);
|
|
3585
|
-
}
|
|
3586
|
-
|
|
3587
|
-
.editor-modal-head {
|
|
3588
|
-
display: grid;
|
|
3589
|
-
gap: 6px;
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
.editor-modal-head h3 {
|
|
3593
|
-
margin: 0;
|
|
3594
|
-
font-size: 1.2rem;
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
|
-
.editor-modal-head p,
|
|
3598
|
-
.editor-modal-error,
|
|
3599
|
-
.editor-modal-label {
|
|
3600
|
-
margin: 0;
|
|
3601
|
-
color: #617087;
|
|
3602
|
-
line-height: 1.6;
|
|
3603
|
-
}
|
|
3604
|
-
|
|
3605
|
-
.editor-modal-label {
|
|
3606
|
-
font-size: 0.82rem;
|
|
3607
|
-
font-weight: 700;
|
|
3608
|
-
letter-spacing: 0.08em;
|
|
3609
|
-
text-transform: uppercase;
|
|
3610
|
-
}
|
|
3611
|
-
|
|
3612
|
-
.editor-modal-input {
|
|
3613
|
-
width: 100%;
|
|
3614
|
-
min-height: 50px;
|
|
3615
|
-
padding: 0 14px;
|
|
3616
|
-
border-radius: 16px;
|
|
3617
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
3618
|
-
background: rgba(255, 255, 255, 0.96);
|
|
3619
|
-
color: #243041;
|
|
3620
|
-
font: inherit;
|
|
3621
|
-
}
|
|
3622
|
-
|
|
3623
|
-
.editor-modal-input:focus {
|
|
3624
|
-
outline: 2px solid rgba(219, 193, 122, 0.7);
|
|
3625
|
-
outline-offset: 2px;
|
|
3626
|
-
}
|
|
3627
|
-
|
|
3628
|
-
.editor-modal-error {
|
|
3629
|
-
min-height: 1.4em;
|
|
3630
|
-
color: #9b2d2d;
|
|
3631
|
-
font-size: 0.84rem;
|
|
3632
|
-
}
|
|
3633
|
-
|
|
3634
|
-
.editor-modal-actions {
|
|
3635
|
-
display: flex;
|
|
3636
|
-
justify-content: flex-end;
|
|
3637
|
-
gap: 10px;
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
.editor-media-modal {
|
|
3641
|
-
width: min(100%, 980px);
|
|
3642
|
-
max-height: calc(100vh - 48px);
|
|
3643
|
-
display: grid;
|
|
3644
|
-
gap: 14px;
|
|
3645
|
-
padding: 18px;
|
|
3646
|
-
border-radius: 28px;
|
|
3647
|
-
border: 1px solid rgba(191, 164, 109, 0.28);
|
|
3648
|
-
background: rgba(255, 251, 244, 0.985);
|
|
3649
|
-
box-shadow: 0 28px 80px rgba(15, 23, 42, 0.28);
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
|
-
.editor-media-modal-head {
|
|
3653
|
-
display: flex;
|
|
3654
|
-
align-items: center;
|
|
3655
|
-
justify-content: space-between;
|
|
3656
|
-
gap: 12px;
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
|
-
.editor-media-modal-actions {
|
|
3660
|
-
display: inline-flex;
|
|
3661
|
-
align-items: center;
|
|
3662
|
-
gap: 10px;
|
|
3663
|
-
flex: 0 0 auto;
|
|
3664
|
-
}
|
|
3665
|
-
|
|
3666
|
-
.editor-media-modal-carousel {
|
|
3667
|
-
display: none;
|
|
3668
|
-
align-items: center;
|
|
3669
|
-
gap: 6px;
|
|
3670
|
-
}
|
|
3671
|
-
|
|
3672
|
-
.editor-media-modal-carousel[data-visible="true"] {
|
|
3673
|
-
display: inline-flex;
|
|
3674
|
-
}
|
|
3675
|
-
|
|
3676
|
-
.editor-media-modal-source {
|
|
3677
|
-
display: inline-flex;
|
|
3678
|
-
align-items: center;
|
|
3679
|
-
gap: 6px;
|
|
3680
|
-
border: 0;
|
|
3681
|
-
background: transparent;
|
|
3682
|
-
color: #617087;
|
|
3683
|
-
font-size: 0.82rem;
|
|
3684
|
-
font-weight: 700;
|
|
3685
|
-
font-family: inherit;
|
|
3686
|
-
text-decoration: underline;
|
|
3687
|
-
text-underline-offset: 0.14em;
|
|
3688
|
-
cursor: pointer;
|
|
3689
|
-
}
|
|
3690
|
-
|
|
3691
|
-
.editor-media-modal-source svg {
|
|
3692
|
-
width: 16px;
|
|
3693
|
-
height: 16px;
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
|
-
.editor-media-modal-source[data-state="done"] {
|
|
3697
|
-
color: #2f7d4f;
|
|
3698
|
-
}
|
|
3699
|
-
|
|
3700
|
-
.editor-media-modal-source[data-state="error"] {
|
|
3701
|
-
color: #a14444;
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
|
-
.editor-media-modal-source[data-visible="false"] {
|
|
3705
|
-
display: none;
|
|
3706
|
-
}
|
|
3707
|
-
|
|
3708
|
-
.editor-media-modal-title {
|
|
3709
|
-
margin: 0;
|
|
3710
|
-
color: #324156;
|
|
3711
|
-
font-size: 0.95rem;
|
|
3712
|
-
font-weight: 700;
|
|
3713
|
-
word-break: break-all;
|
|
3714
|
-
}
|
|
3715
|
-
|
|
3716
|
-
.editor-media-modal-body {
|
|
3717
|
-
min-height: 0;
|
|
3718
|
-
display: grid;
|
|
3719
|
-
place-items: center;
|
|
3720
|
-
overflow: auto;
|
|
3721
|
-
border-radius: 20px;
|
|
3722
|
-
background: rgba(37, 45, 61, 0.96);
|
|
3723
|
-
}
|
|
3724
|
-
|
|
3725
|
-
.editor-media-modal-body img,
|
|
3726
|
-
.editor-media-modal-body video {
|
|
3727
|
-
max-width: 100%;
|
|
3728
|
-
max-height: calc(100vh - 180px);
|
|
3729
|
-
display: block;
|
|
3730
|
-
object-fit: contain;
|
|
3731
|
-
}
|
|
3732
|
-
|
|
3733
|
-
.editor-inpaint-modal-backdrop {
|
|
3734
|
-
position: fixed;
|
|
3735
|
-
inset: 0;
|
|
3736
|
-
display: none;
|
|
3737
|
-
align-items: center;
|
|
3738
|
-
justify-content: center;
|
|
3739
|
-
padding: 18px;
|
|
3740
|
-
background: rgba(15, 23, 42, 0.62);
|
|
3741
|
-
backdrop-filter: blur(12px);
|
|
3742
|
-
z-index: 1300;
|
|
3743
|
-
}
|
|
3744
|
-
|
|
3745
|
-
.editor-inpaint-modal-backdrop[data-open="true"] {
|
|
3746
|
-
display: flex;
|
|
3747
|
-
}
|
|
3748
|
-
|
|
3749
|
-
.editor-inpaint-modal {
|
|
3750
|
-
width: min(100%, 1180px);
|
|
3751
|
-
max-height: calc(100vh - 36px);
|
|
3752
|
-
display: grid;
|
|
3753
|
-
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
3754
|
-
gap: 14px;
|
|
3755
|
-
padding: 16px;
|
|
3756
|
-
border-radius: 24px;
|
|
3757
|
-
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
3758
|
-
background: rgba(255, 251, 244, 0.99);
|
|
3759
|
-
box-shadow: 0 32px 90px rgba(15, 23, 42, 0.32);
|
|
3760
|
-
}
|
|
3761
|
-
|
|
3762
|
-
.editor-inpaint-modal-head,
|
|
3763
|
-
.editor-inpaint-modal-actions,
|
|
3764
|
-
.editor-inpaint-toolbar,
|
|
3765
|
-
.editor-inpaint-color-row,
|
|
3766
|
-
.editor-inpaint-footer {
|
|
3767
|
-
display: flex;
|
|
3768
|
-
align-items: center;
|
|
3769
|
-
gap: 10px;
|
|
3770
|
-
}
|
|
3771
|
-
|
|
3772
|
-
.editor-inpaint-modal-head {
|
|
3773
|
-
justify-content: space-between;
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
.editor-inpaint-title {
|
|
3777
|
-
min-width: 0;
|
|
3778
|
-
display: grid;
|
|
3779
|
-
gap: 2px;
|
|
3780
|
-
}
|
|
3781
|
-
|
|
3782
|
-
.editor-inpaint-title h3 {
|
|
3783
|
-
margin: 0;
|
|
3784
|
-
color: #253041;
|
|
3785
|
-
font-size: 1.02rem;
|
|
3786
|
-
}
|
|
3787
|
-
|
|
3788
|
-
.editor-inpaint-title p,
|
|
3789
|
-
.editor-inpaint-label,
|
|
3790
|
-
.editor-inpaint-status {
|
|
3791
|
-
margin: 0;
|
|
3792
|
-
color: #617087;
|
|
3793
|
-
font-size: 0.8rem;
|
|
3794
|
-
line-height: 1.45;
|
|
3795
|
-
}
|
|
3796
|
-
|
|
3797
|
-
.editor-inpaint-source {
|
|
3798
|
-
overflow: hidden;
|
|
3799
|
-
text-overflow: ellipsis;
|
|
3800
|
-
white-space: nowrap;
|
|
3801
|
-
}
|
|
3802
|
-
|
|
3803
|
-
.editor-inpaint-body {
|
|
3804
|
-
min-height: 0;
|
|
3805
|
-
display: grid;
|
|
3806
|
-
grid-template-columns: minmax(0, 1fr) 340px;
|
|
3807
|
-
gap: 14px;
|
|
3808
|
-
}
|
|
3809
|
-
|
|
3810
|
-
.editor-inpaint-stage {
|
|
3811
|
-
min-height: 360px;
|
|
3812
|
-
display: grid;
|
|
3813
|
-
place-items: center;
|
|
3814
|
-
overflow: auto;
|
|
3815
|
-
border-radius: 18px;
|
|
3816
|
-
background:
|
|
3817
|
-
linear-gradient(45deg, rgba(255,255,255,0.06) 25%, transparent 25%),
|
|
3818
|
-
linear-gradient(-45deg, rgba(255,255,255,0.06) 25%, transparent 25%),
|
|
3819
|
-
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.06) 75%),
|
|
3820
|
-
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.06) 75%),
|
|
3821
|
-
#252d3d;
|
|
3822
|
-
background-size: 22px 22px;
|
|
3823
|
-
background-position: 0 0, 0 11px, 11px -11px, -11px 0;
|
|
3824
|
-
}
|
|
3825
|
-
|
|
3826
|
-
.editor-inpaint-canvas-wrap {
|
|
3827
|
-
position: relative;
|
|
3828
|
-
display: inline-block;
|
|
3829
|
-
max-width: 100%;
|
|
3830
|
-
line-height: 0;
|
|
3831
|
-
}
|
|
3832
|
-
|
|
3833
|
-
.editor-inpaint-canvas-wrap img {
|
|
3834
|
-
max-width: 100%;
|
|
3835
|
-
max-height: calc(100vh - 230px);
|
|
3836
|
-
display: block;
|
|
3837
|
-
object-fit: contain;
|
|
3838
|
-
user-select: none;
|
|
3839
|
-
}
|
|
3840
|
-
|
|
3841
|
-
.editor-inpaint-canvas-wrap canvas {
|
|
3842
|
-
position: absolute;
|
|
3843
|
-
inset: 0;
|
|
3844
|
-
width: 100%;
|
|
3845
|
-
height: 100%;
|
|
3846
|
-
opacity: 0.86;
|
|
3847
|
-
cursor: crosshair;
|
|
3848
|
-
touch-action: none;
|
|
3849
|
-
}
|
|
3850
|
-
|
|
3851
|
-
.editor-inpaint-panel {
|
|
3852
|
-
min-height: 0;
|
|
3853
|
-
display: grid;
|
|
3854
|
-
grid-template-rows: auto;
|
|
3855
|
-
align-content: start;
|
|
3856
|
-
gap: 12px;
|
|
3857
|
-
overflow: auto;
|
|
3858
|
-
padding: 12px;
|
|
3859
|
-
border-radius: 18px;
|
|
3860
|
-
border: 1px solid rgba(191, 164, 109, 0.24);
|
|
3861
|
-
background: rgba(255, 255, 255, 0.72);
|
|
3862
|
-
}
|
|
3863
|
-
|
|
3864
|
-
.editor-inpaint-toolbar {
|
|
3865
|
-
flex-wrap: wrap;
|
|
3866
|
-
}
|
|
3867
|
-
|
|
3868
|
-
.editor-inpaint-brush {
|
|
3869
|
-
display: grid;
|
|
3870
|
-
gap: 6px;
|
|
3871
|
-
min-width: 150px;
|
|
3872
|
-
}
|
|
3873
|
-
|
|
3874
|
-
.editor-inpaint-brush input[type="range"] {
|
|
3875
|
-
width: 100%;
|
|
3876
|
-
}
|
|
3877
|
-
|
|
3878
|
-
.editor-inpaint-color-picker {
|
|
3879
|
-
width: 42px;
|
|
3880
|
-
height: 38px;
|
|
3881
|
-
padding: 2px;
|
|
3882
|
-
border: 1px solid rgba(97, 112, 135, 0.24);
|
|
3883
|
-
border-radius: 12px;
|
|
3884
|
-
background: #fff;
|
|
3885
|
-
}
|
|
3886
|
-
|
|
3887
|
-
.editor-inpaint-swatches {
|
|
3888
|
-
display: none;
|
|
3889
|
-
}
|
|
3890
|
-
|
|
3891
|
-
.editor-inpaint-prompts {
|
|
3892
|
-
display: grid;
|
|
3893
|
-
align-content: start;
|
|
3894
|
-
gap: 10px;
|
|
3895
|
-
}
|
|
3896
|
-
|
|
3897
|
-
.editor-inpaint-provider {
|
|
3898
|
-
display: none;
|
|
3899
|
-
gap: 6px;
|
|
3900
|
-
}
|
|
3901
|
-
|
|
3902
|
-
.editor-inpaint-provider[data-visible="true"] {
|
|
3903
|
-
display: grid;
|
|
3904
|
-
}
|
|
3905
|
-
|
|
3906
|
-
.editor-inpaint-provider select {
|
|
3907
|
-
min-height: 38px;
|
|
3908
|
-
padding: 0 34px 0 12px;
|
|
3909
|
-
border-radius: 12px;
|
|
3910
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
3911
|
-
background: rgba(255, 255, 255, 0.96);
|
|
3912
|
-
color: #243041;
|
|
3913
|
-
font: inherit;
|
|
3914
|
-
font-weight: 800;
|
|
3915
|
-
}
|
|
3916
|
-
|
|
3917
|
-
.editor-inpaint-color-row {
|
|
3918
|
-
position: relative;
|
|
3919
|
-
align-items: flex-start;
|
|
3920
|
-
padding: 4px;
|
|
3921
|
-
border-radius: 16px;
|
|
3922
|
-
}
|
|
3923
|
-
|
|
3924
|
-
.editor-inpaint-color-tools {
|
|
3925
|
-
flex: 0 0 auto;
|
|
3926
|
-
display: grid;
|
|
3927
|
-
justify-items: center;
|
|
3928
|
-
gap: 6px;
|
|
3929
|
-
}
|
|
3930
|
-
|
|
3931
|
-
.editor-inpaint-prompt-stack {
|
|
3932
|
-
min-width: 0;
|
|
3933
|
-
flex: 1 1 auto;
|
|
3934
|
-
display: grid;
|
|
3935
|
-
align-content: start;
|
|
3936
|
-
gap: 8px;
|
|
3937
|
-
}
|
|
3938
|
-
|
|
3939
|
-
.editor-inpaint-global {
|
|
3940
|
-
display: grid;
|
|
3941
|
-
align-content: start;
|
|
3942
|
-
gap: 8px;
|
|
3943
|
-
}
|
|
3944
|
-
|
|
3945
|
-
.editor-inpaint-prompt-field {
|
|
3946
|
-
position: relative;
|
|
3947
|
-
min-width: 0;
|
|
3948
|
-
display: grid;
|
|
3949
|
-
}
|
|
3950
|
-
|
|
3951
|
-
.editor-inpaint-attachment-tools {
|
|
3952
|
-
display: flex;
|
|
3953
|
-
align-items: center;
|
|
3954
|
-
flex-wrap: wrap;
|
|
3955
|
-
gap: 6px;
|
|
3956
|
-
}
|
|
3957
|
-
|
|
3958
|
-
.editor-inpaint-attach-button {
|
|
3959
|
-
position: absolute;
|
|
3960
|
-
right: 8px;
|
|
3961
|
-
bottom: 8px;
|
|
3962
|
-
transform: translateY(-10px);
|
|
3963
|
-
width: 30px;
|
|
3964
|
-
height: 30px;
|
|
3965
|
-
display: inline-grid;
|
|
3966
|
-
place-items: center;
|
|
3967
|
-
padding: 0;
|
|
3968
|
-
border-radius: 999px;
|
|
3969
|
-
border: 1px solid transparent;
|
|
3970
|
-
background: rgba(255, 255, 255, 0.72);
|
|
3971
|
-
color: rgba(37, 48, 65, 0.56);
|
|
3972
|
-
font: inherit;
|
|
3973
|
-
cursor: pointer;
|
|
3974
|
-
}
|
|
3975
|
-
|
|
3976
|
-
.editor-inpaint-attach-button:hover,
|
|
3977
|
-
.editor-inpaint-attach-button:focus-visible {
|
|
3978
|
-
border-color: rgba(191, 164, 109, 0.34);
|
|
3979
|
-
background: rgba(255, 251, 244, 0.94);
|
|
3980
|
-
color: #253041;
|
|
3981
|
-
}
|
|
3982
|
-
|
|
3983
|
-
.editor-inpaint-attach-button svg {
|
|
3984
|
-
width: 16px;
|
|
3985
|
-
height: 16px;
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
.editor-inpaint-attachment-chips {
|
|
3989
|
-
display: flex;
|
|
3990
|
-
align-items: center;
|
|
3991
|
-
flex-wrap: wrap;
|
|
3992
|
-
gap: 6px;
|
|
3993
|
-
min-width: 0;
|
|
3994
|
-
}
|
|
3995
|
-
|
|
3996
|
-
.editor-inpaint-attachment-chip {
|
|
3997
|
-
min-width: 0;
|
|
3998
|
-
max-width: 210px;
|
|
3999
|
-
display: inline-flex;
|
|
4000
|
-
align-items: center;
|
|
4001
|
-
gap: 4px;
|
|
4002
|
-
padding: 1px 4px 1px 1px;
|
|
4003
|
-
border-radius: 999px;
|
|
4004
|
-
border: 1px solid rgba(191, 164, 109, 0.3);
|
|
4005
|
-
background: rgba(247, 241, 230, 0.82);
|
|
4006
|
-
color: #253041;
|
|
4007
|
-
font-size: 0.74rem;
|
|
4008
|
-
font-weight: 800;
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
.editor-inpaint-attachment-chip img {
|
|
4012
|
-
width: 22px;
|
|
4013
|
-
height: 22px;
|
|
4014
|
-
min-width: 22px;
|
|
4015
|
-
max-width: 22px;
|
|
4016
|
-
aspect-ratio: 1 / 1;
|
|
4017
|
-
flex: 0 0 22px;
|
|
4018
|
-
object-fit: cover;
|
|
4019
|
-
border-radius: 999px;
|
|
4020
|
-
background: rgba(37, 48, 65, 0.08);
|
|
4021
|
-
}
|
|
4022
|
-
|
|
4023
|
-
.editor-inpaint-attachment-chip span {
|
|
4024
|
-
min-width: 0;
|
|
4025
|
-
overflow: hidden;
|
|
4026
|
-
text-overflow: ellipsis;
|
|
4027
|
-
white-space: nowrap;
|
|
4028
|
-
}
|
|
4029
|
-
|
|
4030
|
-
.editor-inpaint-attachment-chip button {
|
|
4031
|
-
width: 18px;
|
|
4032
|
-
height: 18px;
|
|
4033
|
-
min-width: 18px;
|
|
4034
|
-
max-width: 18px;
|
|
4035
|
-
aspect-ratio: 1 / 1;
|
|
4036
|
-
flex: 0 0 18px;
|
|
4037
|
-
display: inline-grid;
|
|
4038
|
-
place-items: center;
|
|
4039
|
-
padding: 0;
|
|
4040
|
-
border: 0;
|
|
4041
|
-
border-radius: 999px;
|
|
4042
|
-
background: rgba(37, 48, 65, 0.1);
|
|
4043
|
-
color: #253041;
|
|
4044
|
-
font: inherit;
|
|
4045
|
-
line-height: 1;
|
|
4046
|
-
cursor: pointer;
|
|
4047
|
-
}
|
|
4048
|
-
|
|
4049
|
-
.editor-inpaint-color-row[data-active="true"] {
|
|
4050
|
-
background: rgba(219, 193, 122, 0.16);
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
|
-
.editor-inpaint-color-dot {
|
|
4054
|
-
width: 26px;
|
|
4055
|
-
height: 26px;
|
|
4056
|
-
min-width: 26px;
|
|
4057
|
-
max-width: 26px;
|
|
4058
|
-
aspect-ratio: 1 / 1;
|
|
4059
|
-
flex: 0 0 26px;
|
|
4060
|
-
margin-top: 8px;
|
|
4061
|
-
border-radius: 999px;
|
|
4062
|
-
border: 2px solid rgba(37, 48, 65, 0.18);
|
|
4063
|
-
background: var(--inpaint-color, #ff2d55);
|
|
4064
|
-
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.8);
|
|
4065
|
-
cursor: pointer;
|
|
4066
|
-
}
|
|
4067
|
-
|
|
4068
|
-
.editor-inpaint-color-remove {
|
|
4069
|
-
width: 22px;
|
|
4070
|
-
height: 22px;
|
|
4071
|
-
min-width: 22px;
|
|
4072
|
-
max-width: 22px;
|
|
4073
|
-
aspect-ratio: 1 / 1;
|
|
4074
|
-
flex: 0 0 22px;
|
|
4075
|
-
display: inline-grid;
|
|
4076
|
-
place-items: center;
|
|
4077
|
-
padding: 0;
|
|
4078
|
-
border: 1px solid rgba(37, 48, 65, 0.12);
|
|
4079
|
-
border-radius: 999px;
|
|
4080
|
-
background: rgba(255, 255, 255, 0.72);
|
|
4081
|
-
color: rgba(37, 48, 65, 0.56);
|
|
4082
|
-
cursor: pointer;
|
|
4083
|
-
}
|
|
4084
|
-
|
|
4085
|
-
.editor-inpaint-color-remove:hover,
|
|
4086
|
-
.editor-inpaint-color-remove:focus-visible {
|
|
4087
|
-
border-color: rgba(191, 164, 109, 0.36);
|
|
4088
|
-
color: #253041;
|
|
4089
|
-
}
|
|
4090
|
-
|
|
4091
|
-
.editor-inpaint-color-remove svg {
|
|
4092
|
-
width: 13px;
|
|
4093
|
-
height: 13px;
|
|
4094
|
-
}
|
|
4095
|
-
|
|
4096
|
-
.editor-inpaint-color-row[data-active="true"] .editor-inpaint-color-dot {
|
|
4097
|
-
border-color: #253041;
|
|
4098
|
-
box-shadow: 0 0 0 3px rgba(219, 193, 122, 0.45), inset 0 0 0 2px rgba(255, 255, 255, 0.8);
|
|
4099
|
-
}
|
|
4100
|
-
|
|
4101
|
-
.editor-inpaint-prompt {
|
|
4102
|
-
width: 100%;
|
|
4103
|
-
min-height: 72px;
|
|
4104
|
-
display: block;
|
|
4105
|
-
resize: vertical;
|
|
4106
|
-
padding: 10px 44px 18px 12px;
|
|
4107
|
-
border-radius: 14px;
|
|
4108
|
-
border: 1px solid rgba(191, 164, 109, 0.34);
|
|
4109
|
-
background: rgba(255, 255, 255, 0.96);
|
|
4110
|
-
color: #243041;
|
|
4111
|
-
font: inherit;
|
|
4112
|
-
line-height: 1.45;
|
|
4113
|
-
}
|
|
4114
|
-
|
|
4115
|
-
.editor-inpaint-prompt:focus,
|
|
4116
|
-
.editor-inpaint-color-picker:focus,
|
|
4117
|
-
.editor-inpaint-brush input:focus {
|
|
4118
|
-
outline: 2px solid rgba(219, 193, 122, 0.7);
|
|
4119
|
-
outline-offset: 2px;
|
|
4120
|
-
}
|
|
4121
|
-
|
|
4122
|
-
.editor-inpaint-footer {
|
|
4123
|
-
justify-content: space-between;
|
|
4124
|
-
flex-wrap: wrap;
|
|
4125
|
-
}
|
|
4126
|
-
|
|
4127
|
-
.editor-inpaint-status[data-tone="error"] {
|
|
4128
|
-
color: #9b2d2d;
|
|
4129
|
-
}
|
|
4130
|
-
|
|
4131
|
-
.editor-inpaint-status[data-tone="success"] {
|
|
4132
|
-
color: #2f7d4f;
|
|
4133
|
-
}
|
|
4134
|
-
|
|
4135
|
-
.editor-inpaint-submit[disabled] {
|
|
4136
|
-
opacity: 0.58;
|
|
4137
|
-
cursor: wait;
|
|
4138
|
-
}
|
|
4139
|
-
|
|
4140
|
-
@media (max-width: 1024px) {
|
|
4141
|
-
.editor-hero,
|
|
4142
|
-
.editor-grid {
|
|
4143
|
-
grid-template-columns: 1fr;
|
|
4144
|
-
}
|
|
4145
|
-
|
|
4146
|
-
.editor-history-card-grid {
|
|
4147
|
-
grid-template-columns: 1fr;
|
|
4148
|
-
}
|
|
4149
|
-
|
|
4150
|
-
.editor-quickstart-grid {
|
|
4151
|
-
grid-template-columns: 1fr;
|
|
4152
|
-
}
|
|
4153
|
-
|
|
4154
|
-
.editor-inpaint-body {
|
|
4155
|
-
grid-template-columns: 1fr;
|
|
4156
|
-
}
|
|
4157
|
-
|
|
4158
|
-
.editor-inpaint-panel {
|
|
4159
|
-
max-height: 38vh;
|
|
4160
|
-
}
|
|
4161
|
-
|
|
4162
|
-
.editor-hero,
|
|
4163
|
-
.editor-section,
|
|
4164
|
-
.editor-panel {
|
|
4165
|
-
border-radius: 22px;
|
|
4166
|
-
}
|
|
4167
|
-
|
|
4168
|
-
.editor-panel > summary {
|
|
4169
|
-
align-items: flex-start;
|
|
4170
|
-
padding: 16px;
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4173
|
-
.editor-panel-toggle {
|
|
4174
|
-
font-size: 0;
|
|
4175
|
-
}
|
|
4176
|
-
|
|
4177
|
-
.editor-panel-toggle::before {
|
|
4178
|
-
width: 10px;
|
|
4179
|
-
height: 10px;
|
|
4180
|
-
}
|
|
4181
|
-
|
|
4182
|
-
.editor-panel-body {
|
|
4183
|
-
padding: 0 16px 16px;
|
|
4184
|
-
}
|
|
4185
|
-
|
|
4186
|
-
.editor-hero,
|
|
4187
|
-
.editor-section {
|
|
4188
|
-
padding: 16px;
|
|
4189
|
-
}
|
|
4190
|
-
|
|
4191
|
-
.editor-preview {
|
|
4192
|
-
min-height: 0;
|
|
4193
|
-
max-height: none;
|
|
4194
|
-
aspect-ratio: 16 / 11;
|
|
4195
|
-
border-radius: 18px;
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
|
-
.editor-title {
|
|
4199
|
-
font-size: clamp(2rem, 12vw, 2.85rem);
|
|
4200
|
-
line-height: 0.95;
|
|
4201
|
-
}
|
|
4202
|
-
|
|
4203
|
-
.editor-toolbar,
|
|
4204
|
-
.editor-history-toolbar-actions,
|
|
4205
|
-
.editor-history-group-actions {
|
|
4206
|
-
display: grid;
|
|
4207
|
-
grid-template-columns: 1fr;
|
|
4208
|
-
width: 100%;
|
|
4209
|
-
}
|
|
4210
|
-
|
|
4211
|
-
.editor-copy-button,
|
|
4212
|
-
.editor-history-button,
|
|
4213
|
-
.editor-history-remove {
|
|
4214
|
-
width: 100%;
|
|
4215
|
-
}
|
|
4216
|
-
|
|
4217
|
-
.editor-pill,
|
|
4218
|
-
.editor-route-path,
|
|
4219
|
-
.editor-history-chip {
|
|
4220
|
-
max-width: 100%;
|
|
4221
|
-
overflow: hidden;
|
|
4222
|
-
text-overflow: ellipsis;
|
|
4223
|
-
white-space: nowrap;
|
|
4224
|
-
}
|
|
4225
|
-
}
|
|
4226
|
-
`,
|
|
4227
|
-
body: `
|
|
4228
|
-
<section class="editor-workbench">
|
|
4229
|
-
${renderTemplateEditorChrome({
|
|
4230
|
-
backHref,
|
|
4231
|
-
templateTitle: input.template.title,
|
|
4232
|
-
templateId: input.template.id,
|
|
4233
|
-
templateSlug: input.template.slugId,
|
|
4234
|
-
account: {
|
|
4235
|
-
displayName: input.account.name,
|
|
4236
|
-
email: input.account.email
|
|
4237
|
-
}
|
|
4238
|
-
})}
|
|
4239
|
-
<main class="editor-main">
|
|
4240
|
-
<section class="editor-shell page-content-start">
|
|
4241
|
-
<details class="editor-panel" open>
|
|
4242
|
-
<summary>
|
|
4243
|
-
<div class="editor-panel-summary">
|
|
4244
|
-
<h2>Character card</h2>
|
|
4245
|
-
<p>Preview the composition, metadata, and layer notes without losing space for the sections that follow.</p>
|
|
4246
|
-
</div>
|
|
4247
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
4248
|
-
</summary>
|
|
4249
|
-
<div class="editor-panel-body">
|
|
4250
|
-
<section class="editor-hero">
|
|
4251
|
-
${preview}
|
|
4252
|
-
<div class="editor-copy">
|
|
4253
|
-
<div class="editor-kicker">HyperFrames Composition</div>
|
|
4254
|
-
<h1 class="editor-title">${escapeHtml(input.template.title)}</h1>
|
|
4255
|
-
<div class="editor-pills">
|
|
4256
|
-
<span class="editor-pill">${escapeHtml(input.template.id)}</span>
|
|
4257
|
-
<span class="editor-pill">${escapeHtml(input.template.slugId)}</span>
|
|
4258
|
-
<span class="editor-pill">v${escapeHtml(input.template.version)}</span>
|
|
4259
|
-
<span class="editor-pill" data-difficulty="${escapeAttribute(input.template.difficulty)}">${escapeHtml(formatDifficulty(input.template.difficulty))}</span>
|
|
4260
|
-
</div>
|
|
4261
|
-
${soundMetadata}
|
|
4262
|
-
<p>${escapeHtml(input.template.viralDna || input.template.visualDna || "No layer strategy is available for this composition yet.")}</p>
|
|
4263
|
-
${input.directorSkill?.trim()
|
|
4264
|
-
? `<div class="editor-toolbar">
|
|
4265
|
-
<button
|
|
4266
|
-
type="button"
|
|
4267
|
-
class="editor-copy-button"
|
|
4268
|
-
data-editor-copy-content="${escapeAttribute(input.directorSkill)}"
|
|
4269
|
-
data-editor-copy-label="Director SKILL.md"
|
|
4270
|
-
>Copy Director SKILL.md</button>
|
|
4271
|
-
</div>`
|
|
4272
|
-
: ""}
|
|
4273
|
-
</div>
|
|
4274
|
-
</section>
|
|
4275
|
-
</div>
|
|
4276
|
-
</details>
|
|
4277
|
-
<section class="editor-grid">
|
|
4278
|
-
<details class="editor-panel" open>
|
|
4279
|
-
<summary>
|
|
4280
|
-
<div class="editor-panel-summary">
|
|
4281
|
-
<h2>Layer Brief</h2>
|
|
4282
|
-
<p>Use these readonly references to shape the timeline, overlays, and render prompt before publishing through HyperFrames.</p>
|
|
4283
|
-
</div>
|
|
4284
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
4285
|
-
</summary>
|
|
4286
|
-
<div class="editor-panel-body">
|
|
4287
|
-
<section class="editor-section">
|
|
4288
|
-
<div class="editor-quickstart-grid">
|
|
4289
|
-
${renderQuickstartTextarea("Composition skeleton", input.template.skeletonPrompt, 10)}
|
|
4290
|
-
${renderQuickstartTextarea("Layer proposal generator", input.template.proposalGenerator, 10)}
|
|
4291
|
-
</div>
|
|
4292
|
-
<div class="editor-quickstart-samples">
|
|
4293
|
-
<h3>Sample prompts</h3>
|
|
4294
|
-
<div class="editor-sample-prompt-row" aria-label="Sample prompts">
|
|
4295
|
-
${input.template.samplePrompts.length
|
|
4296
|
-
? input.template.samplePrompts.map((sample, index) => `
|
|
4297
|
-
<div class="editor-sample-prompt">
|
|
4298
|
-
${renderQuickstartTextarea(`Sample ${index + 1}`, sample, 7)}
|
|
4299
|
-
</div>
|
|
4300
|
-
`).join("")
|
|
4301
|
-
: `<div class="editor-sample-prompt">${renderQuickstartTextarea("Sample 1", "No sample prompts are defined for this composition yet.", 7)}</div>`}
|
|
4302
|
-
</div>
|
|
4303
|
-
</div>
|
|
4304
|
-
</section>
|
|
4305
|
-
</div>
|
|
4306
|
-
</details>
|
|
4307
|
-
<details class="editor-panel">
|
|
4308
|
-
<summary>
|
|
4309
|
-
<div class="editor-panel-summary">
|
|
4310
|
-
<h2>Examples</h2>
|
|
4311
|
-
<p>Review sample cuts for this composition before starting a new timeline run.</p>
|
|
4312
|
-
</div>
|
|
4313
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
4314
|
-
</summary>
|
|
4315
|
-
<div class="editor-panel-body">
|
|
4316
|
-
<section class="editor-section">
|
|
4317
|
-
${renderExamples(input.template.examples)}
|
|
4318
|
-
</section>
|
|
4319
|
-
</div>
|
|
4320
|
-
</details>
|
|
4321
|
-
<details class="editor-panel">
|
|
4322
|
-
<summary>
|
|
4323
|
-
<div class="editor-panel-summary">
|
|
4324
|
-
<h2>Render Surface</h2>
|
|
4325
|
-
<p>This workspace combines composition routes with platform primitives so asset generation, image edits, layer assembly, and HyperFrames renders stay in one tracer-aware flow.</p>
|
|
4326
|
-
</div>
|
|
4327
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
4328
|
-
</summary>
|
|
4329
|
-
<div class="editor-panel-body">
|
|
4330
|
-
<section class="editor-section">
|
|
4331
|
-
<div class="editor-section-header">
|
|
4332
|
-
<p>Use tracer IDs to group a run across requests.</p>
|
|
4333
|
-
</div>
|
|
4334
|
-
<div class="editor-route-list">
|
|
4335
|
-
${input.docsRoutes.map((route) => `
|
|
4336
|
-
<article class="editor-route-card">
|
|
4337
|
-
<div class="editor-route-head">
|
|
4338
|
-
<span class="editor-route-method" data-method="${escapeAttribute(route.method)}">${escapeHtml(route.method)}</span>
|
|
4339
|
-
<div class="editor-route-path">${escapeHtml(route.path)}</div>
|
|
4340
|
-
</div>
|
|
4341
|
-
<div class="editor-note">${escapeHtml(route.summary)}</div>
|
|
4342
|
-
</article>
|
|
4343
|
-
`).join("")}
|
|
4344
|
-
</div>
|
|
4345
|
-
</section>
|
|
4346
|
-
</div>
|
|
4347
|
-
</details>
|
|
4348
|
-
<details class="editor-panel" open>
|
|
4349
|
-
<summary>
|
|
4350
|
-
<div class="editor-panel-summary">
|
|
4351
|
-
<h2>Render Logs</h2>
|
|
4352
|
-
<p>Recent render and primitive calls are grouped by tracer so parallel timeline workstreams stay readable. Newest requests appear first inside each tracer group.</p>
|
|
4353
|
-
</div>
|
|
4354
|
-
<span class="editor-panel-toggle" aria-hidden="true">Toggle</span>
|
|
4355
|
-
</summary>
|
|
4356
|
-
<div class="editor-panel-body">
|
|
4357
|
-
<section class="editor-section">
|
|
4358
|
-
<div class="editor-history-toolbar">
|
|
4359
|
-
<p class="editor-note">Tracer visibility lives here. Removing one only clears it from this page view; the underlying runs remain available in Render Runs.</p>
|
|
4360
|
-
<div class="editor-history-toolbar-actions">
|
|
4361
|
-
<button type="button" class="editor-history-button is-primary" data-editor-history-add>Add tracer</button>
|
|
4362
|
-
<div class="editor-history-chip-row" data-editor-history-tracers>
|
|
4363
|
-
${input.initialTracers.length
|
|
4364
|
-
? input.initialTracers.map((tracer) => `<span class="editor-history-chip">${escapeHtml(tracer)}</span>`).join("")
|
|
4365
|
-
: `<span class="editor-history-chip is-empty">No tracer selected yet</span>`}
|
|
4366
|
-
</div>
|
|
4367
|
-
</div>
|
|
4368
|
-
</div>
|
|
4369
|
-
<div class="editor-history-groups" data-editor-history-root>
|
|
4370
|
-
<div class="editor-history-empty">${hasHistoryAccess ? "Loading POST history…" : "Sign in to load tracer-scoped POST history."}</div>
|
|
4371
|
-
</div>
|
|
4372
|
-
</section>
|
|
4373
|
-
</div>
|
|
4374
|
-
</details>
|
|
4375
|
-
</section>
|
|
4376
|
-
</section>
|
|
4377
|
-
</main>
|
|
4378
|
-
</section>
|
|
4379
|
-
<div class="editor-modal-backdrop" data-editor-tracer-modal data-open="false" hidden>
|
|
4380
|
-
<div class="editor-modal" role="dialog" aria-modal="true" aria-labelledby="editor-tracer-modal-title">
|
|
4381
|
-
<div class="editor-modal-head">
|
|
4382
|
-
<h3 id="editor-tracer-modal-title">Add tracer</h3>
|
|
4383
|
-
<p>Type a tracer label to pin it into this page view. Existing labels stay untouched.</p>
|
|
4384
|
-
</div>
|
|
4385
|
-
<label class="editor-modal-label" for="editor-tracer-input">Tracer id</label>
|
|
4386
|
-
<input id="editor-tracer-input" class="editor-modal-input" type="text" autocomplete="off" spellcheck="false" placeholder="${escapeAttribute(`${input.template.slugId}-manual`)}" />
|
|
4387
|
-
<p class="editor-modal-error" data-editor-tracer-modal-error aria-live="polite"></p>
|
|
4388
|
-
<div class="editor-modal-actions">
|
|
4389
|
-
<button type="button" class="editor-history-button" data-editor-tracer-cancel>Cancel</button>
|
|
4390
|
-
<button type="button" class="editor-history-button is-primary" data-editor-tracer-submit>Add tracer</button>
|
|
4391
|
-
</div>
|
|
4392
|
-
</div>
|
|
4393
|
-
</div>
|
|
4394
|
-
<div class="editor-modal-backdrop" data-editor-media-modal data-open="false" hidden>
|
|
4395
|
-
<div class="editor-media-modal" role="dialog" aria-modal="true" aria-labelledby="editor-media-modal-title">
|
|
4396
|
-
<div class="editor-media-modal-head">
|
|
4397
|
-
<p id="editor-media-modal-title" class="editor-media-modal-title">Output preview</p>
|
|
4398
|
-
<div class="editor-media-modal-actions">
|
|
4399
|
-
<span class="editor-media-modal-carousel" data-editor-media-carousel data-visible="false">
|
|
4400
|
-
<button type="button" class="editor-history-button" data-editor-media-prev>Prev</button>
|
|
4401
|
-
<button type="button" class="editor-history-button" data-editor-media-next>Next</button>
|
|
4402
|
-
</span>
|
|
4403
|
-
<a class="editor-media-modal-source" data-editor-media-download href="#" download aria-label="Download media" title="Download">
|
|
4404
|
-
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M10 3v9"></path><path d="m6.5 8.5 3.5 3.5 3.5-3.5"></path><path d="M4 16h12"></path></svg>
|
|
4405
|
-
<span>Download</span>
|
|
4406
|
-
</a>
|
|
4407
|
-
<a class="editor-media-modal-source" data-editor-media-inpaint data-visible="false" href="/inpaint" target="_blank" rel="noopener noreferrer" aria-label="Open image editor" title="Image edit">
|
|
4408
|
-
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4.2 15.8c1.6.5 3.1-.1 3.7-1.7.6-1.4 1.8-2.1 3.5-2.1"></path><path d="m10.7 11.6 5.2-5.2a1.9 1.9 0 0 0-2.7-2.7L8 8.9"></path><path d="m7.9 9 3.1 3.1"></path><path d="M3.5 16.5c1.4.5 3.6.5 4.9-.8"></path></svg>
|
|
4409
|
-
<span>Image edit</span>
|
|
4410
|
-
</a>
|
|
4411
|
-
<button type="button" class="editor-media-modal-source" data-editor-media-copy aria-label="Copy media URL" title="Copy link">
|
|
4412
|
-
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M8.4 11.6a3.2 3.2 0 0 0 4.5 0l2.2-2.2a3.2 3.2 0 0 0-4.5-4.5L9.4 6.1"></path><path d="M11.6 8.4a3.2 3.2 0 0 0-4.5 0L4.9 10.6a3.2 3.2 0 0 0 4.5 4.5l1.2-1.2"></path></svg>
|
|
4413
|
-
<span data-editor-media-copy-label>Copy link</span>
|
|
4414
|
-
</button>
|
|
4415
|
-
<a class="editor-media-modal-source" data-editor-media-source href="#" target="_blank" rel="noreferrer">Open source</a>
|
|
4416
|
-
<button type="button" class="editor-history-button" data-editor-media-close>Close</button>
|
|
4417
|
-
</div>
|
|
4418
|
-
</div>
|
|
4419
|
-
<div class="editor-media-modal-body" data-editor-media-body></div>
|
|
4420
|
-
</div>
|
|
4421
|
-
</div>
|
|
4422
|
-
<div class="editor-inpaint-modal-backdrop" data-editor-inpaint-modal data-open="false" hidden>
|
|
4423
|
-
<div class="editor-inpaint-modal" role="dialog" aria-modal="true" aria-labelledby="editor-inpaint-title">
|
|
4424
|
-
<div class="editor-inpaint-modal-head">
|
|
4425
|
-
<div class="editor-inpaint-title">
|
|
4426
|
-
<h3 id="editor-inpaint-title">Image edit</h3>
|
|
4427
|
-
<p class="editor-inpaint-source" data-editor-inpaint-source>Output preview</p>
|
|
4428
|
-
</div>
|
|
4429
|
-
<div class="editor-inpaint-modal-actions">
|
|
4430
|
-
<button type="button" class="editor-history-button" data-editor-inpaint-undo>Undo</button>
|
|
4431
|
-
<button type="button" class="editor-history-button" data-editor-inpaint-clear>Clear</button>
|
|
4432
|
-
<button type="button" class="editor-history-button" data-editor-inpaint-close>Close</button>
|
|
4433
|
-
</div>
|
|
4434
|
-
</div>
|
|
4435
|
-
<div class="editor-inpaint-body">
|
|
4436
|
-
<div class="editor-inpaint-stage">
|
|
4437
|
-
<div class="editor-inpaint-canvas-wrap">
|
|
4438
|
-
<img data-editor-inpaint-image alt="" />
|
|
4439
|
-
<canvas data-editor-inpaint-canvas></canvas>
|
|
4440
|
-
</div>
|
|
4441
|
-
</div>
|
|
4442
|
-
<aside class="editor-inpaint-panel" aria-label="Image edit controls">
|
|
4443
|
-
<div class="editor-inpaint-toolbar">
|
|
4444
|
-
<label class="editor-inpaint-brush">
|
|
4445
|
-
<span class="editor-inpaint-label">Brush <span data-editor-inpaint-brush-label>70</span></span>
|
|
4446
|
-
<input data-editor-inpaint-brush type="range" min="6" max="180" step="1" value="70" />
|
|
4447
|
-
</label>
|
|
4448
|
-
<input class="editor-inpaint-color-picker" data-editor-inpaint-color-picker type="color" value="#ff2d55" aria-label="Mask color" />
|
|
4449
|
-
<button type="button" class="editor-history-button" data-editor-inpaint-add-color>Add color</button>
|
|
4450
|
-
<label class="editor-inpaint-provider" data-editor-inpaint-provider-wrap>
|
|
4451
|
-
<span class="editor-inpaint-label">Provider</span>
|
|
4452
|
-
<select data-editor-inpaint-provider aria-label="AI provider"></select>
|
|
4453
|
-
</label>
|
|
4454
|
-
</div>
|
|
4455
|
-
<div class="editor-inpaint-global">
|
|
4456
|
-
<span class="editor-inpaint-label">Whole image prompt</span>
|
|
4457
|
-
<div class="editor-inpaint-prompt-field">
|
|
4458
|
-
<textarea class="editor-inpaint-prompt" data-editor-inpaint-global-prompt placeholder="Describe the overall edit, style, constraints, or preservation notes."></textarea>
|
|
4459
|
-
<button type="button" class="editor-inpaint-attach-button" data-editor-inpaint-attach-scope="global" aria-label="Attach reference image" title="Attach reference image">
|
|
4460
|
-
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8.1 10.7 4.8-4.8a2.5 2.5 0 0 1 3.5 3.5l-6.1 6.1a4 4 0 0 1-5.7-5.7l6.3-6.3"></path><path d="m10 8.8-4.1 4.1a1.6 1.6 0 0 0 2.3 2.3l5.6-5.6"></path></svg>
|
|
4461
|
-
</button>
|
|
4462
|
-
</div>
|
|
4463
|
-
<div class="editor-inpaint-attachment-tools">
|
|
4464
|
-
<span class="editor-inpaint-attachment-chips" data-editor-inpaint-attachments="global"></span>
|
|
4465
|
-
</div>
|
|
4466
|
-
</div>
|
|
4467
|
-
<input data-editor-inpaint-attachment-input type="file" accept="image/jpeg,image/png,image/webp" multiple hidden />
|
|
4468
|
-
<div class="editor-inpaint-swatches" data-editor-inpaint-swatches aria-label="Mask colors"></div>
|
|
4469
|
-
<div class="editor-inpaint-prompts" data-editor-inpaint-prompts></div>
|
|
4470
|
-
</aside>
|
|
4471
|
-
</div>
|
|
4472
|
-
<div class="editor-inpaint-footer">
|
|
4473
|
-
<p class="editor-inpaint-status" data-editor-inpaint-status aria-live="polite"></p>
|
|
4474
|
-
<button type="button" class="editor-history-button is-primary editor-inpaint-submit" data-editor-inpaint-submit>Create image edit</button>
|
|
4475
|
-
</div>
|
|
4476
|
-
</div>
|
|
4477
|
-
</div>
|
|
4478
|
-
<div class="vf-editor-chat-http-modal-backdrop" data-editor-http-modal hidden>
|
|
4479
|
-
<div class="vf-editor-chat-http-modal" role="dialog" aria-modal="true" aria-labelledby="editor-http-modal-title">
|
|
4480
|
-
<div class="vf-editor-chat-http-modal-header">
|
|
4481
|
-
<div class="vf-editor-chat-http-modal-header-main" data-editor-http-modal-head>
|
|
4482
|
-
<span id="editor-http-modal-title" class="vf-editor-chat-http-modal-url">HTTP details</span>
|
|
4483
|
-
</div>
|
|
4484
|
-
<button type="button" class="vf-editor-chat-http-modal-close" data-editor-http-close aria-label="Close HTTP details">×</button>
|
|
4485
|
-
</div>
|
|
4486
|
-
<div class="vf-editor-chat-http-card-body is-modal" data-editor-http-modal-body></div>
|
|
4487
|
-
</div>
|
|
4488
|
-
</div>
|
|
4489
|
-
<script>
|
|
4490
|
-
${TEMPLATE_EDITOR_SHELL_SCRIPT}
|
|
4491
|
-
(() => {
|
|
4492
|
-
const historyRoot = document.querySelector("[data-editor-history-root]");
|
|
4493
|
-
const tracerChipRow = document.querySelector("[data-editor-history-tracers]");
|
|
4494
|
-
const addTracerButton = document.querySelector("[data-editor-history-add]");
|
|
4495
|
-
const modalBackdrop = document.querySelector("[data-editor-tracer-modal]");
|
|
4496
|
-
const modalInput = document.getElementById("editor-tracer-input");
|
|
4497
|
-
const modalError = document.querySelector("[data-editor-tracer-modal-error]");
|
|
4498
|
-
const modalSubmit = document.querySelector("[data-editor-tracer-submit]");
|
|
4499
|
-
const modalCancel = document.querySelector("[data-editor-tracer-cancel]");
|
|
4500
|
-
const mediaModalBackdrop = document.querySelector("[data-editor-media-modal]");
|
|
4501
|
-
const mediaModalBody = document.querySelector("[data-editor-media-body]");
|
|
4502
|
-
const mediaModalTitle = document.getElementById("editor-media-modal-title");
|
|
4503
|
-
const mediaModalClose = document.querySelector("[data-editor-media-close]");
|
|
4504
|
-
const mediaModalSource = document.querySelector("[data-editor-media-source]");
|
|
4505
|
-
const mediaModalDownload = document.querySelector("[data-editor-media-download]");
|
|
4506
|
-
const mediaModalInpaint = document.querySelector("[data-editor-media-inpaint]");
|
|
4507
|
-
const mediaModalCopy = document.querySelector("[data-editor-media-copy]");
|
|
4508
|
-
const mediaModalCopyLabel = document.querySelector("[data-editor-media-copy-label]");
|
|
4509
|
-
const mediaModalCarousel = document.querySelector("[data-editor-media-carousel]");
|
|
4510
|
-
const mediaModalPrev = document.querySelector("[data-editor-media-prev]");
|
|
4511
|
-
const mediaModalNext = document.querySelector("[data-editor-media-next]");
|
|
4512
|
-
const inpaintModalBackdrop = document.querySelector("[data-editor-inpaint-modal]");
|
|
4513
|
-
const inpaintSource = document.querySelector("[data-editor-inpaint-source]");
|
|
4514
|
-
const inpaintImage = document.querySelector("[data-editor-inpaint-image]");
|
|
4515
|
-
const inpaintCanvas = document.querySelector("[data-editor-inpaint-canvas]");
|
|
4516
|
-
const inpaintClose = document.querySelector("[data-editor-inpaint-close]");
|
|
4517
|
-
const inpaintUndo = document.querySelector("[data-editor-inpaint-undo]");
|
|
4518
|
-
const inpaintClear = document.querySelector("[data-editor-inpaint-clear]");
|
|
4519
|
-
const inpaintBrush = document.querySelector("[data-editor-inpaint-brush]");
|
|
4520
|
-
const inpaintBrushLabel = document.querySelector("[data-editor-inpaint-brush-label]");
|
|
4521
|
-
const inpaintColorPicker = document.querySelector("[data-editor-inpaint-color-picker]");
|
|
4522
|
-
const inpaintAddColor = document.querySelector("[data-editor-inpaint-add-color]");
|
|
4523
|
-
const inpaintProviderWrap = document.querySelector("[data-editor-inpaint-provider-wrap]");
|
|
4524
|
-
const inpaintProviderSelect = document.querySelector("[data-editor-inpaint-provider]");
|
|
4525
|
-
const inpaintGlobalPrompt = document.querySelector("[data-editor-inpaint-global-prompt]");
|
|
4526
|
-
const inpaintAttachmentInput = document.querySelector("[data-editor-inpaint-attachment-input]");
|
|
4527
|
-
const inpaintGlobalAttachments = document.querySelector('[data-editor-inpaint-attachments="global"]');
|
|
4528
|
-
const inpaintSwatches = document.querySelector("[data-editor-inpaint-swatches]");
|
|
4529
|
-
const inpaintPrompts = document.querySelector("[data-editor-inpaint-prompts]");
|
|
4530
|
-
const inpaintStatus = document.querySelector("[data-editor-inpaint-status]");
|
|
4531
|
-
const inpaintSubmit = document.querySelector("[data-editor-inpaint-submit]");
|
|
4532
|
-
const httpModalBackdrop = document.querySelector("[data-editor-http-modal]");
|
|
4533
|
-
const httpModalHead = document.querySelector("[data-editor-http-modal-head]");
|
|
4534
|
-
const httpModalBody = document.querySelector("[data-editor-http-modal-body]");
|
|
4535
|
-
const httpModalClose = document.querySelector("[data-editor-http-close]");
|
|
4536
|
-
if (!(historyRoot instanceof HTMLElement) || !(tracerChipRow instanceof HTMLElement)) {
|
|
4537
|
-
return;
|
|
4538
|
-
}
|
|
4539
|
-
|
|
4540
|
-
const hasTracerModal = addTracerButton instanceof HTMLButtonElement
|
|
4541
|
-
&& modalBackdrop instanceof HTMLElement
|
|
4542
|
-
&& modalInput instanceof HTMLInputElement
|
|
4543
|
-
&& modalError instanceof HTMLElement
|
|
4544
|
-
&& modalSubmit instanceof HTMLButtonElement
|
|
4545
|
-
&& modalCancel instanceof HTMLButtonElement;
|
|
4546
|
-
const hasMediaModal = mediaModalBackdrop instanceof HTMLElement
|
|
4547
|
-
&& mediaModalBody instanceof HTMLElement
|
|
4548
|
-
&& mediaModalTitle instanceof HTMLElement
|
|
4549
|
-
&& mediaModalClose instanceof HTMLButtonElement
|
|
4550
|
-
&& mediaModalSource instanceof HTMLAnchorElement
|
|
4551
|
-
&& mediaModalDownload instanceof HTMLAnchorElement
|
|
4552
|
-
&& mediaModalInpaint instanceof HTMLAnchorElement
|
|
4553
|
-
&& mediaModalCopy instanceof HTMLButtonElement
|
|
4554
|
-
&& mediaModalCopyLabel instanceof HTMLElement
|
|
4555
|
-
&& mediaModalCarousel instanceof HTMLElement
|
|
4556
|
-
&& mediaModalPrev instanceof HTMLButtonElement
|
|
4557
|
-
&& mediaModalNext instanceof HTMLButtonElement;
|
|
4558
|
-
const hasInpaintModal = inpaintModalBackdrop instanceof HTMLElement
|
|
4559
|
-
&& inpaintSource instanceof HTMLElement
|
|
4560
|
-
&& inpaintImage instanceof HTMLImageElement
|
|
4561
|
-
&& inpaintCanvas instanceof HTMLCanvasElement
|
|
4562
|
-
&& inpaintClose instanceof HTMLButtonElement
|
|
4563
|
-
&& inpaintUndo instanceof HTMLButtonElement
|
|
4564
|
-
&& inpaintClear instanceof HTMLButtonElement
|
|
4565
|
-
&& inpaintBrush instanceof HTMLInputElement
|
|
4566
|
-
&& inpaintBrushLabel instanceof HTMLElement
|
|
4567
|
-
&& inpaintColorPicker instanceof HTMLInputElement
|
|
4568
|
-
&& inpaintAddColor instanceof HTMLButtonElement
|
|
4569
|
-
&& inpaintProviderWrap instanceof HTMLElement
|
|
4570
|
-
&& inpaintProviderSelect instanceof HTMLSelectElement
|
|
4571
|
-
&& inpaintGlobalPrompt instanceof HTMLTextAreaElement
|
|
4572
|
-
&& inpaintAttachmentInput instanceof HTMLInputElement
|
|
4573
|
-
&& inpaintGlobalAttachments instanceof HTMLElement
|
|
4574
|
-
&& inpaintSwatches instanceof HTMLElement
|
|
4575
|
-
&& inpaintPrompts instanceof HTMLElement
|
|
4576
|
-
&& inpaintStatus instanceof HTMLElement
|
|
4577
|
-
&& inpaintSubmit instanceof HTMLButtonElement;
|
|
4578
|
-
const hasHttpModal = httpModalBackdrop instanceof HTMLElement
|
|
4579
|
-
&& httpModalHead instanceof HTMLElement
|
|
4580
|
-
&& httpModalBody instanceof HTMLElement
|
|
4581
|
-
&& httpModalClose instanceof HTMLButtonElement;
|
|
4582
|
-
|
|
4583
|
-
const boot = {
|
|
4584
|
-
historyEndpoint: ${JSON.stringify(input.historyEndpoint)},
|
|
4585
|
-
tracerEndpoint: ${JSON.stringify(input.tracerEndpoint)},
|
|
4586
|
-
fallbackHistoryEndpoint: ${JSON.stringify(withAccountQuery(`/job-runs/history?limit=250&template_id=${encodeURIComponent(input.template.id)}`, input.account.userId))},
|
|
4587
|
-
isLoggedIn: ${input.account.isLoggedIn ? "true" : "false"}
|
|
4588
|
-
};
|
|
4589
|
-
let lastHistoryEntries = [];
|
|
4590
|
-
let lastSelectedTracers = [];
|
|
4591
|
-
let activeMediaItems = [];
|
|
4592
|
-
let activeMediaIndex = 0;
|
|
4593
|
-
let activeInpaintImage = null;
|
|
4594
|
-
let inpaintMaskColors = [];
|
|
4595
|
-
let activeInpaintColor = "#ff2d55";
|
|
4596
|
-
let inpaintStrokes = [];
|
|
4597
|
-
let currentInpaintStroke = null;
|
|
4598
|
-
let isInpaintPainting = false;
|
|
4599
|
-
let inpaintAttachments = [];
|
|
4600
|
-
let pendingInpaintAttachmentScope = null;
|
|
4601
|
-
const maxInpaintAttachments = 8;
|
|
4602
|
-
const allowedInpaintAttachmentTypes = new Set(["image/jpeg", "image/png", "image/webp"]);
|
|
4603
|
-
const templateExamples = ${JSON.stringify(input.template.examples)};
|
|
4604
|
-
const expandedPreviewGroups = new Set();
|
|
4605
|
-
|
|
4606
|
-
function escapeHtml(value) {
|
|
4607
|
-
return String(value)
|
|
4608
|
-
.replace(/&/g, "&")
|
|
4609
|
-
.replace(/</g, "<")
|
|
4610
|
-
.replace(/>/g, ">")
|
|
4611
|
-
.replace(/"/g, """);
|
|
4612
|
-
}
|
|
4613
|
-
|
|
4614
|
-
function formatJson(value) {
|
|
4615
|
-
if (value == null) {
|
|
4616
|
-
return "null";
|
|
4617
|
-
}
|
|
4618
|
-
if (typeof value === "string") {
|
|
4619
|
-
return value;
|
|
4620
|
-
}
|
|
4621
|
-
try {
|
|
4622
|
-
return JSON.stringify(value, null, 2);
|
|
4623
|
-
} catch {
|
|
4624
|
-
return String(value);
|
|
4625
|
-
}
|
|
4626
|
-
}
|
|
4627
|
-
|
|
4628
|
-
function setTextBlock(parent, label, value) {
|
|
4629
|
-
const block = document.createElement("div");
|
|
4630
|
-
block.className = "vf-editor-chat-http-block";
|
|
4631
|
-
const heading = document.createElement("div");
|
|
4632
|
-
heading.className = "vf-editor-chat-http-label";
|
|
4633
|
-
heading.textContent = label;
|
|
4634
|
-
const pre = document.createElement("pre");
|
|
4635
|
-
pre.className = "vf-editor-chat-http-pre";
|
|
4636
|
-
pre.textContent = typeof value === "string" ? value : formatJson(value);
|
|
4637
|
-
block.append(heading, pre);
|
|
4638
|
-
parent.append(block);
|
|
4639
|
-
}
|
|
4640
|
-
|
|
4641
|
-
function makeHttpPayload(entry) {
|
|
4642
|
-
return {
|
|
4643
|
-
method: entry.method,
|
|
4644
|
-
url: entry.path,
|
|
4645
|
-
status: Number(entry.status) || 0,
|
|
4646
|
-
statusText: "",
|
|
4647
|
-
request: {
|
|
4648
|
-
query: entry.request?.query || {},
|
|
4649
|
-
body: entry.request?.body ?? null,
|
|
4650
|
-
omitted: entry.request?.bodyOmittedReason || null
|
|
4651
|
-
},
|
|
4652
|
-
response: {
|
|
4653
|
-
status: entry.status,
|
|
4654
|
-
body: entry.response?.body ?? null,
|
|
4655
|
-
omitted: entry.response?.bodyOmittedReason || null
|
|
4656
|
-
}
|
|
4657
|
-
};
|
|
4658
|
-
}
|
|
4659
|
-
|
|
4660
|
-
function encodeHttpPayload(entry) {
|
|
4661
|
-
try {
|
|
4662
|
-
return encodeURIComponent(JSON.stringify(makeHttpPayload(entry)));
|
|
4663
|
-
} catch {
|
|
4664
|
-
return encodeURIComponent(JSON.stringify({
|
|
4665
|
-
method: entry.method,
|
|
4666
|
-
url: entry.path,
|
|
4667
|
-
status: Number(entry.status) || 0,
|
|
4668
|
-
statusText: "",
|
|
4669
|
-
request: { query: {}, body: null, omitted: "Request sample could not be encoded for display." },
|
|
4670
|
-
response: { status: entry.status, body: null, omitted: "Response sample could not be encoded for display." }
|
|
4671
|
-
}));
|
|
4672
|
-
}
|
|
4673
|
-
}
|
|
4674
|
-
|
|
4675
|
-
function renderHttpAction(entry) {
|
|
4676
|
-
const payload = encodeHttpPayload(entry);
|
|
4677
|
-
const status = Number(entry.status) || 0;
|
|
4678
|
-
const isError = status >= 400;
|
|
4679
|
-
const isSuccess = status >= 200 && status < 300;
|
|
4680
|
-
return '<div class="vf-editor-chat-http-card">' +
|
|
4681
|
-
'<button type="button" class="vf-editor-chat-http-card-header" data-editor-http-detail="' + escapeHtml(payload) + '" aria-label="Open HTTP details for ' + escapeHtml(entry.method) + ' ' + escapeHtml(entry.path) + '">' +
|
|
4682
|
-
'<span class="vf-editor-chat-http-pill">' + escapeHtml(entry.method) + '</span>' +
|
|
4683
|
-
'<span class="vf-editor-chat-http-url" title="' + escapeHtml(entry.path) + '">' + escapeHtml(entry.path) + '</span>' +
|
|
4684
|
-
'<span class="vf-editor-chat-http-status" data-error="' + (isError ? "true" : "false") + '" data-success="' + (isSuccess ? "true" : "false") + '">' + escapeHtml(status) + '</span>' +
|
|
4685
|
-
'<span class="vf-editor-chat-http-expand" aria-hidden="true">+</span>' +
|
|
4686
|
-
'</button>' +
|
|
4687
|
-
'</div>';
|
|
4688
|
-
}
|
|
4689
|
-
|
|
4690
|
-
function openHttpModal(payload) {
|
|
4691
|
-
if (!hasHttpModal) {
|
|
4692
|
-
return;
|
|
4693
|
-
}
|
|
4694
|
-
httpModalHead.innerHTML = "";
|
|
4695
|
-
const isError = payload.status >= 400;
|
|
4696
|
-
const isSuccess = payload.status >= 200 && payload.status < 300;
|
|
4697
|
-
const method = document.createElement("span");
|
|
4698
|
-
method.className = "vf-editor-chat-http-pill";
|
|
4699
|
-
method.textContent = payload.method || "HTTP";
|
|
4700
|
-
const url = document.createElement("span");
|
|
4701
|
-
url.className = "vf-editor-chat-http-modal-url";
|
|
4702
|
-
url.textContent = payload.url || "";
|
|
4703
|
-
const status = document.createElement("span");
|
|
4704
|
-
status.className = "vf-editor-chat-http-status";
|
|
4705
|
-
status.dataset.error = isError ? "true" : "false";
|
|
4706
|
-
status.dataset.success = isSuccess ? "true" : "false";
|
|
4707
|
-
status.textContent = String(payload.status || "");
|
|
4708
|
-
httpModalHead.append(method, url, status);
|
|
4709
|
-
httpModalBody.innerHTML = "";
|
|
4710
|
-
setTextBlock(httpModalBody, "URL", payload.url || "");
|
|
4711
|
-
if (payload.request?.omitted) {
|
|
4712
|
-
setTextBlock(httpModalBody, "Request note", payload.request.omitted);
|
|
4713
|
-
}
|
|
4714
|
-
setTextBlock(httpModalBody, "Request", {
|
|
4715
|
-
query: payload.request?.query || {},
|
|
4716
|
-
body: payload.request?.body ?? null
|
|
4717
|
-
});
|
|
4718
|
-
if (payload.response?.omitted) {
|
|
4719
|
-
setTextBlock(httpModalBody, "Response note", payload.response.omitted);
|
|
4720
|
-
}
|
|
4721
|
-
setTextBlock(httpModalBody, "Response", {
|
|
4722
|
-
status: payload.response?.status ?? payload.status,
|
|
4723
|
-
body: payload.response?.body ?? null
|
|
4724
|
-
});
|
|
4725
|
-
httpModalBackdrop.hidden = false;
|
|
4726
|
-
}
|
|
4727
|
-
|
|
4728
|
-
function closeHttpModal() {
|
|
4729
|
-
if (!hasHttpModal) {
|
|
4730
|
-
return;
|
|
4731
|
-
}
|
|
4732
|
-
httpModalBackdrop.hidden = true;
|
|
4733
|
-
httpModalBody.innerHTML = "";
|
|
4734
|
-
}
|
|
4735
|
-
|
|
4736
|
-
function parseTracersFromUrl() {
|
|
4737
|
-
const params = new URLSearchParams(window.location.search);
|
|
4738
|
-
const values = [...params.getAll("tracers"), ...params.getAll("tracer")];
|
|
4739
|
-
const tracers = [];
|
|
4740
|
-
const seen = new Set();
|
|
4741
|
-
for (const rawValue of values) {
|
|
4742
|
-
const trimmed = String(rawValue || "").trim();
|
|
4743
|
-
if (!trimmed) {
|
|
4744
|
-
continue;
|
|
4745
|
-
}
|
|
4746
|
-
const inner = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1) : trimmed;
|
|
4747
|
-
for (const part of inner.split(",")) {
|
|
4748
|
-
const tracer = part.trim().replace(/^["']|["']$/g, "");
|
|
4749
|
-
if (!tracer || seen.has(tracer)) {
|
|
4750
|
-
continue;
|
|
4751
|
-
}
|
|
4752
|
-
seen.add(tracer);
|
|
4753
|
-
tracers.push(tracer);
|
|
4754
|
-
}
|
|
4755
|
-
}
|
|
4756
|
-
return tracers;
|
|
4757
|
-
}
|
|
4758
|
-
|
|
4759
|
-
function makeTracerId() {
|
|
4760
|
-
const suffix = (globalThis.crypto && typeof globalThis.crypto.randomUUID === "function"
|
|
4761
|
-
? globalThis.crypto.randomUUID().replace(/-/g, "").slice(-8)
|
|
4762
|
-
: Math.random().toString(16).slice(2, 10).padEnd(8, "0").slice(0, 8));
|
|
4763
|
-
return ${JSON.stringify(input.template.slugId)}
|
|
4764
|
-
.replace(/_/g, "-")
|
|
4765
|
-
.replace(/[^a-zA-Z0-9-]/g, "-")
|
|
4766
|
-
.toLowerCase()
|
|
4767
|
-
.replace(/-+/g, "-")
|
|
4768
|
-
.replace(/^-|-$/g, "") + "-" + suffix;
|
|
4769
|
-
}
|
|
4770
|
-
|
|
4771
|
-
function syncTracerUrl(tracers) {
|
|
4772
|
-
const url = new URL(window.location.href);
|
|
4773
|
-
if (tracers.length) {
|
|
4774
|
-
url.searchParams.set("tracers", tracers.join(","));
|
|
4775
|
-
} else {
|
|
4776
|
-
url.searchParams.delete("tracers");
|
|
4777
|
-
}
|
|
4778
|
-
window.history.replaceState({}, "", url.toString());
|
|
4779
|
-
window.dispatchEvent(new CustomEvent("vidfarm:editor-tracers-changed", {
|
|
4780
|
-
detail: { tracers }
|
|
4781
|
-
}));
|
|
4782
|
-
}
|
|
4783
|
-
|
|
4784
|
-
const tracerStore = window.__vidfarmEditorTracers || (() => {
|
|
4785
|
-
let listeners = [];
|
|
4786
|
-
const notify = () => {
|
|
4787
|
-
const nextTracers = parseTracersFromUrl();
|
|
4788
|
-
for (const listener of listeners) {
|
|
4789
|
-
listener(nextTracers);
|
|
4790
|
-
}
|
|
4791
|
-
};
|
|
4792
|
-
return {
|
|
4793
|
-
get() {
|
|
4794
|
-
return parseTracersFromUrl();
|
|
4795
|
-
},
|
|
4796
|
-
set(tracers) {
|
|
4797
|
-
const normalized = [...new Set((Array.isArray(tracers) ? tracers : []).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
4798
|
-
syncTracerUrl(normalized);
|
|
4799
|
-
notify();
|
|
4800
|
-
},
|
|
4801
|
-
add(tracer) {
|
|
4802
|
-
const nextTracer = String(tracer || "").trim() || makeTracerId();
|
|
4803
|
-
const current = this.get().filter((entry) => entry !== nextTracer);
|
|
4804
|
-
this.set([nextTracer, ...current]);
|
|
4805
|
-
return nextTracer;
|
|
4806
|
-
},
|
|
4807
|
-
remove(tracer) {
|
|
4808
|
-
const current = this.get().filter((entry) => entry !== tracer);
|
|
4809
|
-
this.set(current);
|
|
4810
|
-
},
|
|
4811
|
-
activate(tracer) {
|
|
4812
|
-
const current = this.get();
|
|
4813
|
-
if (!current.includes(tracer)) {
|
|
4814
|
-
this.add(tracer);
|
|
4815
|
-
return;
|
|
4816
|
-
}
|
|
4817
|
-
this.set([tracer, ...current.filter((entry) => entry !== tracer)]);
|
|
4818
|
-
},
|
|
4819
|
-
subscribe(listener) {
|
|
4820
|
-
listeners.push(listener);
|
|
4821
|
-
return () => {
|
|
4822
|
-
listeners = listeners.filter((entry) => entry !== listener);
|
|
4823
|
-
};
|
|
4824
|
-
}
|
|
4825
|
-
};
|
|
4826
|
-
})();
|
|
4827
|
-
window.__vidfarmEditorTracers = tracerStore;
|
|
4828
|
-
|
|
4829
|
-
function openTracerModal() {
|
|
4830
|
-
if (!hasTracerModal) {
|
|
4831
|
-
return;
|
|
4832
|
-
}
|
|
4833
|
-
modalBackdrop.hidden = false;
|
|
4834
|
-
modalBackdrop.setAttribute("data-open", "true");
|
|
4835
|
-
modalError.textContent = "";
|
|
4836
|
-
modalInput.value = "";
|
|
4837
|
-
window.requestAnimationFrame(() => modalInput.focus());
|
|
4838
|
-
}
|
|
4839
|
-
|
|
4840
|
-
function closeTracerModal() {
|
|
4841
|
-
if (!hasTracerModal) {
|
|
4842
|
-
return;
|
|
4843
|
-
}
|
|
4844
|
-
modalBackdrop.setAttribute("data-open", "false");
|
|
4845
|
-
modalBackdrop.hidden = true;
|
|
4846
|
-
modalError.textContent = "";
|
|
4847
|
-
}
|
|
4848
|
-
|
|
4849
|
-
function submitTracerModal() {
|
|
4850
|
-
if (!hasTracerModal) {
|
|
4851
|
-
return;
|
|
4852
|
-
}
|
|
4853
|
-
const tracer = modalInput.value.trim();
|
|
4854
|
-
if (!tracer) {
|
|
4855
|
-
modalError.textContent = "Enter a tracer id.";
|
|
4856
|
-
modalInput.focus();
|
|
4857
|
-
return;
|
|
4858
|
-
}
|
|
4859
|
-
tracerStore.add(tracer);
|
|
4860
|
-
closeTracerModal();
|
|
4861
|
-
}
|
|
4862
|
-
|
|
4863
|
-
function renderTracerChips(tracers) {
|
|
4864
|
-
tracerChipRow.innerHTML = tracers.length
|
|
4865
|
-
? tracers.map((tracer) => '<span class="editor-history-chip">' + escapeHtml(tracer) + '</span>').join("")
|
|
4866
|
-
: '<span class="editor-history-chip is-empty">No tracer selected yet</span>';
|
|
4867
|
-
}
|
|
4868
|
-
|
|
4869
|
-
function renderEmpty(message) {
|
|
4870
|
-
historyRoot.innerHTML = '<div class="editor-history-empty">' + escapeHtml(message) + '</div>';
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
function renderRefreshIcon() {
|
|
4874
|
-
return '<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
|
|
4875
|
-
'<path d="M21 12a9 9 0 0 1-15.1 6.6L3 16"></path>' +
|
|
4876
|
-
'<path d="M3 21v-5h5"></path>' +
|
|
4877
|
-
'<path d="M3 12a9 9 0 0 1 15.1-6.6L21 8"></path>' +
|
|
4878
|
-
'<path d="M21 3v5h-5"></path>' +
|
|
4879
|
-
'</svg>';
|
|
4880
|
-
}
|
|
4881
|
-
|
|
4882
|
-
function getPreviewKind(url) {
|
|
4883
|
-
if (/\\.(png|jpe?g|gif|webp|avif|svg)(\\?|#|$)/i.test(url)) {
|
|
4884
|
-
return "image";
|
|
4885
|
-
}
|
|
4886
|
-
if (/\\.(mp4|webm|ogg|mov|m4v)(\\?|#|$)/i.test(url)) {
|
|
4887
|
-
return "video";
|
|
4888
|
-
}
|
|
4889
|
-
return "file";
|
|
4890
|
-
}
|
|
4891
|
-
|
|
4892
|
-
function isPreviewableMediaUrl(url) {
|
|
4893
|
-
return getPreviewKind(url) !== "file";
|
|
4894
|
-
}
|
|
4895
|
-
|
|
4896
|
-
function mediaFileNameFromUrl(url, fallback) {
|
|
4897
|
-
const cleanFallback = String(fallback || "media").trim().replace(/[\\\\/:*?"<>|]+/g, "-") || "media";
|
|
4898
|
-
try {
|
|
4899
|
-
const path = new URL(url, window.location.href).pathname;
|
|
4900
|
-
const name = decodeURIComponent(path.split("/").filter(Boolean).pop() || "");
|
|
4901
|
-
return name.trim().replace(/[\\\\/:*?"<>|]+/g, "-") || cleanFallback;
|
|
4902
|
-
} catch {
|
|
4903
|
-
return cleanFallback;
|
|
4904
|
-
}
|
|
4905
|
-
}
|
|
4906
|
-
|
|
4907
|
-
function normalizeHexColor(value) {
|
|
4908
|
-
const raw = String(value || "").trim().toLowerCase();
|
|
4909
|
-
if (/^#[0-9a-f]{6}$/i.test(raw)) {
|
|
4910
|
-
return raw;
|
|
4911
|
-
}
|
|
4912
|
-
if (/^#[0-9a-f]{3}$/i.test(raw)) {
|
|
4913
|
-
return "#" + raw.slice(1).split("").map((part) => part + part).join("");
|
|
4914
|
-
}
|
|
4915
|
-
return "#ff2d55";
|
|
4916
|
-
}
|
|
4917
|
-
|
|
4918
|
-
function setInpaintStatus(message, tone) {
|
|
4919
|
-
if (!hasInpaintModal) {
|
|
4920
|
-
return;
|
|
4921
|
-
}
|
|
4922
|
-
inpaintStatus.textContent = message || "";
|
|
4923
|
-
if (tone) {
|
|
4924
|
-
inpaintStatus.setAttribute("data-tone", tone);
|
|
4925
|
-
} else {
|
|
4926
|
-
inpaintStatus.removeAttribute("data-tone");
|
|
4927
|
-
}
|
|
4928
|
-
}
|
|
4929
|
-
|
|
4930
|
-
const inpaintColorPalette = ["#ff2d55", "#3b82f6", "#22c55e", "#f59e0b", "#a855f7", "#06b6d4", "#f97316", "#ec4899"];
|
|
4931
|
-
|
|
4932
|
-
function syncInpaintPromptValuesFromDom() {
|
|
4933
|
-
const fields = Array.from(inpaintPrompts.querySelectorAll("[data-color]"));
|
|
4934
|
-
for (const field of fields) {
|
|
4935
|
-
if (field instanceof HTMLTextAreaElement) {
|
|
4936
|
-
updateInpaintPrompt(field.dataset.color || "", field.value);
|
|
4937
|
-
}
|
|
4938
|
-
}
|
|
4939
|
-
}
|
|
4940
|
-
|
|
4941
|
-
function attachmentId() {
|
|
4942
|
-
return "att_" + Date.now().toString(36) + "_" + Math.random().toString(16).slice(2, 8);
|
|
4943
|
-
}
|
|
4944
|
-
|
|
4945
|
-
function attachmentsForScope(scope, color) {
|
|
4946
|
-
const normalizedColor = color ? normalizeHexColor(color) : "";
|
|
4947
|
-
return inpaintAttachments.filter((attachment) => (
|
|
4948
|
-
attachment.scope === scope && (!normalizedColor || attachment.color === normalizedColor)
|
|
4949
|
-
));
|
|
4950
|
-
}
|
|
4951
|
-
|
|
4952
|
-
function renderAttachmentChips(container, scope, color) {
|
|
4953
|
-
container.innerHTML = "";
|
|
4954
|
-
for (const attachment of attachmentsForScope(scope, color)) {
|
|
4955
|
-
const chip = document.createElement("span");
|
|
4956
|
-
chip.className = "editor-inpaint-attachment-chip";
|
|
4957
|
-
const preview = document.createElement("img");
|
|
4958
|
-
preview.src = attachment.url;
|
|
4959
|
-
preview.alt = "";
|
|
4960
|
-
preview.loading = "lazy";
|
|
4961
|
-
const label = document.createElement("span");
|
|
4962
|
-
label.textContent = attachment.fileName || "reference image";
|
|
4963
|
-
const remove = document.createElement("button");
|
|
4964
|
-
remove.type = "button";
|
|
4965
|
-
remove.textContent = "×";
|
|
4966
|
-
remove.dataset.editorInpaintRemoveAttachment = attachment.id;
|
|
4967
|
-
remove.setAttribute("aria-label", "Remove " + (attachment.fileName || "reference image"));
|
|
4968
|
-
chip.append(preview, label, remove);
|
|
4969
|
-
container.append(chip);
|
|
4970
|
-
}
|
|
4971
|
-
}
|
|
4972
|
-
|
|
4973
|
-
function renderAllAttachmentChips() {
|
|
4974
|
-
if (!hasInpaintModal) return;
|
|
4975
|
-
renderAttachmentChips(inpaintGlobalAttachments, "global");
|
|
4976
|
-
for (const container of Array.from(inpaintPrompts.querySelectorAll("[data-editor-inpaint-attachments]"))) {
|
|
4977
|
-
if (container instanceof HTMLElement && container.dataset.color) {
|
|
4978
|
-
renderAttachmentChips(container, "color", container.dataset.color);
|
|
4979
|
-
}
|
|
4980
|
-
}
|
|
4981
|
-
}
|
|
4982
|
-
|
|
4983
|
-
function openAttachmentPicker(scope, color) {
|
|
4984
|
-
pendingInpaintAttachmentScope = { scope, color: color ? normalizeHexColor(color) : "" };
|
|
4985
|
-
inpaintAttachmentInput.click();
|
|
4986
|
-
}
|
|
4987
|
-
|
|
4988
|
-
function isAllowedInpaintAttachment(file) {
|
|
4989
|
-
return file instanceof File && allowedInpaintAttachmentTypes.has(file.type);
|
|
4990
|
-
}
|
|
4991
|
-
|
|
4992
|
-
function imageFilesFromPaste(event) {
|
|
4993
|
-
const clipboard = event.clipboardData;
|
|
4994
|
-
if (!clipboard) return [];
|
|
4995
|
-
const files = [];
|
|
4996
|
-
for (const item of Array.from(clipboard.items || [])) {
|
|
4997
|
-
if (item.kind !== "file") continue;
|
|
4998
|
-
const file = item.getAsFile();
|
|
4999
|
-
if (file) files.push(file);
|
|
5000
|
-
}
|
|
5001
|
-
if (!files.length) {
|
|
5002
|
-
for (const file of Array.from(clipboard.files || [])) {
|
|
5003
|
-
files.push(file);
|
|
5004
|
-
}
|
|
5005
|
-
}
|
|
5006
|
-
return files;
|
|
5007
|
-
}
|
|
5008
|
-
|
|
5009
|
-
function handleAttachmentPaste(event, scope, color) {
|
|
5010
|
-
const files = imageFilesFromPaste(event);
|
|
5011
|
-
if (!files.length) return;
|
|
5012
|
-
event.preventDefault();
|
|
5013
|
-
const allowed = files.filter(isAllowedInpaintAttachment);
|
|
5014
|
-
if (!allowed.length) {
|
|
5015
|
-
setInpaintStatus("Reference attachments must be JPEG, PNG, or WebP images.", "error");
|
|
5016
|
-
return;
|
|
5017
|
-
}
|
|
5018
|
-
void addAttachmentFilesForScope(allowed, {
|
|
5019
|
-
scope,
|
|
5020
|
-
color: scope === "color" ? normalizeHexColor(color) : ""
|
|
5021
|
-
});
|
|
5022
|
-
}
|
|
5023
|
-
|
|
5024
|
-
function pickNextInpaintColor(requestedColor) {
|
|
5025
|
-
const requested = normalizeHexColor(requestedColor);
|
|
5026
|
-
const used = new Set(inpaintMaskColors.map((entry) => entry.color));
|
|
5027
|
-
if (!used.has(requested)) {
|
|
5028
|
-
return requested;
|
|
5029
|
-
}
|
|
5030
|
-
for (const color of inpaintColorPalette) {
|
|
5031
|
-
if (!used.has(color)) {
|
|
5032
|
-
return color;
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
for (let index = 0; index < 32; index += 1) {
|
|
5036
|
-
const color = "#" + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, "0");
|
|
5037
|
-
if (!used.has(color)) {
|
|
5038
|
-
return color;
|
|
5039
|
-
}
|
|
5040
|
-
}
|
|
5041
|
-
return requested;
|
|
5042
|
-
}
|
|
5043
|
-
|
|
5044
|
-
function renderInpaintControls() {
|
|
5045
|
-
if (!hasInpaintModal) {
|
|
5046
|
-
return;
|
|
5047
|
-
}
|
|
5048
|
-
syncInpaintPromptValuesFromDom();
|
|
5049
|
-
inpaintBrushLabel.textContent = inpaintBrush.value;
|
|
5050
|
-
inpaintSwatches.innerHTML = "";
|
|
5051
|
-
inpaintPrompts.innerHTML = "";
|
|
5052
|
-
for (const entry of inpaintMaskColors) {
|
|
5053
|
-
const row = document.createElement("div");
|
|
5054
|
-
row.className = "editor-inpaint-color-row";
|
|
5055
|
-
row.dataset.color = entry.color;
|
|
5056
|
-
row.dataset.active = entry.color === activeInpaintColor ? "true" : "false";
|
|
5057
|
-
const dot = document.createElement("button");
|
|
5058
|
-
dot.type = "button";
|
|
5059
|
-
dot.className = "editor-inpaint-color-dot";
|
|
5060
|
-
dot.style.setProperty("--inpaint-color", entry.color);
|
|
5061
|
-
dot.dataset.color = entry.color;
|
|
5062
|
-
dot.setAttribute("aria-label", "Use mask color " + entry.color);
|
|
5063
|
-
dot.setAttribute("title", entry.color);
|
|
5064
|
-
const colorTools = document.createElement("div");
|
|
5065
|
-
colorTools.className = "editor-inpaint-color-tools";
|
|
5066
|
-
colorTools.append(dot);
|
|
5067
|
-
if (inpaintMaskColors.length > 1) {
|
|
5068
|
-
const removeColor = document.createElement("button");
|
|
5069
|
-
removeColor.type = "button";
|
|
5070
|
-
removeColor.className = "editor-inpaint-color-remove";
|
|
5071
|
-
removeColor.dataset.editorInpaintRemoveColor = entry.color;
|
|
5072
|
-
removeColor.setAttribute("aria-label", "Remove mask color " + entry.color);
|
|
5073
|
-
removeColor.setAttribute("title", "Remove color");
|
|
5074
|
-
removeColor.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M5 5l10 10"></path><path d="M15 5 5 15"></path></svg>';
|
|
5075
|
-
removeColor.addEventListener("click", (event) => {
|
|
5076
|
-
event.preventDefault();
|
|
5077
|
-
event.stopPropagation();
|
|
5078
|
-
removeInpaintColor(entry.color);
|
|
5079
|
-
});
|
|
5080
|
-
colorTools.append(removeColor);
|
|
5081
|
-
}
|
|
5082
|
-
const prompt = document.createElement("textarea");
|
|
5083
|
-
prompt.className = "editor-inpaint-prompt";
|
|
5084
|
-
prompt.dataset.color = entry.color;
|
|
5085
|
-
prompt.value = entry.prompt || "";
|
|
5086
|
-
prompt.placeholder = "In color " + entry.color + " replace with...";
|
|
5087
|
-
const stack = document.createElement("div");
|
|
5088
|
-
stack.className = "editor-inpaint-prompt-stack";
|
|
5089
|
-
const field = document.createElement("div");
|
|
5090
|
-
field.className = "editor-inpaint-prompt-field";
|
|
5091
|
-
const tools = document.createElement("div");
|
|
5092
|
-
tools.className = "editor-inpaint-attachment-tools";
|
|
5093
|
-
const attach = document.createElement("button");
|
|
5094
|
-
attach.type = "button";
|
|
5095
|
-
attach.className = "editor-inpaint-attach-button";
|
|
5096
|
-
attach.dataset.editorInpaintAttachScope = "color";
|
|
5097
|
-
attach.dataset.color = entry.color;
|
|
5098
|
-
attach.setAttribute("aria-label", "Attach reference image for " + entry.color);
|
|
5099
|
-
attach.setAttribute("title", "Attach reference image");
|
|
5100
|
-
attach.innerHTML = '<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8.1 10.7 4.8-4.8a2.5 2.5 0 0 1 3.5 3.5l-6.1 6.1a4 4 0 0 1-5.7-5.7l6.3-6.3"></path><path d="m10 8.8-4.1 4.1a1.6 1.6 0 0 0 2.3 2.3l5.6-5.6"></path></svg>';
|
|
5101
|
-
const chips = document.createElement("span");
|
|
5102
|
-
chips.className = "editor-inpaint-attachment-chips";
|
|
5103
|
-
chips.dataset.editorInpaintAttachments = "color";
|
|
5104
|
-
chips.dataset.color = entry.color;
|
|
5105
|
-
field.append(prompt, attach);
|
|
5106
|
-
tools.append(chips);
|
|
5107
|
-
stack.append(field, tools);
|
|
5108
|
-
row.append(colorTools, stack);
|
|
5109
|
-
inpaintPrompts.append(row);
|
|
5110
|
-
}
|
|
5111
|
-
renderAllAttachmentChips();
|
|
5112
|
-
}
|
|
5113
|
-
|
|
5114
|
-
function redrawInpaintMask() {
|
|
5115
|
-
if (!hasInpaintModal) {
|
|
5116
|
-
return;
|
|
5117
|
-
}
|
|
5118
|
-
const ctx = inpaintCanvas.getContext("2d");
|
|
5119
|
-
if (!ctx) {
|
|
5120
|
-
return;
|
|
5121
|
-
}
|
|
5122
|
-
ctx.clearRect(0, 0, inpaintCanvas.width, inpaintCanvas.height);
|
|
5123
|
-
ctx.lineCap = "round";
|
|
5124
|
-
ctx.lineJoin = "round";
|
|
5125
|
-
for (const stroke of inpaintStrokes) {
|
|
5126
|
-
if (!stroke.points || stroke.points.length < 1) {
|
|
5127
|
-
continue;
|
|
5128
|
-
}
|
|
5129
|
-
ctx.strokeStyle = stroke.color;
|
|
5130
|
-
ctx.lineWidth = stroke.size;
|
|
5131
|
-
ctx.globalAlpha = 1;
|
|
5132
|
-
ctx.beginPath();
|
|
5133
|
-
ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
|
|
5134
|
-
if (stroke.points.length === 1) {
|
|
5135
|
-
ctx.lineTo(stroke.points[0].x + 0.01, stroke.points[0].y + 0.01);
|
|
5136
|
-
} else {
|
|
5137
|
-
for (let index = 1; index < stroke.points.length; index += 1) {
|
|
5138
|
-
ctx.lineTo(stroke.points[index].x, stroke.points[index].y);
|
|
5139
|
-
}
|
|
5140
|
-
}
|
|
5141
|
-
ctx.stroke();
|
|
5142
|
-
}
|
|
5143
|
-
}
|
|
5144
|
-
|
|
5145
|
-
function syncInpaintCanvasSize() {
|
|
5146
|
-
if (!hasInpaintModal || !inpaintImage.naturalWidth || !inpaintImage.naturalHeight) {
|
|
5147
|
-
return;
|
|
5148
|
-
}
|
|
5149
|
-
if (inpaintCanvas.width !== inpaintImage.naturalWidth || inpaintCanvas.height !== inpaintImage.naturalHeight) {
|
|
5150
|
-
inpaintCanvas.width = inpaintImage.naturalWidth;
|
|
5151
|
-
inpaintCanvas.height = inpaintImage.naturalHeight;
|
|
5152
|
-
redrawInpaintMask();
|
|
5153
|
-
}
|
|
5154
|
-
}
|
|
5155
|
-
|
|
5156
|
-
function getInpaintPoint(event) {
|
|
5157
|
-
const rect = inpaintCanvas.getBoundingClientRect();
|
|
5158
|
-
return {
|
|
5159
|
-
x: Math.max(0, Math.min(inpaintCanvas.width, (event.clientX - rect.left) * (inpaintCanvas.width / rect.width))),
|
|
5160
|
-
y: Math.max(0, Math.min(inpaintCanvas.height, (event.clientY - rect.top) * (inpaintCanvas.height / rect.height)))
|
|
5161
|
-
};
|
|
5162
|
-
}
|
|
5163
|
-
|
|
5164
|
-
function beginInpaintStroke(event) {
|
|
5165
|
-
if (!hasInpaintModal || !activeInpaintImage || !inpaintCanvas.width || inpaintSubmit.disabled) {
|
|
5166
|
-
return;
|
|
5167
|
-
}
|
|
5168
|
-
event.preventDefault();
|
|
5169
|
-
inpaintCanvas.setPointerCapture(event.pointerId);
|
|
5170
|
-
isInpaintPainting = true;
|
|
5171
|
-
currentInpaintStroke = {
|
|
5172
|
-
color: activeInpaintColor,
|
|
5173
|
-
size: Number(inpaintBrush.value) || 40,
|
|
5174
|
-
points: [getInpaintPoint(event)]
|
|
5175
|
-
};
|
|
5176
|
-
inpaintStrokes.push(currentInpaintStroke);
|
|
5177
|
-
redrawInpaintMask();
|
|
5178
|
-
}
|
|
5179
|
-
|
|
5180
|
-
function continueInpaintStroke(event) {
|
|
5181
|
-
if (!isInpaintPainting || !currentInpaintStroke) {
|
|
5182
|
-
return;
|
|
5183
|
-
}
|
|
5184
|
-
event.preventDefault();
|
|
5185
|
-
currentInpaintStroke.points.push(getInpaintPoint(event));
|
|
5186
|
-
redrawInpaintMask();
|
|
5187
|
-
}
|
|
5188
|
-
|
|
5189
|
-
function endInpaintStroke(event) {
|
|
5190
|
-
if (!isInpaintPainting) {
|
|
5191
|
-
return;
|
|
5192
|
-
}
|
|
5193
|
-
event.preventDefault();
|
|
5194
|
-
isInpaintPainting = false;
|
|
5195
|
-
currentInpaintStroke = null;
|
|
5196
|
-
try {
|
|
5197
|
-
inpaintCanvas.releasePointerCapture(event.pointerId);
|
|
5198
|
-
} catch {}
|
|
5199
|
-
}
|
|
5200
|
-
|
|
5201
|
-
function addInpaintColor(color) {
|
|
5202
|
-
syncInpaintPromptValuesFromDom();
|
|
5203
|
-
const normalized = pickNextInpaintColor(color);
|
|
5204
|
-
if (!inpaintMaskColors.some((entry) => entry.color === normalized)) {
|
|
5205
|
-
inpaintMaskColors.push({ color: normalized, prompt: "" });
|
|
5206
|
-
}
|
|
5207
|
-
activeInpaintColor = normalized;
|
|
5208
|
-
if (hasInpaintModal) {
|
|
5209
|
-
inpaintColorPicker.value = normalized;
|
|
5210
|
-
}
|
|
5211
|
-
renderInpaintControls();
|
|
5212
|
-
}
|
|
5213
|
-
|
|
5214
|
-
function removeInpaintColor(color) {
|
|
5215
|
-
syncInpaintPromptValuesFromDom();
|
|
5216
|
-
const normalized = normalizeHexColor(color);
|
|
5217
|
-
if (inpaintMaskColors.length <= 1 || !inpaintMaskColors.some((entry) => entry.color === normalized)) {
|
|
5218
|
-
setInpaintStatus("Keep at least one mask color.", "error");
|
|
5219
|
-
return;
|
|
5220
|
-
}
|
|
5221
|
-
inpaintMaskColors = inpaintMaskColors.filter((entry) => entry.color !== normalized);
|
|
5222
|
-
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.scope !== "color" || attachment.color !== normalized);
|
|
5223
|
-
inpaintStrokes = inpaintStrokes.filter((stroke) => normalizeHexColor(stroke.color) !== normalized);
|
|
5224
|
-
if (activeInpaintColor === normalized) {
|
|
5225
|
-
activeInpaintColor = inpaintMaskColors[0]?.color || "#ff2d55";
|
|
5226
|
-
if (hasInpaintModal) {
|
|
5227
|
-
inpaintColorPicker.value = activeInpaintColor;
|
|
5228
|
-
}
|
|
5229
|
-
}
|
|
5230
|
-
redrawInpaintMask();
|
|
5231
|
-
renderInpaintControls();
|
|
5232
|
-
setInpaintStatus("", "");
|
|
5233
|
-
}
|
|
5234
|
-
|
|
5235
|
-
function selectInpaintColor(color) {
|
|
5236
|
-
activeInpaintColor = normalizeHexColor(color);
|
|
5237
|
-
if (hasInpaintModal) {
|
|
5238
|
-
inpaintColorPicker.value = activeInpaintColor;
|
|
5239
|
-
}
|
|
5240
|
-
for (const row of Array.from(inpaintPrompts.querySelectorAll(".editor-inpaint-color-row"))) {
|
|
5241
|
-
if (row instanceof HTMLElement) {
|
|
5242
|
-
row.dataset.active = row.dataset.color === activeInpaintColor ? "true" : "false";
|
|
5243
|
-
}
|
|
5244
|
-
}
|
|
5245
|
-
}
|
|
5246
|
-
|
|
5247
|
-
function updateInpaintPrompt(color, prompt) {
|
|
5248
|
-
const normalized = normalizeHexColor(color);
|
|
5249
|
-
inpaintMaskColors = inpaintMaskColors.map((entry) => (
|
|
5250
|
-
entry.color === normalized ? { ...entry, prompt: String(prompt || "") } : entry
|
|
5251
|
-
));
|
|
5252
|
-
}
|
|
5253
|
-
|
|
5254
|
-
function buildInpaintInstruction() {
|
|
5255
|
-
const globalPrompt = inpaintGlobalPrompt.value.trim();
|
|
5256
|
-
const regions = inpaintMaskColors
|
|
5257
|
-
.map((entry) => ({
|
|
5258
|
-
color: entry.color.toUpperCase(),
|
|
5259
|
-
prompt: String(entry.prompt || "").trim()
|
|
5260
|
-
}))
|
|
5261
|
-
.filter((entry) => entry.prompt);
|
|
5262
|
-
if (!globalPrompt && !regions.length) {
|
|
5263
|
-
return "";
|
|
5264
|
-
}
|
|
5265
|
-
const attachmentNotes = inpaintAttachments.map((attachment, index) => {
|
|
5266
|
-
const label = "reference image " + (index + 1) + " (" + (attachment.fileName || "attachment") + ")";
|
|
5267
|
-
return attachment.scope === "global"
|
|
5268
|
-
? label + " applies to the whole image instruction"
|
|
5269
|
-
: label + " applies only to the UI mask color " + String(attachment.color || "").toUpperCase();
|
|
5270
|
-
});
|
|
5271
|
-
return [
|
|
5272
|
-
"INPAINTING SYSTEM WRAPPER:",
|
|
5273
|
-
"You are editing an existing image with an alpha mask. Treat transparent mask pixels as the only editable region. Everything outside the editable mask must remain the same image: preserve identity, layout, pose, camera angle, framing, background, lighting, colors, textures, text, logos, and all unmasked details.",
|
|
5274
|
-
"Do not redraw, reinterpret, beautify, crop, zoom, rotate, expand, stylize, or otherwise alter unmasked content. Do not render the UI mask colors; they are labels only.",
|
|
5275
|
-
"The final image must keep the original dimensions and aspect ratio unless the user explicitly asks for a dimension change.",
|
|
5276
|
-
globalPrompt ? "Whole image prompt: " + globalPrompt + ". Apply this only as guidance for the masked edit and overall consistency; it does not permit changing unmasked content." : "",
|
|
5277
|
-
regions.map((entry) => (
|
|
5278
|
-
"Masked region " + entry.color + ": replace only that marked area with " + entry.prompt + ". Match the surrounding perspective, lighting, grain, shadows, and edge blending."
|
|
5279
|
-
)).join(" "),
|
|
5280
|
-
attachmentNotes.length ? "Reference image scope: " + attachmentNotes.join("; ") + ". Use each reference only for its stated scope and do not copy unrelated reference details into unmasked areas." : ""
|
|
5281
|
-
].filter(Boolean).join(" ");
|
|
5282
|
-
}
|
|
5283
|
-
|
|
5284
|
-
function getInpaintAspectRatio() {
|
|
5285
|
-
const width = inpaintCanvas.width || inpaintImage.naturalWidth || 0;
|
|
5286
|
-
const height = inpaintCanvas.height || inpaintImage.naturalHeight || 0;
|
|
5287
|
-
if (!width || !height) {
|
|
5288
|
-
return undefined;
|
|
5289
|
-
}
|
|
5290
|
-
const ratio = width / height;
|
|
5291
|
-
if (Math.abs(ratio - 1) < 0.08) {
|
|
5292
|
-
return "1:1";
|
|
5293
|
-
}
|
|
5294
|
-
if (ratio > 1) {
|
|
5295
|
-
return ratio >= 1.55 ? "16:9" : "4:3";
|
|
5296
|
-
}
|
|
5297
|
-
return ratio <= 0.65 ? "9:16" : "3:4";
|
|
5298
|
-
}
|
|
5299
|
-
|
|
5300
|
-
async function createProviderMaskBlob() {
|
|
5301
|
-
const maskCanvas = document.createElement("canvas");
|
|
5302
|
-
maskCanvas.width = inpaintCanvas.width;
|
|
5303
|
-
maskCanvas.height = inpaintCanvas.height;
|
|
5304
|
-
const ctx = maskCanvas.getContext("2d");
|
|
5305
|
-
if (!ctx) {
|
|
5306
|
-
throw new Error("Unable to prepare mask image.");
|
|
5307
|
-
}
|
|
5308
|
-
ctx.fillStyle = "#ffffff";
|
|
5309
|
-
ctx.fillRect(0, 0, maskCanvas.width, maskCanvas.height);
|
|
5310
|
-
ctx.globalCompositeOperation = "destination-out";
|
|
5311
|
-
ctx.lineCap = "round";
|
|
5312
|
-
ctx.lineJoin = "round";
|
|
5313
|
-
for (const stroke of inpaintStrokes) {
|
|
5314
|
-
if (!stroke.points || stroke.points.length < 1) {
|
|
5315
|
-
continue;
|
|
5316
|
-
}
|
|
5317
|
-
ctx.strokeStyle = "rgba(0, 0, 0, 1)";
|
|
5318
|
-
ctx.lineWidth = stroke.size;
|
|
5319
|
-
ctx.beginPath();
|
|
5320
|
-
ctx.moveTo(stroke.points[0].x, stroke.points[0].y);
|
|
5321
|
-
if (stroke.points.length === 1) {
|
|
5322
|
-
ctx.lineTo(stroke.points[0].x + 0.01, stroke.points[0].y + 0.01);
|
|
5323
|
-
} else {
|
|
5324
|
-
for (let index = 1; index < stroke.points.length; index += 1) {
|
|
5325
|
-
ctx.lineTo(stroke.points[index].x, stroke.points[index].y);
|
|
5326
|
-
}
|
|
5327
|
-
}
|
|
5328
|
-
ctx.stroke();
|
|
5329
|
-
}
|
|
5330
|
-
return canvasToPngBlob(maskCanvas);
|
|
5331
|
-
}
|
|
5332
|
-
|
|
5333
|
-
function canvasToPngBlob(canvas) {
|
|
5334
|
-
return new Promise((resolve, reject) => {
|
|
5335
|
-
canvas.toBlob((blob) => {
|
|
5336
|
-
if (blob) {
|
|
5337
|
-
resolve(blob);
|
|
5338
|
-
} else {
|
|
5339
|
-
reject(new Error("Unable to prepare mask image."));
|
|
5340
|
-
}
|
|
5341
|
-
}, "image/png");
|
|
5342
|
-
});
|
|
5343
|
-
}
|
|
5344
|
-
|
|
5345
|
-
function parseJsonResponse(response) {
|
|
5346
|
-
return response.json().catch(() => ({}));
|
|
5347
|
-
}
|
|
5348
|
-
|
|
5349
|
-
async function loadInpaintProviders() {
|
|
5350
|
-
try {
|
|
5351
|
-
const response = await fetch("/api/v1/user/me/provider-keys", { credentials: "same-origin" });
|
|
5352
|
-
const payload = await parseJsonResponse(response);
|
|
5353
|
-
if (!response.ok) {
|
|
5354
|
-
return;
|
|
5355
|
-
}
|
|
5356
|
-
const supported = new Set(["openai", "gemini", "openrouter"]);
|
|
5357
|
-
const providers = [];
|
|
5358
|
-
const seen = new Set();
|
|
5359
|
-
for (const key of Array.isArray(payload.provider_keys) ? payload.provider_keys : []) {
|
|
5360
|
-
const provider = String(key.provider || "");
|
|
5361
|
-
const status = String(key.status || "active");
|
|
5362
|
-
const cooldownUntil = key.cooldown_until ? Date.parse(String(key.cooldown_until)) : 0;
|
|
5363
|
-
if (!supported.has(provider) || status !== "active" || (cooldownUntil && cooldownUntil > Date.now()) || seen.has(provider)) {
|
|
5364
|
-
continue;
|
|
5365
|
-
}
|
|
5366
|
-
seen.add(provider);
|
|
5367
|
-
providers.push(provider);
|
|
5368
|
-
}
|
|
5369
|
-
inpaintProviderSelect.innerHTML = '<option value="">Auto</option>' + providers.map((provider) => (
|
|
5370
|
-
'<option value="' + provider + '">' + provider.charAt(0).toUpperCase() + provider.slice(1) + '</option>'
|
|
5371
|
-
)).join("");
|
|
5372
|
-
inpaintProviderWrap.setAttribute("data-visible", providers.length > 1 ? "true" : "false");
|
|
5373
|
-
} catch {}
|
|
5374
|
-
}
|
|
5375
|
-
|
|
5376
|
-
async function uploadInpaintMask(blob) {
|
|
5377
|
-
const fileName = "inpaint-mask-" + Date.now() + ".png";
|
|
5378
|
-
const presignResponse = await fetch("/api/v1/user/me/temporary-files/presign", {
|
|
5379
|
-
method: "POST",
|
|
5380
|
-
credentials: "same-origin",
|
|
5381
|
-
headers: { "content-type": "application/json" },
|
|
5382
|
-
body: JSON.stringify({
|
|
5383
|
-
file_name: fileName,
|
|
5384
|
-
content_type: "image/png",
|
|
5385
|
-
size_bytes: blob.size,
|
|
5386
|
-
folder_path: "inpaint-masks"
|
|
5387
|
-
})
|
|
5388
|
-
});
|
|
5389
|
-
const presignPayload = await parseJsonResponse(presignResponse);
|
|
5390
|
-
if (!presignResponse.ok || !presignPayload || !presignPayload.transport) {
|
|
5391
|
-
throw new Error(presignPayload && presignPayload.error ? presignPayload.error : "Unable to prepare mask upload.");
|
|
5392
|
-
}
|
|
5393
|
-
if (presignPayload.transport === "server") {
|
|
5394
|
-
const formData = new FormData();
|
|
5395
|
-
formData.append(presignPayload.upload?.form_field || "file", new File([blob], fileName, { type: "image/png" }));
|
|
5396
|
-
formData.append("folder_path", "inpaint-masks");
|
|
5397
|
-
const uploadResponse = await fetch(presignPayload.upload?.url || "/api/v1/user/me/temporary-files/upload", {
|
|
5398
|
-
method: presignPayload.upload?.method || "POST",
|
|
5399
|
-
credentials: "same-origin",
|
|
5400
|
-
body: formData
|
|
5401
|
-
});
|
|
5402
|
-
const uploadPayload = await parseJsonResponse(uploadResponse);
|
|
5403
|
-
if (!uploadResponse.ok || !uploadPayload.file) {
|
|
5404
|
-
throw new Error(uploadPayload.error || "Unable to upload mask.");
|
|
5405
|
-
}
|
|
5406
|
-
return uploadPayload.file.viewUrl || uploadPayload.file.view_url || uploadPayload.file.s3Url || uploadPayload.file.s3_url;
|
|
5407
|
-
}
|
|
5408
|
-
const uploadResponse = await fetch(presignPayload.upload.url, {
|
|
5409
|
-
method: presignPayload.upload.method,
|
|
5410
|
-
headers: presignPayload.upload.headers,
|
|
5411
|
-
body: blob
|
|
5412
|
-
});
|
|
5413
|
-
if (!uploadResponse.ok) {
|
|
5414
|
-
throw new Error("Mask upload failed with status " + uploadResponse.status + ".");
|
|
5415
|
-
}
|
|
5416
|
-
const finalizeResponse = await fetch("/api/v1/user/me/temporary-files", {
|
|
5417
|
-
method: "POST",
|
|
5418
|
-
credentials: "same-origin",
|
|
5419
|
-
headers: { "content-type": "application/json" },
|
|
5420
|
-
body: JSON.stringify({
|
|
5421
|
-
file_id: presignPayload.file_id,
|
|
5422
|
-
file_name: presignPayload.file_name,
|
|
5423
|
-
content_type: presignPayload.content_type,
|
|
5424
|
-
size_bytes: blob.size,
|
|
5425
|
-
storage_key: presignPayload.storage_key,
|
|
5426
|
-
folder_path: presignPayload.folder_path || "inpaint-masks"
|
|
5427
|
-
})
|
|
5428
|
-
});
|
|
5429
|
-
const finalizePayload = await parseJsonResponse(finalizeResponse);
|
|
5430
|
-
if (!finalizeResponse.ok || !finalizePayload.file) {
|
|
5431
|
-
throw new Error(finalizePayload.error || "Unable to save mask.");
|
|
5432
|
-
}
|
|
5433
|
-
return finalizePayload.file.viewUrl || finalizePayload.file.view_url || finalizePayload.file.s3Url || finalizePayload.file.s3_url;
|
|
5434
|
-
}
|
|
5435
|
-
|
|
5436
|
-
function absoluteUrl(value) {
|
|
5437
|
-
return new URL(value, window.location.href).toString();
|
|
5438
|
-
}
|
|
5439
|
-
|
|
5440
|
-
async function uploadInpaintAttachment(file) {
|
|
5441
|
-
const fileName = file.name || "reference.png";
|
|
5442
|
-
const contentType = file.type || "image/png";
|
|
5443
|
-
const presignResponse = await fetch("/api/v1/user/me/temporary-files/presign", {
|
|
5444
|
-
method: "POST",
|
|
5445
|
-
credentials: "same-origin",
|
|
5446
|
-
headers: { "content-type": "application/json" },
|
|
5447
|
-
body: JSON.stringify({
|
|
5448
|
-
file_name: fileName,
|
|
5449
|
-
content_type: contentType,
|
|
5450
|
-
size_bytes: file.size,
|
|
5451
|
-
folder_path: "inpaint-references"
|
|
5452
|
-
})
|
|
5453
|
-
});
|
|
5454
|
-
const presignPayload = await parseJsonResponse(presignResponse);
|
|
5455
|
-
if (!presignResponse.ok || !presignPayload || !presignPayload.transport) {
|
|
5456
|
-
throw new Error(presignPayload && presignPayload.error ? presignPayload.error : "Unable to prepare reference upload.");
|
|
5457
|
-
}
|
|
5458
|
-
if (presignPayload.transport === "server") {
|
|
5459
|
-
const formData = new FormData();
|
|
5460
|
-
formData.append(presignPayload.upload?.form_field || "file", new File([file], fileName, { type: contentType }));
|
|
5461
|
-
formData.append("folder_path", "inpaint-references");
|
|
5462
|
-
const uploadResponse = await fetch(presignPayload.upload?.url || "/api/v1/user/me/temporary-files/upload", {
|
|
5463
|
-
method: presignPayload.upload?.method || "POST",
|
|
5464
|
-
credentials: "same-origin",
|
|
5465
|
-
body: formData
|
|
5466
|
-
});
|
|
5467
|
-
const uploadPayload = await parseJsonResponse(uploadResponse);
|
|
5468
|
-
if (!uploadResponse.ok || !uploadPayload.file) {
|
|
5469
|
-
throw new Error(uploadPayload.error || "Unable to upload reference image.");
|
|
5470
|
-
}
|
|
5471
|
-
return uploadPayload.file.viewUrl || uploadPayload.file.view_url || uploadPayload.file.s3Url || uploadPayload.file.s3_url;
|
|
5472
|
-
}
|
|
5473
|
-
const uploadResponse = await fetch(presignPayload.upload.url, {
|
|
5474
|
-
method: presignPayload.upload.method,
|
|
5475
|
-
headers: presignPayload.upload.headers,
|
|
5476
|
-
body: file
|
|
5477
|
-
});
|
|
5478
|
-
if (!uploadResponse.ok) {
|
|
5479
|
-
throw new Error("Reference upload failed with status " + uploadResponse.status + ".");
|
|
5480
|
-
}
|
|
5481
|
-
const finalizeResponse = await fetch("/api/v1/user/me/temporary-files", {
|
|
5482
|
-
method: "POST",
|
|
5483
|
-
credentials: "same-origin",
|
|
5484
|
-
headers: { "content-type": "application/json" },
|
|
5485
|
-
body: JSON.stringify({
|
|
5486
|
-
file_id: presignPayload.file_id,
|
|
5487
|
-
file_name: presignPayload.file_name,
|
|
5488
|
-
content_type: presignPayload.content_type,
|
|
5489
|
-
size_bytes: file.size,
|
|
5490
|
-
storage_key: presignPayload.storage_key,
|
|
5491
|
-
folder_path: presignPayload.folder_path || "inpaint-references"
|
|
5492
|
-
})
|
|
5493
|
-
});
|
|
5494
|
-
const finalizePayload = await parseJsonResponse(finalizeResponse);
|
|
5495
|
-
if (!finalizeResponse.ok || !finalizePayload.file) {
|
|
5496
|
-
throw new Error(finalizePayload.error || "Unable to save reference image.");
|
|
5497
|
-
}
|
|
5498
|
-
return finalizePayload.file.viewUrl || finalizePayload.file.view_url || finalizePayload.file.s3Url || finalizePayload.file.s3_url;
|
|
5499
|
-
}
|
|
5500
|
-
|
|
5501
|
-
async function addAttachmentFilesForScope(files, scope) {
|
|
5502
|
-
if (!scope) return;
|
|
5503
|
-
const selected = Array.from(files || []);
|
|
5504
|
-
if (!selected.length) return;
|
|
5505
|
-
if (inpaintAttachments.length + selected.length > maxInpaintAttachments) {
|
|
5506
|
-
setInpaintStatus("Use up to " + maxInpaintAttachments + " reference images.", "error");
|
|
5507
|
-
return;
|
|
5508
|
-
}
|
|
5509
|
-
const previousDisabled = inpaintSubmit.disabled;
|
|
5510
|
-
inpaintSubmit.disabled = true;
|
|
5511
|
-
setInpaintStatus("Uploading reference image...", "");
|
|
5512
|
-
try {
|
|
5513
|
-
for (const file of selected) {
|
|
5514
|
-
if (!isAllowedInpaintAttachment(file)) {
|
|
5515
|
-
throw new Error("Reference attachments must be JPEG, PNG, or WebP images.");
|
|
5516
|
-
}
|
|
5517
|
-
const uploadedUrl = await uploadInpaintAttachment(file);
|
|
5518
|
-
inpaintAttachments.push({
|
|
5519
|
-
id: attachmentId(),
|
|
5520
|
-
scope: scope.scope === "color" ? "color" : "global",
|
|
5521
|
-
color: scope.scope === "color" ? normalizeHexColor(scope.color) : "",
|
|
5522
|
-
fileName: file.name || "reference.png",
|
|
5523
|
-
url: absoluteUrl(uploadedUrl)
|
|
5524
|
-
});
|
|
5525
|
-
}
|
|
5526
|
-
renderAllAttachmentChips();
|
|
5527
|
-
setInpaintStatus("Reference image ready.", "success");
|
|
5528
|
-
} catch (error) {
|
|
5529
|
-
console.error(error);
|
|
5530
|
-
setInpaintStatus(error instanceof Error ? error.message : "Unable to upload reference image.", "error");
|
|
5531
|
-
} finally {
|
|
5532
|
-
inpaintSubmit.disabled = previousDisabled;
|
|
5533
|
-
}
|
|
5534
|
-
}
|
|
5535
|
-
|
|
5536
|
-
async function addAttachmentFiles(files) {
|
|
5537
|
-
const scope = pendingInpaintAttachmentScope;
|
|
5538
|
-
pendingInpaintAttachmentScope = null;
|
|
5539
|
-
await addAttachmentFilesForScope(files, scope);
|
|
5540
|
-
}
|
|
5541
|
-
|
|
5542
|
-
async function submitInpaintJob() {
|
|
5543
|
-
if (!hasInpaintModal || !activeInpaintImage || inpaintSubmit.disabled) {
|
|
5544
|
-
return;
|
|
5545
|
-
}
|
|
5546
|
-
if (!inpaintStrokes.length) {
|
|
5547
|
-
setInpaintStatus("Paint at least one mask region.", "error");
|
|
5548
|
-
return;
|
|
5549
|
-
}
|
|
5550
|
-
const usedColors = new Set(inpaintStrokes.map((stroke) => normalizeHexColor(stroke.color)));
|
|
5551
|
-
const globalPrompt = inpaintGlobalPrompt.value.trim();
|
|
5552
|
-
const missingPromptColor = inpaintMaskColors.find((entry) => usedColors.has(entry.color) && !String(entry.prompt || "").trim());
|
|
5553
|
-
if (!globalPrompt && missingPromptColor) {
|
|
5554
|
-
setInpaintStatus("Add a replacement prompt for " + missingPromptColor.color.toUpperCase() + ".", "error");
|
|
5555
|
-
return;
|
|
5556
|
-
}
|
|
5557
|
-
const instruction = buildInpaintInstruction();
|
|
5558
|
-
if (!instruction) {
|
|
5559
|
-
setInpaintStatus("Add a replacement prompt for at least one color.", "error");
|
|
5560
|
-
return;
|
|
5561
|
-
}
|
|
5562
|
-
const currentTracers = tracerStore.get();
|
|
5563
|
-
const tracer = currentTracers[0] || tracerStore.add();
|
|
5564
|
-
if (!currentTracers.length) {
|
|
5565
|
-
renderTracerChips(tracerStore.get());
|
|
5566
|
-
}
|
|
5567
|
-
inpaintSubmit.disabled = true;
|
|
5568
|
-
setInpaintStatus("Uploading mask...", "");
|
|
5569
|
-
try {
|
|
5570
|
-
const maskUrl = await uploadInpaintMask(await createProviderMaskBlob());
|
|
5571
|
-
setInpaintStatus("Creating image edit job...", "");
|
|
5572
|
-
const response = await fetch("/api/v1/primitives/images/inpaint", {
|
|
5573
|
-
method: "POST",
|
|
5574
|
-
credentials: "same-origin",
|
|
5575
|
-
headers: { "content-type": "application/json" },
|
|
5576
|
-
body: JSON.stringify({
|
|
5577
|
-
tracer,
|
|
5578
|
-
payload: {
|
|
5579
|
-
source_image_url: absoluteUrl(activeInpaintImage.url),
|
|
5580
|
-
mask_url: absoluteUrl(maskUrl),
|
|
5581
|
-
instruction,
|
|
5582
|
-
reference_attachments: inpaintAttachments.map((attachment) => attachment.url),
|
|
5583
|
-
...(inpaintProviderSelect.value ? { provider: inpaintProviderSelect.value } : {}),
|
|
5584
|
-
size: "auto",
|
|
5585
|
-
...(getInpaintAspectRatio() ? { aspect_ratio: getInpaintAspectRatio() } : {}),
|
|
5586
|
-
output_format: "png"
|
|
5587
|
-
}
|
|
5588
|
-
})
|
|
5589
|
-
});
|
|
5590
|
-
const payload = await parseJsonResponse(response);
|
|
5591
|
-
if (!response.ok) {
|
|
5592
|
-
throw new Error(payload.error || "Unable to create image edit job.");
|
|
5593
|
-
}
|
|
5594
|
-
setInpaintStatus("Image edit job " + (payload.job_id || "") + " started.", "success");
|
|
5595
|
-
await loadHistory({ showLoading: false });
|
|
5596
|
-
window.setTimeout(() => loadHistory({ showLoading: false }), 2200);
|
|
5597
|
-
window.setTimeout(() => loadHistory({ showLoading: false }), 6000);
|
|
5598
|
-
} catch (error) {
|
|
5599
|
-
console.error(error);
|
|
5600
|
-
setInpaintStatus(error instanceof Error ? error.message : "Unable to create image edit job.", "error");
|
|
5601
|
-
} finally {
|
|
5602
|
-
inpaintSubmit.disabled = false;
|
|
5603
|
-
}
|
|
5604
|
-
}
|
|
5605
|
-
|
|
5606
|
-
function openInpaintModalForItem(item) {
|
|
5607
|
-
if (!hasInpaintModal || !item || !item.url) {
|
|
5608
|
-
return;
|
|
5609
|
-
}
|
|
5610
|
-
activeInpaintImage = { url: item.url, label: item.label || "Output preview" };
|
|
5611
|
-
inpaintMaskColors = [{ color: "#ff2d55", prompt: "" }];
|
|
5612
|
-
inpaintAttachments = [];
|
|
5613
|
-
pendingInpaintAttachmentScope = null;
|
|
5614
|
-
activeInpaintColor = "#ff2d55";
|
|
5615
|
-
inpaintStrokes = [];
|
|
5616
|
-
currentInpaintStroke = null;
|
|
5617
|
-
isInpaintPainting = false;
|
|
5618
|
-
inpaintBrush.value = "70";
|
|
5619
|
-
inpaintColorPicker.value = activeInpaintColor;
|
|
5620
|
-
inpaintGlobalPrompt.value = "";
|
|
5621
|
-
inpaintImage.removeAttribute("src");
|
|
5622
|
-
inpaintCanvas.width = 1;
|
|
5623
|
-
inpaintCanvas.height = 1;
|
|
5624
|
-
inpaintSource.textContent = activeInpaintImage.label;
|
|
5625
|
-
setInpaintStatus("", "");
|
|
5626
|
-
renderInpaintControls();
|
|
5627
|
-
inpaintImage.onload = () => {
|
|
5628
|
-
syncInpaintCanvasSize();
|
|
5629
|
-
redrawInpaintMask();
|
|
5630
|
-
};
|
|
5631
|
-
inpaintImage.src = item.url;
|
|
5632
|
-
inpaintModalBackdrop.hidden = false;
|
|
5633
|
-
inpaintModalBackdrop.setAttribute("data-open", "true");
|
|
5634
|
-
}
|
|
5635
|
-
|
|
5636
|
-
function openInpaintModalFromActiveMedia() {
|
|
5637
|
-
if (!hasMediaModal || !hasInpaintModal) {
|
|
5638
|
-
return;
|
|
5639
|
-
}
|
|
5640
|
-
const item = activeMediaItems[activeMediaIndex] || null;
|
|
5641
|
-
if (!item || (item.kind || getPreviewKind(item.url)) !== "image") {
|
|
5642
|
-
return;
|
|
5643
|
-
}
|
|
5644
|
-
openInpaintModalForItem(item);
|
|
5645
|
-
}
|
|
5646
|
-
|
|
5647
|
-
function buildInpaintPageUrlForMedia(item, options) {
|
|
5648
|
-
const url = item && item.url ? String(item.url) : "";
|
|
5649
|
-
if (!url) {
|
|
5650
|
-
return "";
|
|
5651
|
-
}
|
|
5652
|
-
const pageUrl = new URL("/inpaint", window.location.origin);
|
|
5653
|
-
const currentTracers = tracerStore.get();
|
|
5654
|
-
const tracer = currentTracers[0] || (options && options.ensureTracer ? tracerStore.add() : "");
|
|
5655
|
-
if (tracer) {
|
|
5656
|
-
pageUrl.searchParams.set("tracer", tracer);
|
|
5657
|
-
}
|
|
5658
|
-
pageUrl.searchParams.set("source_image_url", url);
|
|
5659
|
-
pageUrl.searchParams.set("source_image_name", item.label || mediaFileNameFromUrl(url, "Output image"));
|
|
5660
|
-
return pageUrl.toString();
|
|
5661
|
-
}
|
|
5662
|
-
|
|
5663
|
-
function closeInpaintModal() {
|
|
5664
|
-
if (!hasInpaintModal) {
|
|
5665
|
-
return;
|
|
5666
|
-
}
|
|
5667
|
-
inpaintModalBackdrop.setAttribute("data-open", "false");
|
|
5668
|
-
inpaintModalBackdrop.hidden = true;
|
|
5669
|
-
activeInpaintImage = null;
|
|
5670
|
-
inpaintStrokes = [];
|
|
5671
|
-
currentInpaintStroke = null;
|
|
5672
|
-
isInpaintPainting = false;
|
|
5673
|
-
inpaintImage.removeAttribute("src");
|
|
5674
|
-
const ctx = inpaintCanvas.getContext("2d");
|
|
5675
|
-
if (ctx) {
|
|
5676
|
-
ctx.clearRect(0, 0, inpaintCanvas.width, inpaintCanvas.height);
|
|
5677
|
-
}
|
|
5678
|
-
setInpaintStatus("", "");
|
|
5679
|
-
}
|
|
5680
|
-
|
|
5681
|
-
function renderActiveMediaModalItem() {
|
|
5682
|
-
if (!hasMediaModal) {
|
|
5683
|
-
return;
|
|
5684
|
-
}
|
|
5685
|
-
const item = activeMediaItems[activeMediaIndex] || null;
|
|
5686
|
-
if (!item || !item.url) {
|
|
5687
|
-
return;
|
|
5688
|
-
}
|
|
5689
|
-
const url = item.url;
|
|
5690
|
-
const kind = item.kind || getPreviewKind(url);
|
|
5691
|
-
const label = item.label || "Output preview";
|
|
5692
|
-
mediaModalTitle.textContent = activeMediaItems.length > 1
|
|
5693
|
-
? label + " (" + (activeMediaIndex + 1) + " of " + activeMediaItems.length + ")"
|
|
5694
|
-
: label;
|
|
5695
|
-
mediaModalSource.href = url;
|
|
5696
|
-
mediaModalDownload.href = url;
|
|
5697
|
-
mediaModalDownload.setAttribute("download", mediaFileNameFromUrl(url, label));
|
|
5698
|
-
mediaModalCopy.dataset.url = url;
|
|
5699
|
-
mediaModalCopy.removeAttribute("data-state");
|
|
5700
|
-
mediaModalCopy.setAttribute("aria-label", "Copy media URL");
|
|
5701
|
-
mediaModalCopy.setAttribute("title", "Copy link");
|
|
5702
|
-
mediaModalCopyLabel.textContent = "Copy link";
|
|
5703
|
-
mediaModalCarousel.setAttribute("data-visible", activeMediaItems.length > 1 ? "true" : "false");
|
|
5704
|
-
mediaModalInpaint.setAttribute("data-visible", kind === "image" ? "true" : "false");
|
|
5705
|
-
mediaModalInpaint.href = kind === "image" ? buildInpaintPageUrlForMedia(item, { ensureTracer: false }) : "/inpaint";
|
|
5706
|
-
mediaModalInpaint.setAttribute("aria-label", "Open image editor for " + label);
|
|
5707
|
-
if (kind === "image") {
|
|
5708
|
-
mediaModalBody.innerHTML = '<img src="' + escapeHtml(url) + '" alt="' + escapeHtml(label) + '" />';
|
|
5709
|
-
} else if (kind === "video") {
|
|
5710
|
-
mediaModalBody.innerHTML = '<video src="' + escapeHtml(url) + '" controls autoplay playsinline preload="metadata"></video>';
|
|
5711
|
-
} else {
|
|
5712
|
-
mediaModalBody.innerHTML = '<a class="editor-history-button is-primary" href="' + escapeHtml(url) + '" target="_blank" rel="noreferrer">Open file</a>';
|
|
5713
|
-
}
|
|
5714
|
-
}
|
|
5715
|
-
|
|
5716
|
-
function openMediaModal(url, kind, label) {
|
|
5717
|
-
if (!hasMediaModal) {
|
|
5718
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
5719
|
-
return;
|
|
5720
|
-
}
|
|
5721
|
-
activeMediaItems = [{ url, kind, label }];
|
|
5722
|
-
activeMediaIndex = 0;
|
|
5723
|
-
renderActiveMediaModalItem();
|
|
5724
|
-
mediaModalBackdrop.hidden = false;
|
|
5725
|
-
mediaModalBackdrop.setAttribute("data-open", "true");
|
|
5726
|
-
}
|
|
5727
|
-
|
|
5728
|
-
function openMediaGroupFromButton(button) {
|
|
5729
|
-
const url = button.getAttribute("data-preview-url") || "";
|
|
5730
|
-
const kind = button.getAttribute("data-preview-kind") || "file";
|
|
5731
|
-
const label = button.getAttribute("data-preview-label") || "Output preview";
|
|
5732
|
-
if (!url) {
|
|
5733
|
-
return;
|
|
5734
|
-
}
|
|
5735
|
-
const group = button.closest(".editor-history-card-previews, .editor-history-group-previews");
|
|
5736
|
-
const buttons = group
|
|
5737
|
-
? Array.from(group.querySelectorAll("[data-preview-url]")).filter((entry) => entry instanceof HTMLButtonElement)
|
|
5738
|
-
: [button];
|
|
5739
|
-
const items = buttons
|
|
5740
|
-
.map((entry, index) => ({
|
|
5741
|
-
url: entry.getAttribute("data-preview-url") || "",
|
|
5742
|
-
kind: entry.getAttribute("data-preview-kind") || "file",
|
|
5743
|
-
label: entry.getAttribute("data-preview-label") || ("Output " + (index + 1))
|
|
5744
|
-
}))
|
|
5745
|
-
.filter((entry) => entry.url);
|
|
5746
|
-
const allImages = items.length > 1 && items.every((entry) => entry.kind === "image");
|
|
5747
|
-
if (!allImages) {
|
|
5748
|
-
openMediaModal(url, kind, label);
|
|
5749
|
-
return;
|
|
5750
|
-
}
|
|
5751
|
-
if (!hasMediaModal) {
|
|
5752
|
-
window.open(url, "_blank", "noopener,noreferrer");
|
|
5753
|
-
return;
|
|
5754
|
-
}
|
|
5755
|
-
activeMediaItems = items;
|
|
5756
|
-
activeMediaIndex = Math.max(0, items.findIndex((entry) => entry.url === url));
|
|
5757
|
-
renderActiveMediaModalItem();
|
|
5758
|
-
mediaModalBackdrop.hidden = false;
|
|
5759
|
-
mediaModalBackdrop.setAttribute("data-open", "true");
|
|
5760
|
-
}
|
|
5761
|
-
|
|
5762
|
-
function openExampleModal(exampleIndex, mediaIndex) {
|
|
5763
|
-
const example = Array.isArray(templateExamples[exampleIndex]) ? templateExamples[exampleIndex] : [];
|
|
5764
|
-
const items = example
|
|
5765
|
-
.map((entry, index) => ({
|
|
5766
|
-
url: String(entry && entry.mediaUrl || ""),
|
|
5767
|
-
kind: getPreviewKind(String(entry && entry.mediaUrl || "")),
|
|
5768
|
-
label: String(entry && entry.note || ("Example " + (exampleIndex + 1) + "." + (index + 1)))
|
|
5769
|
-
}))
|
|
5770
|
-
.filter((entry) => entry.url);
|
|
5771
|
-
if (!items.length) {
|
|
5772
|
-
return;
|
|
5773
|
-
}
|
|
5774
|
-
if (!hasMediaModal) {
|
|
5775
|
-
window.open(items[Math.max(0, Math.min(mediaIndex, items.length - 1))].url, "_blank", "noopener,noreferrer");
|
|
5776
|
-
return;
|
|
5777
|
-
}
|
|
5778
|
-
activeMediaItems = items;
|
|
5779
|
-
activeMediaIndex = Math.max(0, Math.min(mediaIndex, activeMediaItems.length - 1));
|
|
5780
|
-
renderActiveMediaModalItem();
|
|
5781
|
-
mediaModalBackdrop.hidden = false;
|
|
5782
|
-
mediaModalBackdrop.setAttribute("data-open", "true");
|
|
5783
|
-
}
|
|
5784
|
-
|
|
5785
|
-
function stepMediaModal(direction) {
|
|
5786
|
-
if (!activeMediaItems.length) {
|
|
5787
|
-
return;
|
|
5788
|
-
}
|
|
5789
|
-
activeMediaIndex = (activeMediaIndex + direction + activeMediaItems.length) % activeMediaItems.length;
|
|
5790
|
-
renderActiveMediaModalItem();
|
|
5791
|
-
}
|
|
5792
|
-
|
|
5793
|
-
async function copyActiveMediaUrl() {
|
|
5794
|
-
if (!hasMediaModal) {
|
|
5795
|
-
return;
|
|
5796
|
-
}
|
|
5797
|
-
const url = mediaModalCopy.dataset.url || "";
|
|
5798
|
-
if (!url) {
|
|
5799
|
-
return;
|
|
5800
|
-
}
|
|
5801
|
-
try {
|
|
5802
|
-
const copied = await copyText(url);
|
|
5803
|
-
mediaModalCopy.setAttribute("data-state", copied ? "done" : "error");
|
|
5804
|
-
mediaModalCopy.setAttribute("aria-label", copied ? "Media URL copied" : "Unable to copy media URL");
|
|
5805
|
-
mediaModalCopy.setAttribute("title", copied ? "Copied" : "Copy failed");
|
|
5806
|
-
mediaModalCopyLabel.textContent = copied ? "Copied" : "Copy failed";
|
|
5807
|
-
} catch {
|
|
5808
|
-
mediaModalCopy.setAttribute("data-state", "error");
|
|
5809
|
-
mediaModalCopy.setAttribute("aria-label", "Unable to copy media URL");
|
|
5810
|
-
mediaModalCopy.setAttribute("title", "Copy failed");
|
|
5811
|
-
mediaModalCopyLabel.textContent = "Copy failed";
|
|
5812
|
-
}
|
|
5813
|
-
window.setTimeout(() => {
|
|
5814
|
-
mediaModalCopy.removeAttribute("data-state");
|
|
5815
|
-
mediaModalCopy.setAttribute("aria-label", "Copy media URL");
|
|
5816
|
-
mediaModalCopy.setAttribute("title", "Copy link");
|
|
5817
|
-
mediaModalCopyLabel.textContent = "Copy link";
|
|
5818
|
-
}, 1400);
|
|
5819
|
-
}
|
|
5820
|
-
|
|
5821
|
-
function closeMediaModal() {
|
|
5822
|
-
if (!hasMediaModal) {
|
|
5823
|
-
return;
|
|
5824
|
-
}
|
|
5825
|
-
mediaModalBackdrop.setAttribute("data-open", "false");
|
|
5826
|
-
mediaModalBackdrop.hidden = true;
|
|
5827
|
-
mediaModalSource.href = "#";
|
|
5828
|
-
mediaModalDownload.href = "#";
|
|
5829
|
-
mediaModalDownload.setAttribute("download", "");
|
|
5830
|
-
mediaModalCopy.dataset.url = "";
|
|
5831
|
-
mediaModalCopy.removeAttribute("data-state");
|
|
5832
|
-
mediaModalCopy.setAttribute("aria-label", "Copy media URL");
|
|
5833
|
-
mediaModalCopy.setAttribute("title", "Copy link");
|
|
5834
|
-
mediaModalCopyLabel.textContent = "Copy link";
|
|
5835
|
-
mediaModalBody.innerHTML = "";
|
|
5836
|
-
mediaModalCarousel.setAttribute("data-visible", "false");
|
|
5837
|
-
mediaModalInpaint.setAttribute("data-visible", "false");
|
|
5838
|
-
mediaModalInpaint.href = "/inpaint";
|
|
5839
|
-
activeMediaItems = [];
|
|
5840
|
-
activeMediaIndex = 0;
|
|
5841
|
-
}
|
|
5842
|
-
|
|
5843
|
-
function renderPreviewLinks(urls) {
|
|
5844
|
-
if (!Array.isArray(urls) || !urls.length) {
|
|
5845
|
-
return "";
|
|
5846
|
-
}
|
|
5847
|
-
return '<div class="editor-history-card-previews">' + urls.map((url, index) => (
|
|
5848
|
-
(() => {
|
|
5849
|
-
const kind = getPreviewKind(url);
|
|
5850
|
-
const label = 'Output ' + (index + 1);
|
|
5851
|
-
if (kind === "image") {
|
|
5852
|
-
return '<button type="button" class="editor-history-card-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="' + kind + '" data-preview-label="' + escapeHtml(label) + '">' +
|
|
5853
|
-
'<img src="' + escapeHtml(url) + '" alt="' + escapeHtml(label) + '" loading="lazy" decoding="async" />' +
|
|
5854
|
-
'<span class="editor-history-card-preview-label">' + escapeHtml(label) + '</span>' +
|
|
5855
|
-
'</button>';
|
|
5856
|
-
}
|
|
5857
|
-
if (kind === "video") {
|
|
5858
|
-
return '<button type="button" class="editor-history-card-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="' + kind + '" data-preview-label="' + escapeHtml(label) + '">' +
|
|
5859
|
-
'<video src="' + escapeHtml(url) + '" muted playsinline preload="metadata" aria-hidden="true"></video>' +
|
|
5860
|
-
'<span class="editor-history-video-play" aria-hidden="true"></span>' +
|
|
5861
|
-
'<span class="editor-history-card-preview-label">' + escapeHtml(label) + '</span>' +
|
|
5862
|
-
'</button>';
|
|
5863
|
-
}
|
|
5864
|
-
return '<button type="button" class="editor-history-card-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="' + kind + '" data-preview-label="' + escapeHtml(label) + '">' +
|
|
5865
|
-
'<span class="editor-history-card-preview-fallback">' + escapeHtml(label) + '</span>' +
|
|
5866
|
-
'</button>';
|
|
5867
|
-
})()
|
|
5868
|
-
)).join("") + '</div>';
|
|
5869
|
-
}
|
|
5870
|
-
|
|
5871
|
-
function collectGroupPreviewUrls(entries) {
|
|
5872
|
-
const seen = new Set();
|
|
5873
|
-
const urls = [];
|
|
5874
|
-
for (const entry of entries) {
|
|
5875
|
-
const candidates = entry.outputMediaUrls && entry.outputMediaUrls.length ? entry.outputMediaUrls : entry.mediaUrls;
|
|
5876
|
-
if (!Array.isArray(candidates)) {
|
|
5877
|
-
continue;
|
|
5878
|
-
}
|
|
5879
|
-
for (const url of candidates) {
|
|
5880
|
-
if (!url || seen.has(url) || !isPreviewableMediaUrl(url)) {
|
|
5881
|
-
continue;
|
|
5882
|
-
}
|
|
5883
|
-
seen.add(url);
|
|
5884
|
-
urls.push(url);
|
|
5885
|
-
}
|
|
5886
|
-
}
|
|
5887
|
-
return urls;
|
|
5888
|
-
}
|
|
5889
|
-
|
|
5890
|
-
function renderGroupPreviewStrip(entries, groupKey) {
|
|
5891
|
-
const urls = collectGroupPreviewUrls(entries);
|
|
5892
|
-
if (!urls.length) {
|
|
5893
|
-
return "";
|
|
5894
|
-
}
|
|
5895
|
-
const isExpanded = expandedPreviewGroups.has(groupKey);
|
|
5896
|
-
const visibleUrls = isExpanded ? urls : urls.slice(0, 5);
|
|
5897
|
-
const visible = visibleUrls.map((url) => {
|
|
5898
|
-
const kind = getPreviewKind(url);
|
|
5899
|
-
if (kind === "video") {
|
|
5900
|
-
const label = 'Output preview';
|
|
5901
|
-
return '<button type="button" class="editor-history-group-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="video" data-preview-label="' + escapeHtml(label) + '" aria-label="Preview video output">' +
|
|
5902
|
-
'<video src="' + escapeHtml(url) + '" muted playsinline preload="metadata" aria-hidden="true"></video>' +
|
|
5903
|
-
'<span class="editor-history-video-play" aria-hidden="true"></span>' +
|
|
5904
|
-
'</button>';
|
|
5905
|
-
}
|
|
5906
|
-
if (kind === "image") {
|
|
5907
|
-
const label = 'Output preview';
|
|
5908
|
-
return '<button type="button" class="editor-history-group-preview" data-preview-url="' + escapeHtml(url) + '" data-preview-kind="image" data-preview-label="' + escapeHtml(label) + '" aria-label="Preview image output">' +
|
|
5909
|
-
'<img src="' + escapeHtml(url) + '" alt="" loading="lazy" decoding="async" />' +
|
|
5910
|
-
'</button>';
|
|
5911
|
-
}
|
|
5912
|
-
return "";
|
|
5913
|
-
}).join("");
|
|
5914
|
-
const more = !isExpanded && urls.length > visibleUrls.length
|
|
5915
|
-
? '<button type="button" class="editor-history-group-preview-more" data-editor-expand-media="' + escapeHtml(groupKey) + '" aria-label="Show all media previews">+' + escapeHtml(urls.length - visibleUrls.length) + '</button>'
|
|
5916
|
-
: "";
|
|
5917
|
-
return '<div class="editor-history-group-previews' + (isExpanded ? ' is-expanded' : '') + '" aria-label="Generated media previews">' + visible + more + '</div>';
|
|
5918
|
-
}
|
|
5919
|
-
|
|
5920
|
-
function renderSample(title, payload, omittedReason) {
|
|
5921
|
-
return '<section class="editor-history-sample">' +
|
|
5922
|
-
'<h4>' + escapeHtml(title) + '</h4>' +
|
|
5923
|
-
(omittedReason ? '<div class="editor-history-sample-note">' + escapeHtml(omittedReason) + '</div>' : '') +
|
|
5924
|
-
'<pre>' + escapeHtml(formatJson(payload)) + '</pre>' +
|
|
5925
|
-
'</section>';
|
|
5926
|
-
}
|
|
5927
|
-
|
|
5928
|
-
function renderEntry(entry) {
|
|
5929
|
-
return '<article class="editor-history-card">' +
|
|
5930
|
-
renderHttpAction(entry) +
|
|
5931
|
-
'<div class="editor-history-card-foot">' +
|
|
5932
|
-
'<span>' + escapeHtml(new Date(entry.createdAt).toLocaleString()) + '</span>' +
|
|
5933
|
-
'</div>' +
|
|
5934
|
-
renderPreviewLinks(entry.outputMediaUrls && entry.outputMediaUrls.length ? entry.outputMediaUrls : entry.mediaUrls) +
|
|
5935
|
-
'</article>';
|
|
5936
|
-
}
|
|
5937
|
-
|
|
5938
|
-
function hasGeneratedTracerSuffix(tracer) {
|
|
5939
|
-
return /-[a-z0-9]{8}$/i.test(String(tracer || "").trim());
|
|
5940
|
-
}
|
|
5941
|
-
|
|
5942
|
-
function doesTracerMatchSelection(entryTracer, selectedTracer) {
|
|
5943
|
-
const normalizedEntry = String(entryTracer || "").trim();
|
|
5944
|
-
const normalizedSelected = String(selectedTracer || "").trim();
|
|
5945
|
-
if (!normalizedEntry || !normalizedSelected) {
|
|
5946
|
-
return false;
|
|
5947
|
-
}
|
|
5948
|
-
if (normalizedEntry === normalizedSelected) {
|
|
5949
|
-
return true;
|
|
5950
|
-
}
|
|
5951
|
-
return normalizedEntry.startsWith(normalizedSelected + "-") && hasGeneratedTracerSuffix(normalizedEntry);
|
|
5952
|
-
}
|
|
5953
|
-
|
|
5954
|
-
async function copyText(value) {
|
|
5955
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
5956
|
-
try {
|
|
5957
|
-
await navigator.clipboard.writeText(value);
|
|
5958
|
-
return true;
|
|
5959
|
-
} catch {
|
|
5960
|
-
// Fall back to the textarea path when browser permissions block clipboard.writeText.
|
|
5961
|
-
}
|
|
5962
|
-
}
|
|
5963
|
-
const field = document.createElement("textarea");
|
|
5964
|
-
field.value = value;
|
|
5965
|
-
field.setAttribute("readonly", "");
|
|
5966
|
-
field.style.position = "fixed";
|
|
5967
|
-
field.style.opacity = "0";
|
|
5968
|
-
document.body.appendChild(field);
|
|
5969
|
-
field.select();
|
|
5970
|
-
let copied = false;
|
|
5971
|
-
try {
|
|
5972
|
-
copied = document.execCommand("copy");
|
|
5973
|
-
} finally {
|
|
5974
|
-
field.remove();
|
|
5975
|
-
}
|
|
5976
|
-
return copied;
|
|
5977
|
-
}
|
|
5978
|
-
|
|
5979
|
-
const copyFeedbackTimers = new WeakMap();
|
|
5980
|
-
|
|
5981
|
-
function clearCopyFeedback(button) {
|
|
5982
|
-
const previousTimer = copyFeedbackTimers.get(button);
|
|
5983
|
-
if (previousTimer) {
|
|
5984
|
-
window.clearTimeout(previousTimer);
|
|
5985
|
-
}
|
|
5986
|
-
copyFeedbackTimers.delete(button);
|
|
5987
|
-
button.removeAttribute("data-copying");
|
|
5988
|
-
button.removeAttribute("data-copied");
|
|
5989
|
-
button.removeAttribute("data-copy-error");
|
|
5990
|
-
}
|
|
5991
|
-
|
|
5992
|
-
function resetCopyFeedback(button, label) {
|
|
5993
|
-
button.removeAttribute("data-copying");
|
|
5994
|
-
button.removeAttribute("data-copied");
|
|
5995
|
-
button.removeAttribute("data-copy-error");
|
|
5996
|
-
button.setAttribute("aria-label", "Copy " + label);
|
|
5997
|
-
button.setAttribute("title", "Copy " + label);
|
|
5998
|
-
copyFeedbackTimers.delete(button);
|
|
5999
|
-
}
|
|
6000
|
-
|
|
6001
|
-
function showCopyingFeedback(button, label) {
|
|
6002
|
-
clearCopyFeedback(button);
|
|
6003
|
-
button.setAttribute("data-copying", "true");
|
|
6004
|
-
button.setAttribute("aria-label", "Copying " + label);
|
|
6005
|
-
button.setAttribute("title", "Copying");
|
|
6006
|
-
}
|
|
6007
|
-
|
|
6008
|
-
function showCopyFeedback(button, label) {
|
|
6009
|
-
clearCopyFeedback(button);
|
|
6010
|
-
button.setAttribute("data-copied", "true");
|
|
6011
|
-
button.setAttribute("aria-label", "Copied " + label);
|
|
6012
|
-
button.setAttribute("title", "Copied");
|
|
6013
|
-
const timer = window.setTimeout(() => {
|
|
6014
|
-
resetCopyFeedback(button, label);
|
|
6015
|
-
}, 1400);
|
|
6016
|
-
copyFeedbackTimers.set(button, timer);
|
|
6017
|
-
}
|
|
6018
|
-
|
|
6019
|
-
function showCopyErrorFeedback(button, label) {
|
|
6020
|
-
clearCopyFeedback(button);
|
|
6021
|
-
button.setAttribute("data-copy-error", "true");
|
|
6022
|
-
button.setAttribute("aria-label", "Copy failed for " + label);
|
|
6023
|
-
button.setAttribute("title", "Copy failed");
|
|
6024
|
-
const timer = window.setTimeout(() => {
|
|
6025
|
-
resetCopyFeedback(button, label);
|
|
6026
|
-
}, 1800);
|
|
6027
|
-
copyFeedbackTimers.set(button, timer);
|
|
6028
|
-
}
|
|
6029
|
-
|
|
6030
|
-
function renderGroups(entries, tracers) {
|
|
6031
|
-
if (!tracers.length) {
|
|
6032
|
-
renderEmpty("Use Copilot to start or select a tracer, then its grouped POST history will appear here.");
|
|
6033
|
-
return;
|
|
6034
|
-
}
|
|
6035
|
-
|
|
6036
|
-
const grouped = new Map();
|
|
6037
|
-
for (const entry of entries) {
|
|
6038
|
-
const key = tracers.find((tracer) => doesTracerMatchSelection(entry.tracer, tracer)) || entry.tracer || "No tracer";
|
|
6039
|
-
const existing = grouped.get(key) || [];
|
|
6040
|
-
existing.push(entry);
|
|
6041
|
-
grouped.set(key, existing);
|
|
6042
|
-
}
|
|
6043
|
-
|
|
6044
|
-
historyRoot.innerHTML = tracers.map((tracer, index) => {
|
|
6045
|
-
const groupEntries = grouped.get(tracer) || [];
|
|
6046
|
-
const activeRefresh = index === 0
|
|
6047
|
-
? '<button type="button" class="editor-history-button editor-history-refresh" data-editor-history-refresh="' + escapeHtml(tracer) + '" aria-label="Refresh tracer history" title="Refresh tracer history">' + renderRefreshIcon() + '</button>'
|
|
6048
|
-
: '';
|
|
6049
|
-
const menu = '<span class="editor-history-menu-wrap" data-editor-history-menu-wrap>' +
|
|
6050
|
-
'<button type="button" class="editor-history-button editor-history-menu-button" data-editor-history-menu-toggle="' + escapeHtml(tracer) + '" aria-label="Tracer actions" title="Tracer actions">...</button>' +
|
|
6051
|
-
'<span class="editor-history-menu" role="menu">' +
|
|
6052
|
-
'<button type="button" data-editor-history-copy="' + escapeHtml(tracer) + '">Copy Tracer</button>' +
|
|
6053
|
-
'<button type="button" data-editor-history-rename="' + escapeHtml(tracer) + '">Rename tracer</button>' +
|
|
6054
|
-
'<button type="button" data-editor-history-remove="' + escapeHtml(tracer) + '">Remove from view</button>' +
|
|
6055
|
-
'<button type="button" data-editor-history-archive="' + escapeHtml(tracer) + '" data-danger="true">Archive tracer</button>' +
|
|
6056
|
-
'</span>' +
|
|
6057
|
-
'</span>';
|
|
6058
|
-
const callsLabel = groupEntries.length
|
|
6059
|
-
? escapeHtml(groupEntries.length) + ' POST call' + (groupEntries.length === 1 ? '' : 's')
|
|
6060
|
-
: 'No POST calls yet';
|
|
6061
|
-
return (
|
|
6062
|
-
'<details class="editor-history-group"' + (index === 0 ? ' open' : '') + '>' +
|
|
6063
|
-
'<summary>' +
|
|
6064
|
-
'<div class="editor-history-group-main">' +
|
|
6065
|
-
'<h3>' + escapeHtml(tracer) + '</h3>' +
|
|
6066
|
-
'<p>' + callsLabel + '</p>' +
|
|
6067
|
-
renderGroupPreviewStrip(groupEntries, tracer) +
|
|
6068
|
-
'</div>' +
|
|
6069
|
-
'<div class="editor-history-group-actions">' +
|
|
6070
|
-
activeRefresh +
|
|
6071
|
-
menu +
|
|
6072
|
-
'<span class="editor-history-group-toggle">Toggle</span>' +
|
|
6073
|
-
'</div>' +
|
|
6074
|
-
'</summary>' +
|
|
6075
|
-
'<div class="editor-history-group-body">' +
|
|
6076
|
-
(groupEntries.length
|
|
6077
|
-
? groupEntries.map(renderEntry).join("")
|
|
6078
|
-
: '<div class="editor-history-empty">No POST history exists for this tracer yet.</div>') +
|
|
6079
|
-
'</div>' +
|
|
6080
|
-
'</details>'
|
|
6081
|
-
)}).join("");
|
|
6082
|
-
}
|
|
6083
|
-
|
|
6084
|
-
async function fetchHistoryPayload(endpoint) {
|
|
6085
|
-
const response = await fetch(endpoint.toString(), { credentials: "same-origin" });
|
|
6086
|
-
let payload = null;
|
|
6087
|
-
try {
|
|
6088
|
-
payload = await response.json();
|
|
6089
|
-
} catch {
|
|
6090
|
-
payload = null;
|
|
6091
|
-
}
|
|
6092
|
-
if (!response.ok) {
|
|
6093
|
-
const message = payload && typeof payload.error === "string"
|
|
6094
|
-
? payload.error
|
|
6095
|
-
: "Unable to load POST history.";
|
|
6096
|
-
throw new Error(message + " (" + response.status + ")");
|
|
6097
|
-
}
|
|
6098
|
-
return payload || {};
|
|
6099
|
-
}
|
|
6100
|
-
|
|
6101
|
-
async function loadFallbackHistory(selectedTracers) {
|
|
6102
|
-
if (!boot.fallbackHistoryEndpoint) {
|
|
6103
|
-
throw new Error("No fallback history endpoint is configured.");
|
|
6104
|
-
}
|
|
6105
|
-
const byId = new Map();
|
|
6106
|
-
for (const tracer of selectedTracers) {
|
|
6107
|
-
const endpoint = new URL(boot.fallbackHistoryEndpoint, window.location.origin);
|
|
6108
|
-
endpoint.searchParams.set("tracer", tracer);
|
|
6109
|
-
const payload = await fetchHistoryPayload(endpoint);
|
|
6110
|
-
for (const entry of Array.isArray(payload.entries) ? payload.entries : []) {
|
|
6111
|
-
byId.set(entry.id, entry);
|
|
6112
|
-
}
|
|
6113
|
-
}
|
|
6114
|
-
return { entries: [...byId.values()] };
|
|
6115
|
-
}
|
|
6116
|
-
|
|
6117
|
-
async function loadHistory(options) {
|
|
6118
|
-
const showLoading = Boolean(options && options.showLoading);
|
|
6119
|
-
const triggerButton = options && options.triggerButton instanceof HTMLButtonElement ? options.triggerButton : null;
|
|
6120
|
-
const selectedTracers = tracerStore.get();
|
|
6121
|
-
renderTracerChips(selectedTracers);
|
|
6122
|
-
if (!selectedTracers.length) {
|
|
6123
|
-
renderEmpty("No tracer selected yet.");
|
|
6124
|
-
return;
|
|
6125
|
-
}
|
|
6126
|
-
if (!boot.isLoggedIn) {
|
|
6127
|
-
renderEmpty("Sign in to load tracer-scoped POST history.");
|
|
6128
|
-
return;
|
|
6129
|
-
}
|
|
6130
|
-
|
|
6131
|
-
const endpoint = new URL(boot.historyEndpoint, window.location.origin);
|
|
6132
|
-
if (selectedTracers.length) {
|
|
6133
|
-
endpoint.searchParams.set("tracers", selectedTracers.join(","));
|
|
6134
|
-
} else {
|
|
6135
|
-
endpoint.searchParams.delete("tracers");
|
|
6136
|
-
}
|
|
6137
|
-
|
|
6138
|
-
if (triggerButton) {
|
|
6139
|
-
triggerButton.disabled = true;
|
|
6140
|
-
triggerButton.setAttribute("data-loading", "true");
|
|
6141
|
-
} else if (showLoading) {
|
|
6142
|
-
historyRoot.innerHTML = '<div class="editor-history-empty">Loading POST history…</div>';
|
|
6143
|
-
}
|
|
6144
|
-
try {
|
|
6145
|
-
let payload;
|
|
6146
|
-
try {
|
|
6147
|
-
payload = await fetchHistoryPayload(endpoint);
|
|
6148
|
-
} catch (primaryError) {
|
|
6149
|
-
console.warn("Editor-scoped history failed; trying job-runs history fallback.", primaryError);
|
|
6150
|
-
payload = await loadFallbackHistory(selectedTracers);
|
|
6151
|
-
}
|
|
6152
|
-
const entries = Array.isArray(payload.entries) ? payload.entries : [];
|
|
6153
|
-
lastHistoryEntries = entries;
|
|
6154
|
-
lastSelectedTracers = selectedTracers;
|
|
6155
|
-
renderGroups(entries, selectedTracers);
|
|
6156
|
-
} catch (error) {
|
|
6157
|
-
console.error(error);
|
|
6158
|
-
renderEmpty("Unable to load tracer-scoped POST history" + (error instanceof Error && error.message ? ": " + error.message : "."));
|
|
6159
|
-
} finally {
|
|
6160
|
-
if (triggerButton) {
|
|
6161
|
-
triggerButton.disabled = false;
|
|
6162
|
-
triggerButton.setAttribute("data-loading", "false");
|
|
6163
|
-
}
|
|
6164
|
-
}
|
|
6165
|
-
}
|
|
6166
|
-
|
|
6167
|
-
async function renameStoredTracer(fromTracer, toTracer) {
|
|
6168
|
-
const normalizedFrom = String(fromTracer || "").trim();
|
|
6169
|
-
const normalizedTo = String(toTracer || "").trim();
|
|
6170
|
-
if (!normalizedFrom || !normalizedTo || normalizedFrom === normalizedTo) {
|
|
6171
|
-
return;
|
|
6172
|
-
}
|
|
6173
|
-
const response = await fetch(boot.tracerEndpoint, {
|
|
6174
|
-
method: "PATCH",
|
|
6175
|
-
credentials: "same-origin",
|
|
6176
|
-
headers: { "content-type": "application/json" },
|
|
6177
|
-
body: JSON.stringify({
|
|
6178
|
-
from_tracer: normalizedFrom,
|
|
6179
|
-
to_tracer: normalizedTo
|
|
6180
|
-
})
|
|
6181
|
-
});
|
|
6182
|
-
if (!response.ok) {
|
|
6183
|
-
throw new Error("Unable to update tracer.");
|
|
6184
|
-
}
|
|
6185
|
-
const nextTracers = tracerStore.get().map((entry) => entry === normalizedFrom ? normalizedTo : entry);
|
|
6186
|
-
tracerStore.set(nextTracers);
|
|
6187
|
-
await loadHistory({ showLoading: true });
|
|
6188
|
-
}
|
|
6189
|
-
|
|
6190
|
-
loadHistory({ showLoading: true });
|
|
6191
|
-
window.addEventListener("popstate", () => loadHistory({ showLoading: true }));
|
|
6192
|
-
window.addEventListener("vidfarm:editor-tracers-changed", () => loadHistory());
|
|
6193
|
-
if (hasTracerModal) {
|
|
6194
|
-
addTracerButton.addEventListener("click", () => openTracerModal());
|
|
6195
|
-
modalSubmit.addEventListener("click", () => submitTracerModal());
|
|
6196
|
-
modalCancel.addEventListener("click", () => closeTracerModal());
|
|
6197
|
-
modalInput.addEventListener("keydown", (event) => {
|
|
6198
|
-
if (event.key === "Enter") {
|
|
6199
|
-
event.preventDefault();
|
|
6200
|
-
submitTracerModal();
|
|
6201
|
-
return;
|
|
6202
|
-
}
|
|
6203
|
-
if (event.key === "Escape") {
|
|
6204
|
-
event.preventDefault();
|
|
6205
|
-
closeTracerModal();
|
|
6206
|
-
}
|
|
6207
|
-
});
|
|
6208
|
-
modalBackdrop.addEventListener("click", (event) => {
|
|
6209
|
-
if (event.target === modalBackdrop) {
|
|
6210
|
-
closeTracerModal();
|
|
6211
|
-
}
|
|
6212
|
-
});
|
|
6213
|
-
}
|
|
6214
|
-
if (hasMediaModal) {
|
|
6215
|
-
mediaModalClose.addEventListener("click", () => closeMediaModal());
|
|
6216
|
-
mediaModalCopy.addEventListener("click", () => {
|
|
6217
|
-
void copyActiveMediaUrl();
|
|
6218
|
-
});
|
|
6219
|
-
mediaModalInpaint.addEventListener("click", (event) => {
|
|
6220
|
-
const item = activeMediaItems[activeMediaIndex] || null;
|
|
6221
|
-
if (!item || (item.kind || getPreviewKind(item.url)) !== "image") {
|
|
6222
|
-
event.preventDefault();
|
|
6223
|
-
return;
|
|
6224
|
-
}
|
|
6225
|
-
const href = buildInpaintPageUrlForMedia(item, { ensureTracer: true });
|
|
6226
|
-
if (!href) {
|
|
6227
|
-
event.preventDefault();
|
|
6228
|
-
return;
|
|
6229
|
-
}
|
|
6230
|
-
mediaModalInpaint.href = href;
|
|
6231
|
-
});
|
|
6232
|
-
mediaModalPrev.addEventListener("click", () => stepMediaModal(-1));
|
|
6233
|
-
mediaModalNext.addEventListener("click", () => stepMediaModal(1));
|
|
6234
|
-
mediaModalBackdrop.addEventListener("click", (event) => {
|
|
6235
|
-
if (event.target === mediaModalBackdrop) {
|
|
6236
|
-
closeMediaModal();
|
|
6237
|
-
}
|
|
6238
|
-
});
|
|
6239
|
-
}
|
|
6240
|
-
if (hasInpaintModal) {
|
|
6241
|
-
void loadInpaintProviders();
|
|
6242
|
-
inpaintClose.addEventListener("click", () => closeInpaintModal());
|
|
6243
|
-
inpaintUndo.addEventListener("click", () => {
|
|
6244
|
-
inpaintStrokes.pop();
|
|
6245
|
-
redrawInpaintMask();
|
|
6246
|
-
setInpaintStatus("", "");
|
|
6247
|
-
});
|
|
6248
|
-
inpaintClear.addEventListener("click", () => {
|
|
6249
|
-
inpaintStrokes = [];
|
|
6250
|
-
redrawInpaintMask();
|
|
6251
|
-
setInpaintStatus("", "");
|
|
6252
|
-
});
|
|
6253
|
-
inpaintBrush.addEventListener("input", () => {
|
|
6254
|
-
inpaintBrushLabel.textContent = inpaintBrush.value;
|
|
6255
|
-
});
|
|
6256
|
-
inpaintColorPicker.addEventListener("input", () => {
|
|
6257
|
-
activeInpaintColor = normalizeHexColor(inpaintColorPicker.value);
|
|
6258
|
-
renderInpaintControls();
|
|
6259
|
-
});
|
|
6260
|
-
inpaintAddColor.addEventListener("click", () => addInpaintColor(inpaintColorPicker.value));
|
|
6261
|
-
inpaintAttachmentInput.addEventListener("change", () => {
|
|
6262
|
-
void addAttachmentFiles(inpaintAttachmentInput.files);
|
|
6263
|
-
inpaintAttachmentInput.value = "";
|
|
6264
|
-
});
|
|
6265
|
-
inpaintGlobalAttachments.addEventListener("click", (event) => {
|
|
6266
|
-
const target = event.target;
|
|
6267
|
-
if (!(target instanceof Element)) return;
|
|
6268
|
-
const remove = target.closest("[data-editor-inpaint-remove-attachment]");
|
|
6269
|
-
if (remove instanceof HTMLElement) {
|
|
6270
|
-
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.id !== remove.dataset.editorInpaintRemoveAttachment);
|
|
6271
|
-
renderAllAttachmentChips();
|
|
6272
|
-
}
|
|
6273
|
-
});
|
|
6274
|
-
document.querySelector('[data-editor-inpaint-attach-scope="global"]')?.addEventListener("click", () => {
|
|
6275
|
-
openAttachmentPicker("global");
|
|
6276
|
-
});
|
|
6277
|
-
inpaintGlobalPrompt.addEventListener("paste", (event) => {
|
|
6278
|
-
handleAttachmentPaste(event, "global");
|
|
6279
|
-
});
|
|
6280
|
-
inpaintPrompts.addEventListener("click", (event) => {
|
|
6281
|
-
const target = event.target;
|
|
6282
|
-
if (!(target instanceof Element)) {
|
|
6283
|
-
return;
|
|
6284
|
-
}
|
|
6285
|
-
const removeColor = target.closest("[data-editor-inpaint-remove-color]");
|
|
6286
|
-
if (removeColor instanceof HTMLElement && removeColor.dataset.editorInpaintRemoveColor) {
|
|
6287
|
-
removeInpaintColor(removeColor.dataset.editorInpaintRemoveColor);
|
|
6288
|
-
return;
|
|
6289
|
-
}
|
|
6290
|
-
const remove = target.closest("[data-editor-inpaint-remove-attachment]");
|
|
6291
|
-
if (remove instanceof HTMLElement) {
|
|
6292
|
-
inpaintAttachments = inpaintAttachments.filter((attachment) => attachment.id !== remove.dataset.editorInpaintRemoveAttachment);
|
|
6293
|
-
renderAllAttachmentChips();
|
|
6294
|
-
return;
|
|
6295
|
-
}
|
|
6296
|
-
const attach = target.closest("[data-editor-inpaint-attach-scope]");
|
|
6297
|
-
if (attach instanceof HTMLElement && attach.dataset.editorInpaintAttachScope === "color" && attach.dataset.color) {
|
|
6298
|
-
openAttachmentPicker("color", attach.dataset.color);
|
|
6299
|
-
return;
|
|
6300
|
-
}
|
|
6301
|
-
const row = target.closest(".editor-inpaint-color-row");
|
|
6302
|
-
if (row instanceof HTMLElement && row.dataset.color) {
|
|
6303
|
-
selectInpaintColor(row.dataset.color);
|
|
6304
|
-
}
|
|
6305
|
-
});
|
|
6306
|
-
inpaintPrompts.addEventListener("focusin", (event) => {
|
|
6307
|
-
const target = event.target;
|
|
6308
|
-
if (!(target instanceof Element)) {
|
|
6309
|
-
return;
|
|
6310
|
-
}
|
|
6311
|
-
const row = target.closest(".editor-inpaint-color-row");
|
|
6312
|
-
if (row instanceof HTMLElement && row.dataset.color) {
|
|
6313
|
-
selectInpaintColor(row.dataset.color);
|
|
6314
|
-
}
|
|
6315
|
-
});
|
|
6316
|
-
inpaintPrompts.addEventListener("input", (event) => {
|
|
6317
|
-
const target = event.target;
|
|
6318
|
-
if (target instanceof HTMLTextAreaElement) {
|
|
6319
|
-
updateInpaintPrompt(target.dataset.color || "", target.value);
|
|
6320
|
-
}
|
|
6321
|
-
});
|
|
6322
|
-
inpaintPrompts.addEventListener("paste", (event) => {
|
|
6323
|
-
const target = event.target;
|
|
6324
|
-
if (target instanceof HTMLTextAreaElement && target.dataset.color) {
|
|
6325
|
-
handleAttachmentPaste(event, "color", target.dataset.color);
|
|
6326
|
-
}
|
|
6327
|
-
});
|
|
6328
|
-
inpaintCanvas.addEventListener("pointerdown", (event) => beginInpaintStroke(event));
|
|
6329
|
-
inpaintCanvas.addEventListener("pointermove", (event) => continueInpaintStroke(event));
|
|
6330
|
-
inpaintCanvas.addEventListener("pointerup", (event) => endInpaintStroke(event));
|
|
6331
|
-
inpaintCanvas.addEventListener("pointercancel", (event) => endInpaintStroke(event));
|
|
6332
|
-
inpaintCanvas.addEventListener("pointerleave", (event) => endInpaintStroke(event));
|
|
6333
|
-
inpaintSubmit.addEventListener("click", () => {
|
|
6334
|
-
void submitInpaintJob();
|
|
6335
|
-
});
|
|
6336
|
-
inpaintModalBackdrop.addEventListener("click", (event) => {
|
|
6337
|
-
if (event.target === inpaintModalBackdrop) {
|
|
6338
|
-
closeInpaintModal();
|
|
6339
|
-
}
|
|
6340
|
-
});
|
|
6341
|
-
window.addEventListener("resize", () => syncInpaintCanvasSize());
|
|
6342
|
-
}
|
|
6343
|
-
if (hasHttpModal) {
|
|
6344
|
-
httpModalClose.addEventListener("click", () => closeHttpModal());
|
|
6345
|
-
httpModalBackdrop.addEventListener("click", (event) => {
|
|
6346
|
-
if (event.target === httpModalBackdrop) {
|
|
6347
|
-
closeHttpModal();
|
|
6348
|
-
}
|
|
6349
|
-
});
|
|
6350
|
-
}
|
|
6351
|
-
window.addEventListener("keydown", (event) => {
|
|
6352
|
-
if (event.key === "Escape") {
|
|
6353
|
-
if (hasInpaintModal && inpaintModalBackdrop.getAttribute("data-open") === "true") {
|
|
6354
|
-
closeInpaintModal();
|
|
6355
|
-
return;
|
|
6356
|
-
}
|
|
6357
|
-
if (hasHttpModal && httpModalBackdrop.hidden === false) {
|
|
6358
|
-
closeHttpModal();
|
|
6359
|
-
return;
|
|
6360
|
-
}
|
|
6361
|
-
if (hasMediaModal && mediaModalBackdrop.getAttribute("data-open") === "true") {
|
|
6362
|
-
closeMediaModal();
|
|
6363
|
-
return;
|
|
6364
|
-
}
|
|
6365
|
-
if (hasTracerModal && modalBackdrop.getAttribute("data-open") === "true") {
|
|
6366
|
-
closeTracerModal();
|
|
6367
|
-
}
|
|
6368
|
-
return;
|
|
6369
|
-
}
|
|
6370
|
-
if (hasMediaModal && mediaModalBackdrop.getAttribute("data-open") === "true" && activeMediaItems.length > 1) {
|
|
6371
|
-
if (event.key === "ArrowLeft") {
|
|
6372
|
-
event.preventDefault();
|
|
6373
|
-
stepMediaModal(-1);
|
|
6374
|
-
return;
|
|
6375
|
-
}
|
|
6376
|
-
if (event.key === "ArrowRight") {
|
|
6377
|
-
event.preventDefault();
|
|
6378
|
-
stepMediaModal(1);
|
|
6379
|
-
}
|
|
6380
|
-
}
|
|
6381
|
-
});
|
|
6382
|
-
historyRoot.addEventListener("click", (event) => {
|
|
6383
|
-
const target = event.target;
|
|
6384
|
-
if (!(target instanceof Element)) {
|
|
6385
|
-
return;
|
|
6386
|
-
}
|
|
6387
|
-
const previewButton = target.closest("[data-preview-url]");
|
|
6388
|
-
if (previewButton instanceof HTMLButtonElement) {
|
|
6389
|
-
event.preventDefault();
|
|
6390
|
-
event.stopPropagation();
|
|
6391
|
-
const url = previewButton.getAttribute("data-preview-url") || "";
|
|
6392
|
-
if (url) {
|
|
6393
|
-
openMediaGroupFromButton(previewButton);
|
|
6394
|
-
}
|
|
6395
|
-
return;
|
|
6396
|
-
}
|
|
6397
|
-
const expandMediaButton = target.closest("[data-editor-expand-media]");
|
|
6398
|
-
if (expandMediaButton instanceof HTMLButtonElement) {
|
|
6399
|
-
event.preventDefault();
|
|
6400
|
-
event.stopPropagation();
|
|
6401
|
-
const key = expandMediaButton.getAttribute("data-editor-expand-media") || "";
|
|
6402
|
-
if (key) {
|
|
6403
|
-
expandedPreviewGroups.add(key);
|
|
6404
|
-
renderGroups(lastHistoryEntries, lastSelectedTracers);
|
|
6405
|
-
}
|
|
6406
|
-
return;
|
|
6407
|
-
}
|
|
6408
|
-
const httpDetailButton = target.closest("[data-editor-http-detail]");
|
|
6409
|
-
if (httpDetailButton instanceof HTMLButtonElement) {
|
|
6410
|
-
event.preventDefault();
|
|
6411
|
-
event.stopPropagation();
|
|
6412
|
-
try {
|
|
6413
|
-
openHttpModal(JSON.parse(decodeURIComponent(httpDetailButton.getAttribute("data-editor-http-detail") || "")));
|
|
6414
|
-
} catch (error) {
|
|
6415
|
-
console.error(error);
|
|
6416
|
-
}
|
|
6417
|
-
return;
|
|
6418
|
-
}
|
|
6419
|
-
const refreshButton = target.closest("[data-editor-history-refresh]");
|
|
6420
|
-
if (refreshButton instanceof HTMLButtonElement) {
|
|
6421
|
-
event.preventDefault();
|
|
6422
|
-
event.stopPropagation();
|
|
6423
|
-
void loadHistory({ triggerButton: refreshButton });
|
|
6424
|
-
return;
|
|
6425
|
-
}
|
|
6426
|
-
const menuToggle = target.closest("[data-editor-history-menu-toggle]");
|
|
6427
|
-
if (menuToggle instanceof HTMLButtonElement) {
|
|
6428
|
-
event.preventDefault();
|
|
6429
|
-
event.stopPropagation();
|
|
6430
|
-
const wrap = menuToggle.closest("[data-editor-history-menu-wrap]");
|
|
6431
|
-
const isOpen = wrap instanceof HTMLElement && wrap.getAttribute("data-open") === "true";
|
|
6432
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
6433
|
-
if (wrap instanceof HTMLElement && !isOpen) {
|
|
6434
|
-
wrap.setAttribute("data-open", "true");
|
|
6435
|
-
}
|
|
6436
|
-
return;
|
|
6437
|
-
}
|
|
6438
|
-
const copyButton = target.closest("[data-editor-history-copy]");
|
|
6439
|
-
if (copyButton instanceof HTMLButtonElement) {
|
|
6440
|
-
event.preventDefault();
|
|
6441
|
-
event.stopPropagation();
|
|
6442
|
-
const tracer = copyButton.getAttribute("data-editor-history-copy") || "";
|
|
6443
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
6444
|
-
if (!tracer) {
|
|
6445
|
-
return;
|
|
6446
|
-
}
|
|
6447
|
-
void copyText(tracer).then((copied) => {
|
|
6448
|
-
if (!copied) {
|
|
6449
|
-
window.prompt("Copy tracer", tracer);
|
|
6450
|
-
}
|
|
6451
|
-
}).catch((error) => {
|
|
6452
|
-
console.error(error);
|
|
6453
|
-
window.prompt("Copy tracer", tracer);
|
|
6454
|
-
});
|
|
6455
|
-
return;
|
|
6456
|
-
}
|
|
6457
|
-
const renameButton = target.closest("[data-editor-history-rename]");
|
|
6458
|
-
if (renameButton instanceof HTMLButtonElement) {
|
|
6459
|
-
event.preventDefault();
|
|
6460
|
-
event.stopPropagation();
|
|
6461
|
-
const tracer = renameButton.getAttribute("data-editor-history-rename") || "";
|
|
6462
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
6463
|
-
const nextTracer = window.prompt("Rename tracer", tracer);
|
|
6464
|
-
if (nextTracer === null || !nextTracer.trim() || nextTracer.trim() === tracer) {
|
|
6465
|
-
return;
|
|
6466
|
-
}
|
|
6467
|
-
void renameStoredTracer(tracer, nextTracer.trim()).catch((error) => {
|
|
6468
|
-
console.error(error);
|
|
6469
|
-
window.alert("Unable to rename tracer.");
|
|
6470
|
-
});
|
|
6471
|
-
return;
|
|
6472
|
-
}
|
|
6473
|
-
const archiveButton = target.closest("[data-editor-history-archive]");
|
|
6474
|
-
if (archiveButton instanceof HTMLButtonElement) {
|
|
6475
|
-
event.preventDefault();
|
|
6476
|
-
event.stopPropagation();
|
|
6477
|
-
const tracer = archiveButton.getAttribute("data-editor-history-archive") || "";
|
|
6478
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
6479
|
-
if (!tracer || tracer.startsWith("archived_")) {
|
|
6480
|
-
return;
|
|
6481
|
-
}
|
|
6482
|
-
const confirmed = window.confirm('Archive "' + tracer + '"? This renames its POST logs and jobs to "archived_' + tracer + '".');
|
|
6483
|
-
if (confirmed) {
|
|
6484
|
-
void renameStoredTracer(tracer, "archived_" + tracer).catch((error) => {
|
|
6485
|
-
console.error(error);
|
|
6486
|
-
window.alert("Unable to archive tracer.");
|
|
6487
|
-
});
|
|
6488
|
-
}
|
|
6489
|
-
return;
|
|
6490
|
-
}
|
|
6491
|
-
const removeButton = target.closest("[data-editor-history-remove]");
|
|
6492
|
-
if (removeButton instanceof HTMLButtonElement) {
|
|
6493
|
-
event.preventDefault();
|
|
6494
|
-
event.stopPropagation();
|
|
6495
|
-
const tracer = removeButton.getAttribute("data-editor-history-remove") || "";
|
|
6496
|
-
document.querySelectorAll("[data-editor-history-menu-wrap]").forEach((entry) => entry.removeAttribute("data-open"));
|
|
6497
|
-
if (!tracer) {
|
|
6498
|
-
return;
|
|
6499
|
-
}
|
|
6500
|
-
const confirmed = window.confirm('Remove "' + tracer + '" from this page view? This does not delete its runs. You can still find them in Render Runs.');
|
|
6501
|
-
if (confirmed) {
|
|
6502
|
-
tracerStore.remove(tracer);
|
|
6503
|
-
}
|
|
6504
|
-
}
|
|
6505
|
-
});
|
|
6506
|
-
document.addEventListener("click", (event) => {
|
|
6507
|
-
const target = event.target;
|
|
6508
|
-
if (!(target instanceof Element)) {
|
|
6509
|
-
return;
|
|
6510
|
-
}
|
|
6511
|
-
const exampleButton = target.closest("[data-editor-example-index]");
|
|
6512
|
-
if (exampleButton instanceof HTMLButtonElement) {
|
|
6513
|
-
event.preventDefault();
|
|
6514
|
-
const exampleIndex = Number(exampleButton.getAttribute("data-editor-example-index") || "0");
|
|
6515
|
-
const mediaIndex = Number(exampleButton.getAttribute("data-editor-example-media-index") || "0");
|
|
6516
|
-
openExampleModal(Number.isFinite(exampleIndex) ? exampleIndex : 0, Number.isFinite(mediaIndex) ? mediaIndex : 0);
|
|
6517
|
-
return;
|
|
6518
|
-
}
|
|
6519
|
-
const copyButton = target.closest("[data-editor-copy-content]");
|
|
6520
|
-
if (!(copyButton instanceof HTMLButtonElement)) {
|
|
6521
|
-
return;
|
|
6522
|
-
}
|
|
6523
|
-
event.preventDefault();
|
|
6524
|
-
const value = copyButton.getAttribute("data-editor-copy-content") || "";
|
|
6525
|
-
const label = copyButton.getAttribute("data-editor-copy-label") || "Copied text";
|
|
6526
|
-
if (!value) {
|
|
6527
|
-
return;
|
|
6528
|
-
}
|
|
6529
|
-
showCopyingFeedback(copyButton, label);
|
|
6530
|
-
void copyText(value).then((copied) => {
|
|
6531
|
-
if (!copied) {
|
|
6532
|
-
showCopyErrorFeedback(copyButton, label);
|
|
6533
|
-
window.prompt(label, value);
|
|
6534
|
-
return;
|
|
6535
|
-
}
|
|
6536
|
-
showCopyFeedback(copyButton, label);
|
|
6537
|
-
}).catch((error) => {
|
|
6538
|
-
console.error(error);
|
|
6539
|
-
showCopyErrorFeedback(copyButton, label);
|
|
6540
|
-
window.prompt(label, value);
|
|
6541
|
-
});
|
|
6542
|
-
});
|
|
6543
|
-
})();
|
|
6544
|
-
</script>
|
|
6545
|
-
`
|
|
6546
|
-
});
|
|
6547
|
-
}
|
|
6548
|
-
function renderPreview(url, alt) {
|
|
6549
|
-
if (/\.(mp4|webm|ogg|mov)(\?|#|$)/i.test(url)) {
|
|
6550
|
-
return `<video class="editor-preview" src="${escapeAttribute(url)}" controls playsinline preload="metadata" aria-label="${escapeAttribute(alt)}"></video>`;
|
|
6551
|
-
}
|
|
6552
|
-
return `<img class="editor-preview" src="${escapeAttribute(url)}" alt="${escapeAttribute(alt)}" loading="lazy" decoding="async" />`;
|
|
6553
|
-
}
|
|
6554
|
-
function formatDifficulty(difficulty) {
|
|
6555
|
-
return difficulty.charAt(0).toUpperCase() + difficulty.slice(1);
|
|
6556
|
-
}
|
|
6557
|
-
function renderTemplateSound(sound) {
|
|
6558
|
-
const title = sound.sound_title.trim();
|
|
6559
|
-
const url = sound.sound_url.trim();
|
|
6560
|
-
if (!title && !url) {
|
|
6561
|
-
return "";
|
|
6562
|
-
}
|
|
6563
|
-
const displayTitle = title || url;
|
|
6564
|
-
const value = url
|
|
6565
|
-
? `<a class="editor-sound-title" href="${escapeAttribute(url)}" target="_blank" rel="noopener noreferrer">${escapeHtml(displayTitle)}</a>`
|
|
6566
|
-
: `<span class="editor-sound-title">${escapeHtml(displayTitle)}</span>`;
|
|
6567
|
-
return `
|
|
6568
|
-
<div class="editor-sound">
|
|
6569
|
-
<span class="editor-sound-label">Sound</span>
|
|
6570
|
-
${value}
|
|
6571
|
-
</div>
|
|
6572
|
-
`;
|
|
6573
|
-
}
|
|
6574
|
-
function renderExamples(examples) {
|
|
6575
|
-
const cards = examples.flatMap((example, exampleIndex) => (example.map((entry, mediaIndex) => renderExampleCard(entry, exampleIndex, mediaIndex))));
|
|
6576
|
-
if (!cards.length) {
|
|
6577
|
-
return `<div class="editor-example-empty">No reference cuts are configured for this composition yet.</div>`;
|
|
6578
|
-
}
|
|
6579
|
-
return `<div class="editor-examples-row" aria-label="Composition reference cuts">${cards.join("")}</div>`;
|
|
6580
|
-
}
|
|
6581
|
-
function renderExampleCard(entry, exampleIndex, mediaIndex) {
|
|
6582
|
-
const url = entry.mediaUrl;
|
|
6583
|
-
const note = entry.note.trim() || `Example ${exampleIndex + 1}`;
|
|
6584
|
-
const media = /\.(mp4|webm|ogg|mov|m4v)(\?|#|$)/i.test(url)
|
|
6585
|
-
? `<video src="${escapeAttribute(url)}" muted playsinline preload="metadata" aria-hidden="true"></video>`
|
|
6586
|
-
: `<img src="${escapeAttribute(url)}" alt="${escapeAttribute(note)}" loading="lazy" decoding="async" />`;
|
|
6587
|
-
return `
|
|
6588
|
-
<article class="editor-example-card">
|
|
6589
|
-
<button
|
|
6590
|
-
type="button"
|
|
6591
|
-
class="editor-example-media"
|
|
6592
|
-
data-editor-example-index="${exampleIndex}"
|
|
6593
|
-
data-editor-example-media-index="${mediaIndex}"
|
|
6594
|
-
aria-label="Open ${escapeAttribute(note)}"
|
|
6595
|
-
>${media}</button>
|
|
6596
|
-
<p class="editor-example-note">${escapeHtml(note)}</p>
|
|
6597
|
-
</article>
|
|
6598
|
-
`;
|
|
6599
|
-
}
|
|
6600
|
-
function renderQuickstartTextarea(label, value, rows) {
|
|
6601
|
-
const text = value.trim() || "Not configured for this composition yet.";
|
|
6602
|
-
return `
|
|
6603
|
-
<div class="editor-quickstart-field">
|
|
6604
|
-
<div class="editor-quickstart-field-head">
|
|
6605
|
-
<span>${escapeHtml(label)}</span>
|
|
6606
|
-
<button
|
|
6607
|
-
type="button"
|
|
6608
|
-
class="editor-quickstart-copy"
|
|
6609
|
-
data-editor-copy-content="${escapeAttribute(text)}"
|
|
6610
|
-
data-editor-copy-label="${escapeAttribute(label)}"
|
|
6611
|
-
aria-label="Copy ${escapeAttribute(label)}"
|
|
6612
|
-
title="Copy ${escapeAttribute(label)}"
|
|
6613
|
-
>
|
|
6614
|
-
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
|
6615
|
-
<rect x="9" y="9" width="10" height="10" rx="2" fill="none" stroke="currentColor" stroke-width="2"></rect>
|
|
6616
|
-
<path d="M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
|
|
6617
|
-
</svg>
|
|
6618
|
-
</button>
|
|
6619
|
-
</div>
|
|
6620
|
-
<textarea class="editor-quickstart-textarea" readonly rows="${rows}" spellcheck="false" aria-label="${escapeAttribute(label)}">${escapeHtml(text)}</textarea>
|
|
6621
|
-
</div>
|
|
6622
|
-
`;
|
|
6623
|
-
}
|
|
6624
2491
|
//# sourceMappingURL=template-editor-pages.js.map
|