@pixerate/editor-svelte 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/StylesGallery.svelte +169 -0
- package/dist/StylesGallery.svelte.d.ts +27 -0
- package/dist/StylesGallery.svelte.d.ts.map +1 -0
- package/dist/canvas/TokenBindingPicker.svelte +93 -0
- package/dist/canvas/TokenBindingPicker.svelte.d.ts +20 -0
- package/dist/canvas/TokenBindingPicker.svelte.d.ts.map +1 -0
- package/dist/canvas/TokenModeSwitcher.svelte +55 -0
- package/dist/canvas/TokenModeSwitcher.svelte.d.ts +18 -0
- package/dist/canvas/TokenModeSwitcher.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasArrange.svelte.d.ts +4 -0
- package/dist/canvas/createCanvasArrange.svelte.d.ts.map +1 -1
- package/dist/canvas/createCanvasAutoLayout.svelte.d.ts.map +1 -1
- package/dist/canvas/createCanvasTokens.svelte.d.ts +31 -0
- package/dist/canvas/createCanvasTokens.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasTokens.svelte.js +50 -0
- package/dist/canvas/index.d.ts +5 -0
- package/dist/canvas/index.d.ts.map +1 -1
- package/dist/canvas/index.js +7 -0
- package/dist/fileImport.svelte.d.ts +8 -1
- package/dist/fileImport.svelte.d.ts.map +1 -1
- package/dist/fileImport.svelte.js +11 -1
- package/dist/generation/AssetMentionChips.svelte +130 -0
- package/dist/generation/AssetMentionChips.svelte.d.ts +21 -0
- package/dist/generation/AssetMentionChips.svelte.d.ts.map +1 -0
- package/dist/generation/GenerationHistory.svelte +161 -0
- package/dist/generation/GenerationHistory.svelte.d.ts +28 -0
- package/dist/generation/GenerationHistory.svelte.d.ts.map +1 -0
- package/dist/generation/index.d.ts +7 -0
- package/dist/generation/index.d.ts.map +1 -1
- package/dist/generation/index.js +3 -0
- package/dist/generation/records.svelte.d.ts +18 -0
- package/dist/generation/records.svelte.d.ts.map +1 -0
- package/dist/generation/records.svelte.js +66 -0
- package/dist/image-editor/ImageEditor.svelte +72 -1
- package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/image-editor/RefineEdgePanel.svelte +180 -0
- package/dist/image-editor/RefineEdgePanel.svelte.d.ts +13 -0
- package/dist/image-editor/RefineEdgePanel.svelte.d.ts.map +1 -0
- package/dist/image-editor/SelectionOptionsBar.svelte +135 -0
- package/dist/image-editor/SelectionOptionsBar.svelte.d.ts +15 -0
- package/dist/image-editor/SelectionOptionsBar.svelte.d.ts.map +1 -0
- package/dist/image-editor/index.d.ts +6 -0
- package/dist/image-editor/index.d.ts.map +1 -1
- package/dist/image-editor/index.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/paragraphStyles.svelte.d.ts +30 -0
- package/dist/paragraphStyles.svelte.d.ts.map +1 -0
- package/dist/paragraphStyles.svelte.js +44 -0
- package/dist/review/TrackedChangesList.svelte +67 -0
- package/dist/review/TrackedChangesList.svelte.d.ts +13 -0
- package/dist/review/TrackedChangesList.svelte.d.ts.map +1 -0
- package/dist/review/index.d.ts +3 -0
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -0
- package/dist/review/review.svelte.d.ts +6 -0
- package/dist/review/review.svelte.d.ts.map +1 -1
- package/dist/review/review.svelte.js +3 -0
- package/dist/spreadsheet/FormulaBar.svelte +13 -4
- package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/FormulaHints.svelte +7 -5
- package/dist/spreadsheet/FormulaHints.svelte.d.ts +3 -0
- package/dist/spreadsheet/FormulaHints.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/NameManager.svelte +165 -0
- package/dist/spreadsheet/NameManager.svelte.d.ts +11 -0
- package/dist/spreadsheet/NameManager.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SheetTabs.svelte +161 -0
- package/dist/spreadsheet/SheetTabs.svelte.d.ts +10 -0
- package/dist/spreadsheet/SheetTabs.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte +184 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetConditionalFormatPanel.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte +9 -4
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts +5 -1
- package/dist/spreadsheet/SpreadsheetContextMenu.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte +141 -0
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetDataValidationPanel.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/SpreadsheetEditor.svelte +197 -13
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +2 -0
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte +92 -0
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts +9 -0
- package/dist/spreadsheet/SpreadsheetValidationDialog.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/index.d.ts +9 -0
- package/dist/spreadsheet/index.d.ts.map +1 -1
- package/dist/spreadsheet/index.js +5 -0
- package/dist/spreadsheet/spreadsheet.svelte.d.ts +55 -2
- package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -1
- package/dist/spreadsheet/spreadsheet.svelte.js +105 -6
- package/dist/tokens/TokenSwatch.svelte +14 -0
- package/dist/tokens/TokenSwatch.svelte.d.ts +8 -0
- package/dist/tokens/TokenSwatch.svelte.d.ts.map +1 -0
- package/dist/tokens/TokensPanel.svelte +323 -0
- package/dist/tokens/TokensPanel.svelte.d.ts +14 -0
- package/dist/tokens/TokensPanel.svelte.d.ts.map +1 -0
- package/dist/tokens/createTokens.svelte.d.ts +31 -0
- package/dist/tokens/createTokens.svelte.d.ts.map +1 -0
- package/dist/tokens/createTokens.svelte.js +43 -0
- package/dist/tokens/index.d.ts +7 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +3 -0
- package/package.json +10 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileImportController, } from "@pixerate/editor";
|
|
1
|
+
import { FileImportController, summarizeImportStructure, } from "@pixerate/editor";
|
|
2
2
|
/**
|
|
3
3
|
* Imports markdown, HTML, plain-text and RTF files (or anything registered
|
|
4
4
|
* with `registerImportAdapter` for the `document` target) into an editor,
|
|
@@ -14,6 +14,7 @@ export function createFileImport(getEditor, options = {}) {
|
|
|
14
14
|
return editor && !editor.isDestroyed ? editor : null;
|
|
15
15
|
}, options);
|
|
16
16
|
let state = $state(controller.getState());
|
|
17
|
+
let summary = $derived(summarizeImportStructure(state.lastOutcomes));
|
|
17
18
|
controller.subscribe((next) => {
|
|
18
19
|
state = next;
|
|
19
20
|
});
|
|
@@ -27,6 +28,15 @@ export function createFileImport(getEditor, options = {}) {
|
|
|
27
28
|
get lastOutcomes() {
|
|
28
29
|
return state.lastOutcomes;
|
|
29
30
|
},
|
|
31
|
+
/**
|
|
32
|
+
* Structures detected in the last import (headings, lists, tables,
|
|
33
|
+
* columns), or `null` when no adapter reported any (only structure-aware
|
|
34
|
+
* adapters such as PDF do). Turn PDF detection off with
|
|
35
|
+
* `setOptions({ adapterOptions: { pdf: { structure: false } } })`.
|
|
36
|
+
*/
|
|
37
|
+
get structureSummary() {
|
|
38
|
+
return summary;
|
|
39
|
+
},
|
|
30
40
|
/** Value for `<input type="file" accept={…}>`. */
|
|
31
41
|
get accept() {
|
|
32
42
|
return controller.accept;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import {
|
|
4
|
+
defaultAssetRoleOptions,
|
|
5
|
+
describeAssetMentions,
|
|
6
|
+
findTextMentions,
|
|
7
|
+
getAssetRoleOptions,
|
|
8
|
+
setMentionRoleInText,
|
|
9
|
+
type AssetInfo,
|
|
10
|
+
type AssetMentionChip,
|
|
11
|
+
type AssetRoleOption,
|
|
12
|
+
type ModelCapabilities
|
|
13
|
+
} from '@pixerate/editor/generation';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
/** Prompt text containing `@asset` / `@asset:role` mentions. */
|
|
17
|
+
content: string;
|
|
18
|
+
/** Called with the text after a role is picked (`@fox` → `@fox:style_ref`). */
|
|
19
|
+
onchange: (next: string) => void;
|
|
20
|
+
/** Mentionable assets; mentions that match none (by id or label) are not shown. */
|
|
21
|
+
assets: readonly AssetInfo[];
|
|
22
|
+
/** The selected model: its roles are offered and mentions are checked against it. */
|
|
23
|
+
capabilities?: ModelCapabilities;
|
|
24
|
+
/** Role options (default: the model's roles, else the built-in roles). */
|
|
25
|
+
roles?: readonly AssetRoleOption[];
|
|
26
|
+
/** Custom chip content (default: the asset label). */
|
|
27
|
+
asset?: Snippet<[AssetMentionChip]>;
|
|
28
|
+
class?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let { content, onchange, assets, capabilities, roles, asset, class: className = '' }: Props = $props();
|
|
32
|
+
|
|
33
|
+
let open = $state<number | null>(null);
|
|
34
|
+
let rootEl = $state<HTMLDivElement | null>(null);
|
|
35
|
+
const chips = $derived(describeAssetMentions(findTextMentions(content), assets, capabilities));
|
|
36
|
+
const options = $derived(roles ?? (capabilities ? getAssetRoleOptions(capabilities) : defaultAssetRoleOptions()));
|
|
37
|
+
|
|
38
|
+
$effect(() => {
|
|
39
|
+
if (open === null) return;
|
|
40
|
+
const onPointer = (event: MouseEvent) => {
|
|
41
|
+
if (!rootEl?.contains(event.target as Node)) open = null;
|
|
42
|
+
};
|
|
43
|
+
document.addEventListener('mousedown', onPointer);
|
|
44
|
+
return () => document.removeEventListener('mousedown', onPointer);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const pick = (chip: AssetMentionChip, role: string | null) => {
|
|
48
|
+
open = null;
|
|
49
|
+
if (chip.start === undefined) return;
|
|
50
|
+
onchange(setMentionRoleInText(content, chip.start, role));
|
|
51
|
+
};
|
|
52
|
+
const problemOf = (chip: AssetMentionChip) =>
|
|
53
|
+
chip.issues.find((issue) => issue.severity === 'error') ?? chip.issues.find((issue) => issue.severity === 'warning');
|
|
54
|
+
const onKey = (event: KeyboardEvent) => {
|
|
55
|
+
if (event.key === 'Escape') open = null;
|
|
56
|
+
};
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<!-- Asset mentions as chips with role badges and a role picker popover. -->
|
|
60
|
+
{#if chips.length > 0}
|
|
61
|
+
<div bind:this={rootEl} class="flex flex-wrap gap-1.5 {className}" data-asset-mention-chips="">
|
|
62
|
+
{#each chips as chip (`${chip.start}-${chip.index}`)}
|
|
63
|
+
{@const problem = problemOf(chip)}
|
|
64
|
+
{@const isOpen = open === chip.index}
|
|
65
|
+
<span class="relative inline-flex" data-asset-chip={chip.asset.id}>
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
aria-haspopup="listbox"
|
|
69
|
+
aria-expanded={isOpen}
|
|
70
|
+
title={problem?.message}
|
|
71
|
+
data-issue={problem?.code}
|
|
72
|
+
onclick={() => (open = isOpen ? null : chip.index)}
|
|
73
|
+
onkeydown={onKey}
|
|
74
|
+
class="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs {problem?.severity === 'error'
|
|
75
|
+
? 'border-red-500/60'
|
|
76
|
+
: problem
|
|
77
|
+
? 'border-amber-500/60'
|
|
78
|
+
: 'border-border'} bg-muted/40 hover:bg-muted"
|
|
79
|
+
>
|
|
80
|
+
<span>{#if asset}{@render asset(chip)}{:else}@{chip.asset.label ?? chip.handle}{/if}</span>
|
|
81
|
+
<span
|
|
82
|
+
class="rounded px-1 text-[10px] uppercase tracking-wide {chip.role ? 'bg-primary/15' : 'opacity-60'}"
|
|
83
|
+
data-role-badge={chip.role ?? ''}>{chip.roleLabel ?? 'No role'}</span
|
|
84
|
+
>
|
|
85
|
+
</button>
|
|
86
|
+
{#if isOpen}
|
|
87
|
+
<div
|
|
88
|
+
role="listbox"
|
|
89
|
+
tabindex="-1"
|
|
90
|
+
aria-label="Role for {chip.asset.label ?? chip.handle}"
|
|
91
|
+
class="absolute left-0 top-full z-50 mt-1 flex min-w-[12rem] flex-col rounded-md border border-border bg-background p-1 text-xs shadow-lg"
|
|
92
|
+
data-role-picker=""
|
|
93
|
+
onkeydown={onKey}
|
|
94
|
+
>
|
|
95
|
+
{#each options as option (option.role)}
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
role="option"
|
|
99
|
+
aria-selected={chip.role === option.role}
|
|
100
|
+
data-role-option={option.role}
|
|
101
|
+
title={option.description}
|
|
102
|
+
onclick={() => pick(chip, option.role)}
|
|
103
|
+
class="flex items-center justify-between gap-2 rounded px-2 py-1 text-left hover:bg-muted"
|
|
104
|
+
>
|
|
105
|
+
<span>{option.label}</span>{#if option.accepts?.length}<span class="text-[10px] opacity-60"
|
|
106
|
+
>{option.accepts.join(', ')}</span
|
|
107
|
+
>{/if}
|
|
108
|
+
</button>
|
|
109
|
+
{/each}
|
|
110
|
+
<button
|
|
111
|
+
type="button"
|
|
112
|
+
role="option"
|
|
113
|
+
aria-selected={!chip.role}
|
|
114
|
+
data-role-option=""
|
|
115
|
+
onclick={() => pick(chip, null)}
|
|
116
|
+
class="rounded px-2 py-1 text-left opacity-70 hover:bg-muted">No role</button
|
|
117
|
+
>
|
|
118
|
+
{#if chip.issues.length > 0}
|
|
119
|
+
<ul class="mt-1 border-t border-border px-2 pt-1 text-[10px]" data-chip-issues="">
|
|
120
|
+
{#each chip.issues as issue, i (i)}
|
|
121
|
+
<li class={issue.severity === 'error' ? 'text-red-500' : 'text-amber-500'}>{issue.message}</li>
|
|
122
|
+
{/each}
|
|
123
|
+
</ul>
|
|
124
|
+
{/if}
|
|
125
|
+
</div>
|
|
126
|
+
{/if}
|
|
127
|
+
</span>
|
|
128
|
+
{/each}
|
|
129
|
+
</div>
|
|
130
|
+
{/if}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type AssetInfo, type AssetMentionChip, type AssetRoleOption, type ModelCapabilities } from '@pixerate/editor/generation';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Prompt text containing `@asset` / `@asset:role` mentions. */
|
|
5
|
+
content: string;
|
|
6
|
+
/** Called with the text after a role is picked (`@fox` → `@fox:style_ref`). */
|
|
7
|
+
onchange: (next: string) => void;
|
|
8
|
+
/** Mentionable assets; mentions that match none (by id or label) are not shown. */
|
|
9
|
+
assets: readonly AssetInfo[];
|
|
10
|
+
/** The selected model: its roles are offered and mentions are checked against it. */
|
|
11
|
+
capabilities?: ModelCapabilities;
|
|
12
|
+
/** Role options (default: the model's roles, else the built-in roles). */
|
|
13
|
+
roles?: readonly AssetRoleOption[];
|
|
14
|
+
/** Custom chip content (default: the asset label). */
|
|
15
|
+
asset?: Snippet<[AssetMentionChip]>;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
declare const AssetMentionChips: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type AssetMentionChips = ReturnType<typeof AssetMentionChips>;
|
|
20
|
+
export default AssetMentionChips;
|
|
21
|
+
//# sourceMappingURL=AssetMentionChips.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetMentionChips.svelte.d.ts","sourceRoot":"","sources":["../../src/generation/AssetMentionChips.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAMH,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAC;AAGrC,UAAU,KAAK;IACb,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,mFAAmF;IACnF,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7B,qFAAqF;IACrF,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC,sDAAsD;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyEH,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
let nextId = 0;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
import {
|
|
8
|
+
DRIFT_TYPE_LABELS,
|
|
9
|
+
recreateRequest,
|
|
10
|
+
referenceRoleLabel,
|
|
11
|
+
variationRequest,
|
|
12
|
+
type AssetLookup,
|
|
13
|
+
type GenerationRecord,
|
|
14
|
+
type ModelCapabilities,
|
|
15
|
+
type RecreateMode,
|
|
16
|
+
type RecreateOptions,
|
|
17
|
+
type RecreateResult
|
|
18
|
+
} from '@pixerate/editor/generation';
|
|
19
|
+
// Not `@pixerate/editor/grammar`: svelte-package keeps type imports in
|
|
20
|
+
// `.svelte` output, which would pull another core entry into this one.
|
|
21
|
+
type Template = NonNullable<RecreateOptions['templates']>[number];
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
records: readonly GenerationRecord[];
|
|
25
|
+
/** Current templates (drift detection, and resolution in `latest` mode). */
|
|
26
|
+
templates?: readonly Template[];
|
|
27
|
+
/** Current models (capability drift and re-planning). */
|
|
28
|
+
capabilities?: ModelCapabilities | readonly ModelCapabilities[];
|
|
29
|
+
/** Current assets (missing / changed asset drift). */
|
|
30
|
+
assets?: AssetLookup;
|
|
31
|
+
/** Recreate mode (bindable); starts at `exact`. */
|
|
32
|
+
mode?: RecreateMode;
|
|
33
|
+
onmodechange?: (mode: RecreateMode) => void;
|
|
34
|
+
/** "Recreate": receives the rebuilt request and drift report. */
|
|
35
|
+
onrecreate?: (result: RecreateResult, record: GenerationRecord) => void;
|
|
36
|
+
/** "Variation": same request with a new seed. */
|
|
37
|
+
onvariation?: (result: RecreateResult, record: GenerationRecord) => void;
|
|
38
|
+
onremove?: (record: GenerationRecord) => void;
|
|
39
|
+
/** Extra content per record (e.g. output thumbnails). */
|
|
40
|
+
preview?: Snippet<[GenerationRecord]>;
|
|
41
|
+
emptyText?: string;
|
|
42
|
+
class?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let {
|
|
46
|
+
records,
|
|
47
|
+
templates,
|
|
48
|
+
capabilities,
|
|
49
|
+
assets,
|
|
50
|
+
mode = $bindable('exact'),
|
|
51
|
+
onmodechange,
|
|
52
|
+
onrecreate,
|
|
53
|
+
onvariation,
|
|
54
|
+
onremove,
|
|
55
|
+
preview,
|
|
56
|
+
emptyText = 'No generations yet.',
|
|
57
|
+
class: className = ''
|
|
58
|
+
}: Props = $props();
|
|
59
|
+
|
|
60
|
+
const groupName = `generation-history-mode-${++nextId}`;
|
|
61
|
+
const options = $derived({ templates, capabilities, assets, mode });
|
|
62
|
+
const results = $derived(records.map((record) => recreateRequest(record, options)));
|
|
63
|
+
const models = $derived(
|
|
64
|
+
capabilities ? (Array.isArray(capabilities) ? capabilities : [capabilities as ModelCapabilities]) : []
|
|
65
|
+
);
|
|
66
|
+
const severityClass = {
|
|
67
|
+
info: 'text-sky-600 dark:text-sky-300',
|
|
68
|
+
warning: 'text-amber-600 dark:text-amber-300',
|
|
69
|
+
error: 'text-red-600 dark:text-red-400'
|
|
70
|
+
} as const;
|
|
71
|
+
|
|
72
|
+
const setMode = (next: RecreateMode) => {
|
|
73
|
+
mode = next;
|
|
74
|
+
onmodechange?.(next);
|
|
75
|
+
};
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<!-- Generation history with drift reports and Recreate / Variation actions. -->
|
|
79
|
+
<div class="flex flex-col gap-2 text-sm {className}" data-generation-history="">
|
|
80
|
+
<div class="flex items-center gap-2 text-xs" role="radiogroup" aria-label="Recreate mode">
|
|
81
|
+
{#each ['exact', 'latest'] as const as value (value)}
|
|
82
|
+
<label class="inline-flex items-center gap-1">
|
|
83
|
+
<input
|
|
84
|
+
type="radio"
|
|
85
|
+
name={groupName}
|
|
86
|
+
{value}
|
|
87
|
+
checked={mode === value}
|
|
88
|
+
onchange={() => setMode(value)}
|
|
89
|
+
data-mode={value}
|
|
90
|
+
/>
|
|
91
|
+
{value === 'exact' ? 'Exact (recorded versions)' : 'Latest templates & settings'}
|
|
92
|
+
</label>
|
|
93
|
+
{/each}
|
|
94
|
+
</div>
|
|
95
|
+
{#if records.length === 0}<p class="text-xs text-muted-foreground">{emptyText}</p>{/if}
|
|
96
|
+
<ul class="flex flex-col gap-2">
|
|
97
|
+
{#each records as record, i (record.id)}
|
|
98
|
+
{@const result = results[i]}
|
|
99
|
+
{@const model = models.find((m) => m.id === record.provenance.modelId)}
|
|
100
|
+
<li class="rounded-md border border-border p-2" data-record={record.id} data-drift={result.drift.length}>
|
|
101
|
+
<div class="flex flex-wrap items-center justify-between gap-2 text-xs">
|
|
102
|
+
<span class="font-medium">{model?.name ?? record.provenance.modelId}</span>
|
|
103
|
+
<span class="text-muted-foreground">
|
|
104
|
+
{#if record.kind !== 'generate'}<span data-kind="">{record.kind} · </span>{/if}<time datetime={record.createdAt}
|
|
105
|
+
>{record.createdAt.replace('T', ' ').slice(0, 16)}</time
|
|
106
|
+
>{#if record.seed !== undefined} · seed {record.seed}{/if}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
<p class="mt-1 line-clamp-3 break-words text-xs" data-record-prompt="">{record.provenance.resolvedPrompt}</p>
|
|
110
|
+
{#if record.assets.length > 0}
|
|
111
|
+
<div class="mt-1 flex flex-wrap gap-1">
|
|
112
|
+
{#each record.assets as item (`${item.role}:${item.id}`)}
|
|
113
|
+
<span class="rounded-full bg-muted px-2 py-0.5 text-[10px]" data-asset-role={item.role}
|
|
114
|
+
>{item.id} · {referenceRoleLabel(item.role, model)}</span
|
|
115
|
+
>
|
|
116
|
+
{/each}
|
|
117
|
+
</div>
|
|
118
|
+
{/if}
|
|
119
|
+
{#if preview}{@render preview(record)}{/if}
|
|
120
|
+
{#if result.drift.length > 0}
|
|
121
|
+
<ul class="mt-1 flex flex-col gap-0.5 text-[11px]" data-drift-report="">
|
|
122
|
+
{#each result.drift as item, j (j)}
|
|
123
|
+
<li class={severityClass[item.severity]} data-drift-type={item.type}>
|
|
124
|
+
<strong>{DRIFT_TYPE_LABELS[item.type]}:</strong>
|
|
125
|
+
{item.message}
|
|
126
|
+
</li>
|
|
127
|
+
{/each}
|
|
128
|
+
</ul>
|
|
129
|
+
{/if}
|
|
130
|
+
<div class="mt-2 flex gap-2">
|
|
131
|
+
{#if onrecreate}
|
|
132
|
+
<button
|
|
133
|
+
type="button"
|
|
134
|
+
disabled={!result.ok}
|
|
135
|
+
title={!result.ok ? 'This generation cannot be recreated as recorded.' : undefined}
|
|
136
|
+
onclick={() => onrecreate(result, record)}
|
|
137
|
+
class="rounded border border-border px-2 py-0.5 text-xs hover:bg-muted disabled:opacity-50"
|
|
138
|
+
data-action="recreate">Recreate</button
|
|
139
|
+
>
|
|
140
|
+
{/if}
|
|
141
|
+
{#if onvariation}
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
onclick={() => onvariation(variationRequest(record, {}, options), record)}
|
|
145
|
+
class="rounded border border-border px-2 py-0.5 text-xs hover:bg-muted"
|
|
146
|
+
data-action="variation">Variation</button
|
|
147
|
+
>
|
|
148
|
+
{/if}
|
|
149
|
+
{#if onremove}
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
onclick={() => onremove(record)}
|
|
153
|
+
class="ml-auto rounded px-2 py-0.5 text-xs text-muted-foreground hover:bg-muted"
|
|
154
|
+
data-action="remove">Remove</button
|
|
155
|
+
>
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
158
|
+
</li>
|
|
159
|
+
{/each}
|
|
160
|
+
</ul>
|
|
161
|
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type AssetLookup, type GenerationRecord, type ModelCapabilities, type RecreateMode, type RecreateOptions, type RecreateResult } from '@pixerate/editor/generation';
|
|
3
|
+
type Template = NonNullable<RecreateOptions['templates']>[number];
|
|
4
|
+
interface Props {
|
|
5
|
+
records: readonly GenerationRecord[];
|
|
6
|
+
/** Current templates (drift detection, and resolution in `latest` mode). */
|
|
7
|
+
templates?: readonly Template[];
|
|
8
|
+
/** Current models (capability drift and re-planning). */
|
|
9
|
+
capabilities?: ModelCapabilities | readonly ModelCapabilities[];
|
|
10
|
+
/** Current assets (missing / changed asset drift). */
|
|
11
|
+
assets?: AssetLookup;
|
|
12
|
+
/** Recreate mode (bindable); starts at `exact`. */
|
|
13
|
+
mode?: RecreateMode;
|
|
14
|
+
onmodechange?: (mode: RecreateMode) => void;
|
|
15
|
+
/** "Recreate": receives the rebuilt request and drift report. */
|
|
16
|
+
onrecreate?: (result: RecreateResult, record: GenerationRecord) => void;
|
|
17
|
+
/** "Variation": same request with a new seed. */
|
|
18
|
+
onvariation?: (result: RecreateResult, record: GenerationRecord) => void;
|
|
19
|
+
onremove?: (record: GenerationRecord) => void;
|
|
20
|
+
/** Extra content per record (e.g. output thumbnails). */
|
|
21
|
+
preview?: Snippet<[GenerationRecord]>;
|
|
22
|
+
emptyText?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
declare const GenerationHistory: import("svelte").Component<Props, {}, "mode">;
|
|
26
|
+
type GenerationHistory = ReturnType<typeof GenerationHistory>;
|
|
27
|
+
export default GenerationHistory;
|
|
28
|
+
//# sourceMappingURL=GenerationHistory.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenerationHistory.svelte.d.ts","sourceRoot":"","sources":["../../src/generation/GenerationHistory.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAKH,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;AAIrC,KAAK,QAAQ,GAAG,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,UAAU,KAAK;IACb,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IAChC,yDAAyD;IACzD,YAAY,CAAC,EAAE,iBAAiB,GAAG,SAAS,iBAAiB,EAAE,CAAC;IAChE,sDAAsD;IACtD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mDAAmD;IACnD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxE,iDAAiD;IACjD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC9C,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqGH,QAAA,MAAM,iBAAiB,+CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import type { ComponentProps } from "svelte";
|
|
2
2
|
import type GenerationSettingsComponent from "./GenerationSettings.svelte";
|
|
3
3
|
import type JobProgressComponent from "./JobProgress.svelte";
|
|
4
|
+
import type AssetMentionChipsComponent from "./AssetMentionChips.svelte";
|
|
5
|
+
import type GenerationHistoryComponent from "./GenerationHistory.svelte";
|
|
4
6
|
export { default as GenerationSettings } from "./GenerationSettings.svelte";
|
|
5
7
|
export { default as JobProgress } from "./JobProgress.svelte";
|
|
8
|
+
export { default as AssetMentionChips } from "./AssetMentionChips.svelte";
|
|
9
|
+
export { default as GenerationHistory } from "./GenerationHistory.svelte";
|
|
6
10
|
export * from "./plan.svelte.js";
|
|
7
11
|
export * from "./progress.svelte.js";
|
|
12
|
+
export * from "./records.svelte.js";
|
|
8
13
|
export type GenerationSettingsProps = ComponentProps<typeof GenerationSettingsComponent>;
|
|
9
14
|
export type JobProgressProps = ComponentProps<typeof JobProgressComponent>;
|
|
15
|
+
export type AssetMentionChipsProps = ComponentProps<typeof AssetMentionChipsComponent>;
|
|
16
|
+
export type GenerationHistoryProps = ComponentProps<typeof GenerationHistoryComponent>;
|
|
10
17
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generation/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,2BAA2B,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generation/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,2BAA2B,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,0BAA0B,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,0BAA0B,MAAM,4BAA4B,CAAC;AAEzE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AAEpC,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,OAAO,2BAA2B,CAAC,CAAC;AACzF,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,0BAA0B,CAAC,CAAC;AACvF,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
|
package/dist/generation/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { default as GenerationSettings } from "./GenerationSettings.svelte";
|
|
2
2
|
export { default as JobProgress } from "./JobProgress.svelte";
|
|
3
|
+
export { default as AssetMentionChips } from "./AssetMentionChips.svelte";
|
|
4
|
+
export { default as GenerationHistory } from "./GenerationHistory.svelte";
|
|
3
5
|
export * from "./plan.svelte.js";
|
|
4
6
|
export * from "./progress.svelte.js";
|
|
7
|
+
export * from "./records.svelte.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { GenerationRecord, GenerationRecordFilter, GenerationRecordStore } from "@pixerate/editor/generation";
|
|
2
|
+
export interface SvelteGenerationRecords {
|
|
3
|
+
readonly records: GenerationRecord[];
|
|
4
|
+
readonly loading: boolean;
|
|
5
|
+
readonly error: unknown;
|
|
6
|
+
refresh(): Promise<void>;
|
|
7
|
+
/** Stops following the store. */
|
|
8
|
+
destroy(): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reactive records of a `GenerationRecordStore` (Svelte 5 runes), newest
|
|
12
|
+
* first, reloaded whenever the store changes. Pass a getter for a reactive
|
|
13
|
+
* filter: `createGenerationRecords(store, () => ({ model }))`. Call
|
|
14
|
+
* `destroy()` (or create it inside a component, where it is cleaned up with
|
|
15
|
+
* the component's effects) to stop listening.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createGenerationRecords(store: GenerationRecordStore, filter?: () => GenerationRecordFilter | undefined): SvelteGenerationRecords;
|
|
18
|
+
//# sourceMappingURL=records.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.svelte.d.ts","sourceRoot":"","sources":["../../src/generation/records.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,iCAAiC;IACjC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,GAAE,MAAM,sBAAsB,GAAG,SAA2B,GACjE,uBAAuB,CAuDzB"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reactive records of a `GenerationRecordStore` (Svelte 5 runes), newest
|
|
3
|
+
* first, reloaded whenever the store changes. Pass a getter for a reactive
|
|
4
|
+
* filter: `createGenerationRecords(store, () => ({ model }))`. Call
|
|
5
|
+
* `destroy()` (or create it inside a component, where it is cleaned up with
|
|
6
|
+
* the component's effects) to stop listening.
|
|
7
|
+
*/
|
|
8
|
+
export function createGenerationRecords(store, filter = () => undefined) {
|
|
9
|
+
let records = $state([]);
|
|
10
|
+
let loading = $state(true);
|
|
11
|
+
let error = $state(null);
|
|
12
|
+
let currentFilter = undefined;
|
|
13
|
+
let generation = 0;
|
|
14
|
+
const refresh = async () => {
|
|
15
|
+
const ticket = ++generation;
|
|
16
|
+
loading = true;
|
|
17
|
+
try {
|
|
18
|
+
const next = await store.list(currentFilter);
|
|
19
|
+
if (ticket !== generation)
|
|
20
|
+
return;
|
|
21
|
+
records = next;
|
|
22
|
+
error = null;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (ticket === generation)
|
|
26
|
+
error = err;
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
if (ticket === generation)
|
|
30
|
+
loading = false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const unsubscribe = store.subscribe(() => void refresh());
|
|
34
|
+
let stopEffect = null;
|
|
35
|
+
if ($effect.tracking()) {
|
|
36
|
+
$effect(() => {
|
|
37
|
+
currentFilter = filter();
|
|
38
|
+
void refresh();
|
|
39
|
+
});
|
|
40
|
+
$effect(() => unsubscribe);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
stopEffect = $effect.root(() => {
|
|
44
|
+
$effect(() => {
|
|
45
|
+
currentFilter = filter();
|
|
46
|
+
void refresh();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
get records() {
|
|
52
|
+
return records;
|
|
53
|
+
},
|
|
54
|
+
get loading() {
|
|
55
|
+
return loading;
|
|
56
|
+
},
|
|
57
|
+
get error() {
|
|
58
|
+
return error;
|
|
59
|
+
},
|
|
60
|
+
refresh,
|
|
61
|
+
destroy() {
|
|
62
|
+
unsubscribe();
|
|
63
|
+
stopEffect?.();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -20,10 +20,15 @@
|
|
|
20
20
|
LATEST_PROCESS_VERSION,
|
|
21
21
|
IMAGE_ADJUSTMENT_CONTROLS,
|
|
22
22
|
readAdjustmentControls,
|
|
23
|
+
DEFAULT_SELECTION_TOOL_OPTIONS,
|
|
24
|
+
SelectionGesture,
|
|
25
|
+
type SelectionToolOptions,
|
|
23
26
|
} from '@pixerate/editor/image-editor';
|
|
24
27
|
import ControlSlider from './ControlSlider.svelte';
|
|
25
28
|
import Histogram from './Histogram.svelte';
|
|
26
29
|
import AdjustmentLayersPanel from './AdjustmentLayersPanel.svelte';
|
|
30
|
+
import SelectionOptionsBar from './SelectionOptionsBar.svelte';
|
|
31
|
+
import RefineEdgePanel from './RefineEdgePanel.svelte';
|
|
27
32
|
import { onDestroy, untrack } from 'svelte';
|
|
28
33
|
import { createReactiveImageEditor, type ReactiveImageEditor } from './imageEditorState.svelte.js';
|
|
29
34
|
|
|
@@ -60,7 +65,13 @@
|
|
|
60
65
|
|
|
61
66
|
let canvasEl = $state<HTMLCanvasElement | null>(null);
|
|
62
67
|
let containerEl = $state<HTMLDivElement | null>(null);
|
|
63
|
-
let activeTab = $state<'crop' | 'adjust' | 'annotate' | 'depth' | 'ai'>('crop');
|
|
68
|
+
let activeTab = $state<'crop' | 'adjust' | 'select' | 'annotate' | 'depth' | 'ai'>('crop');
|
|
69
|
+
|
|
70
|
+
// Selection tools (magic wand, marquees, lasso): options bar state and the pointer gesture.
|
|
71
|
+
let selectionOptions = $state<SelectionToolOptions>({ ...DEFAULT_SELECTION_TOOL_OPTIONS });
|
|
72
|
+
const selectionGesture = $derived(new SelectionGesture(editor.controller, () => selectionOptions));
|
|
73
|
+
let selectionDraft = $state.raw<ImagePoint[] | null>(null);
|
|
74
|
+
let selectionError = $state<string | null>(null);
|
|
64
75
|
let activeCropRatio = $state<number | null>(null);
|
|
65
76
|
|
|
66
77
|
// AI state
|
|
@@ -187,6 +198,7 @@
|
|
|
187
198
|
interactive: true,
|
|
188
199
|
draftAnnotation: overrideAnnotation !== undefined ? overrideAnnotation : draftAnnotation,
|
|
189
200
|
draftCrop: overrideCrop !== undefined ? overrideCrop : draftCrop,
|
|
201
|
+
draftSelectionPath: selectionDraft,
|
|
190
202
|
});
|
|
191
203
|
}
|
|
192
204
|
|
|
@@ -244,6 +256,13 @@
|
|
|
244
256
|
if (!canvasEl) return;
|
|
245
257
|
const canvasPt = getCanvasEventPoint(e);
|
|
246
258
|
|
|
259
|
+
if (activeTab === 'select') {
|
|
260
|
+
const gesture = selectionGesture;
|
|
261
|
+
const imgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false);
|
|
262
|
+
void gesture.begin(imgPt, e).then(() => (selectionError = gesture.error));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
247
266
|
if (activeTab === 'crop') {
|
|
248
267
|
const activeCrop = draftCrop || editor.state.crop || {
|
|
249
268
|
x: 0,
|
|
@@ -316,6 +335,15 @@
|
|
|
316
335
|
if (!canvasEl) return;
|
|
317
336
|
const canvasPt = getCanvasEventPoint(e);
|
|
318
337
|
|
|
338
|
+
if (activeTab === 'select') {
|
|
339
|
+
const gesture = selectionGesture;
|
|
340
|
+
if (gesture.active && gesture.move(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false))) {
|
|
341
|
+
selectionDraft = gesture.draftPath;
|
|
342
|
+
renderCanvas();
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
319
347
|
if (activeTab === 'crop') {
|
|
320
348
|
if (cropDragInfo) {
|
|
321
349
|
const currentImgPt = canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, true);
|
|
@@ -468,6 +496,16 @@
|
|
|
468
496
|
if (!canvasEl) return;
|
|
469
497
|
const canvasPt = getCanvasEventPoint(e);
|
|
470
498
|
|
|
499
|
+
if (activeTab === 'select') {
|
|
500
|
+
const gesture = selectionGesture;
|
|
501
|
+
if (gesture.active) {
|
|
502
|
+
gesture.end(canvasToImagePoint(canvasPt, editor.state, canvasEl.width, canvasEl.height, false), e);
|
|
503
|
+
selectionDraft = null;
|
|
504
|
+
renderCanvas();
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
471
509
|
if (activeTab === 'crop') {
|
|
472
510
|
if (cropDragInfo) {
|
|
473
511
|
if (draftCrop && draftCrop.width >= 20 && draftCrop.height >= 20) {
|
|
@@ -621,6 +659,18 @@
|
|
|
621
659
|
>
|
|
622
660
|
Adjustments
|
|
623
661
|
</button>
|
|
662
|
+
<button
|
|
663
|
+
type="button"
|
|
664
|
+
onclick={() => {
|
|
665
|
+
activeTab = 'select';
|
|
666
|
+
editor.setTool('select');
|
|
667
|
+
}}
|
|
668
|
+
class="px-3 py-1.5 rounded-md font-medium transition-colors {activeTab === 'select'
|
|
669
|
+
? 'bg-indigo-600 text-white shadow'
|
|
670
|
+
: 'text-slate-400 hover:text-slate-200'}"
|
|
671
|
+
>
|
|
672
|
+
Select & Mask
|
|
673
|
+
</button>
|
|
624
674
|
<button
|
|
625
675
|
type="button"
|
|
626
676
|
onclick={() => {
|
|
@@ -1153,6 +1203,27 @@
|
|
|
1153
1203
|
</div>
|
|
1154
1204
|
{/if}
|
|
1155
1205
|
|
|
1206
|
+
<!-- SELECT & MASK: selection tools, refine edge, layer masks -->
|
|
1207
|
+
{#if activeTab === 'select'}
|
|
1208
|
+
<div class="flex flex-col gap-4 text-xs">
|
|
1209
|
+
<SelectionOptionsBar
|
|
1210
|
+
controller={editor.controller}
|
|
1211
|
+
options={selectionOptions}
|
|
1212
|
+
onOptionsChange={(next) => (selectionOptions = next)}
|
|
1213
|
+
selection={editor.state.selection ?? null}
|
|
1214
|
+
quickMask={Boolean(editor.state.quickMask)}
|
|
1215
|
+
/>
|
|
1216
|
+
{#if selectionError}
|
|
1217
|
+
<p role="alert" class="text-rose-400">{selectionError}</p>
|
|
1218
|
+
{/if}
|
|
1219
|
+
<RefineEdgePanel
|
|
1220
|
+
controller={editor.controller}
|
|
1221
|
+
selection={editor.state.selection ?? null}
|
|
1222
|
+
layers={editor.state.adjustments.layers}
|
|
1223
|
+
/>
|
|
1224
|
+
</div>
|
|
1225
|
+
{/if}
|
|
1226
|
+
|
|
1156
1227
|
<!-- TAB 5: AI TOOLS / INPAINTING & BACKGROUND REMOVAL -->
|
|
1157
1228
|
{#if activeTab === 'ai'}
|
|
1158
1229
|
<div class="flex flex-col gap-4 text-xs">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/image-editor/ImageEditor.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAIR,aAAa,EAGb,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,+BAA+B,CAAC;AAqBzC,OAAO,EAA6B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGjG,UAAU,KAAK;IACb,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,OAAO,CAAC,EAAE,kBAAkB,CAAC;CAC9B;AA8iCH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|