@aethervtt/shard-audio-editor 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/dom.d.ts +6 -0
- package/dist/dom.js +11 -0
- package/dist/drawing.d.ts +2 -0
- package/dist/drawing.js +12 -0
- package/dist/icons.d.ts +1 -0
- package/dist/icons.js +29 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/memory.d.ts +13 -0
- package/dist/memory.js +93 -0
- package/dist/methods.d.ts +4 -0
- package/dist/methods.js +79 -0
- package/dist/model.d.ts +61 -0
- package/dist/model.js +560 -0
- package/dist/panels/common.d.ts +33 -0
- package/dist/panels/common.js +1556 -0
- package/dist/panels/diagnostics.d.ts +2 -0
- package/dist/panels/diagnostics.js +7 -0
- package/dist/panels/event.d.ts +2 -0
- package/dist/panels/event.js +7 -0
- package/dist/panels/library.d.ts +2 -0
- package/dist/panels/library.js +7 -0
- package/dist/panels/mixer.d.ts +2 -0
- package/dist/panels/mixer.js +7 -0
- package/dist/panels/music.d.ts +2 -0
- package/dist/panels/music.js +7 -0
- package/dist/panels/parameters.d.ts +2 -0
- package/dist/panels/parameters.js +7 -0
- package/dist/preview-export.d.ts +68 -0
- package/dist/preview-export.js +143 -0
- package/dist/preview-render.d.ts +2 -0
- package/dist/preview-render.js +6 -0
- package/dist/preview-runtime.d.ts +114 -0
- package/dist/preview-runtime.js +414 -0
- package/dist/preview.d.ts +25 -0
- package/dist/preview.js +296 -0
- package/dist/project.d.ts +15 -0
- package/dist/project.js +332 -0
- package/dist/schema.d.ts +22 -0
- package/dist/schema.js +155 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.js +192 -0
- package/dist/types.d.ts +113 -0
- package/dist/types.js +0 -0
- package/dist/waveform.d.ts +16 -0
- package/dist/waveform.js +154 -0
- package/package.json +83 -4
package/dist/schema.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AudioCollection,
|
|
3
|
+
AudioEvent,
|
|
4
|
+
AudioMixer,
|
|
5
|
+
AudioParameters,
|
|
6
|
+
AudioSnapshot,
|
|
7
|
+
MusicProgram
|
|
8
|
+
} from "@aethervtt/shard-audio";
|
|
9
|
+
import {
|
|
10
|
+
ShardError
|
|
11
|
+
} from "@aethervtt/shard-core";
|
|
12
|
+
const audioEditorTypes = [
|
|
13
|
+
AudioEvent,
|
|
14
|
+
AudioParameters,
|
|
15
|
+
AudioCollection,
|
|
16
|
+
AudioMixer,
|
|
17
|
+
AudioSnapshot,
|
|
18
|
+
MusicProgram
|
|
19
|
+
];
|
|
20
|
+
function getAudioEditorSchema(type) {
|
|
21
|
+
return audioEditorTypes.find((schema) => schema.name === type);
|
|
22
|
+
}
|
|
23
|
+
function editorError(code, message, path, cause) {
|
|
24
|
+
return new ShardError(`audio-editor/${code}`, message, {
|
|
25
|
+
path,
|
|
26
|
+
cause,
|
|
27
|
+
hint: "Check the selected document and host capabilities, then try again."
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function asEditorError(error) {
|
|
31
|
+
return error instanceof ShardError ? error : editorError("invalid-document", String(error), void 0, error);
|
|
32
|
+
}
|
|
33
|
+
function clone(value) {
|
|
34
|
+
return structuredClone(value);
|
|
35
|
+
}
|
|
36
|
+
function equal(a, b) {
|
|
37
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
38
|
+
}
|
|
39
|
+
function merge(base, overrides) {
|
|
40
|
+
const out = clone(base);
|
|
41
|
+
for (const key of Object.keys(overrides)) {
|
|
42
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
|
|
43
|
+
const value = overrides[key];
|
|
44
|
+
const previous = out[key];
|
|
45
|
+
out[key] = value && typeof value === "object" && !Array.isArray(value) && previous && typeof previous === "object" && !Array.isArray(previous) ? merge(previous, value) : clone(value);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function knownFields(fields, value) {
|
|
50
|
+
const out = {};
|
|
51
|
+
for (const key of Object.keys(fields)) {
|
|
52
|
+
if (Object.hasOwn(value, key)) out[key] = knownField(fields[key], value[key]);
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
function knownField(field, value) {
|
|
57
|
+
if (field.kind === "struct" && field.fields && value && typeof value === "object" && !Array.isArray(value))
|
|
58
|
+
return knownFields(field.fields, value);
|
|
59
|
+
if (field.kind === "list" && field.item && Array.isArray(value))
|
|
60
|
+
return value.map((item) => knownField(field.item, item));
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
function audioDocumentReferences(fields, value, prefix = "") {
|
|
64
|
+
const refs = [];
|
|
65
|
+
for (const key of Object.keys(fields))
|
|
66
|
+
collectReference(fields[key], value[key], `${prefix}/${key}`, refs);
|
|
67
|
+
return refs;
|
|
68
|
+
}
|
|
69
|
+
function collectReference(field, value, path, refs) {
|
|
70
|
+
if (!value) return;
|
|
71
|
+
if (field.kind === "handle" && typeof value === "object" && !Array.isArray(value)) {
|
|
72
|
+
const id = value.guid ?? value.path;
|
|
73
|
+
if (typeof id === "string") refs.push({ id, type: field.assetType, path });
|
|
74
|
+
} else if (field.kind === "struct" && field.fields && typeof value === "object" && !Array.isArray(value)) {
|
|
75
|
+
refs.push(...audioDocumentReferences(field.fields, value, path));
|
|
76
|
+
} else if (field.kind === "list" && field.item && Array.isArray(value)) {
|
|
77
|
+
value.forEach((item, index) => {
|
|
78
|
+
collectReference(field.item, item, `${path}/${index}`, refs);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function materializeAudioDocument(document, documents, signal, chain = []) {
|
|
83
|
+
if (chain.includes(document.id) || chain.length >= 32)
|
|
84
|
+
throw editorError(
|
|
85
|
+
"invalid-document",
|
|
86
|
+
"Document inheritance contains a cycle or exceeds 32 levels.",
|
|
87
|
+
"/$extends"
|
|
88
|
+
);
|
|
89
|
+
const schema = getAudioEditorSchema(document.type);
|
|
90
|
+
if (!schema || (document.schemaVersion ?? 1) > schema.version)
|
|
91
|
+
throw editorError("unsupported-schema", `Unsupported document schema ${document.type}.`);
|
|
92
|
+
let value = clone(document.value);
|
|
93
|
+
if (value.$extends !== void 0) {
|
|
94
|
+
if (typeof value.$extends !== "string")
|
|
95
|
+
throw editorError("invalid-document", "$extends must be a host document ID.", "/$extends");
|
|
96
|
+
const parent = await documents.read(value.$extends, signal);
|
|
97
|
+
if (parent.type !== document.type)
|
|
98
|
+
throw editorError("invalid-document", "Inherited document type does not match.", "/$extends");
|
|
99
|
+
const inherited = await materializeAudioDocument(parent, documents, signal, [
|
|
100
|
+
...chain,
|
|
101
|
+
document.id
|
|
102
|
+
]);
|
|
103
|
+
value = merge(inherited.value, value);
|
|
104
|
+
delete value.$extends;
|
|
105
|
+
}
|
|
106
|
+
const known = knownFields(schema.fields, value);
|
|
107
|
+
const normalized = schema.serialize(schema.deserialize(known));
|
|
108
|
+
return { ...document, value: normalized };
|
|
109
|
+
}
|
|
110
|
+
async function validateAudioDocument(document, documents, assets, signal) {
|
|
111
|
+
try {
|
|
112
|
+
const effective = await materializeAudioDocument(document, documents, signal);
|
|
113
|
+
const schema = getAudioEditorSchema(document.type);
|
|
114
|
+
const errors = schema.importer.check?.(effective.value, () => void 0) ?? [];
|
|
115
|
+
const references = audioDocumentReferences(schema.fields, effective.value);
|
|
116
|
+
for (const ref of references) {
|
|
117
|
+
try {
|
|
118
|
+
const asset = await assets.get(ref.id, signal);
|
|
119
|
+
if (asset.type !== ref.type)
|
|
120
|
+
errors.push(
|
|
121
|
+
editorError(
|
|
122
|
+
"invalid-document",
|
|
123
|
+
`Expected ${ref.type}, received ${asset.type}.`,
|
|
124
|
+
ref.path
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
} catch (cause) {
|
|
128
|
+
errors.push(
|
|
129
|
+
editorError(
|
|
130
|
+
"asset-unavailable",
|
|
131
|
+
`The host cannot resolve asset ${ref.id}.`,
|
|
132
|
+
ref.path,
|
|
133
|
+
cause
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { effective, errors };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return { errors: [asEditorError(error)] };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
asEditorError,
|
|
145
|
+
audioDocumentReferences,
|
|
146
|
+
audioEditorTypes,
|
|
147
|
+
clone,
|
|
148
|
+
editorError,
|
|
149
|
+
equal,
|
|
150
|
+
getAudioEditorSchema,
|
|
151
|
+
knownFields,
|
|
152
|
+
materializeAudioDocument,
|
|
153
|
+
merge,
|
|
154
|
+
validateAudioDocument
|
|
155
|
+
};
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Scoped, host-overridable tokens. No stylesheet is installed until a view is mounted. */
|
|
2
|
+
export declare const audioEditorStyles = "\n:host {\n display:block; min-width:0; min-height:0; height:100%;\n --ae-background:#15191c; --ae-surface:#1d2327; --ae-input:#111619;\n --ae-text:#e5ecef; --ae-muted:#9aaab2; --ae-border:#344148;\n --ae-accent:#7ce2c3; --ae-danger:#ff9ca5; --ae-radius:10px;\n --ae-font:ui-sans-serif,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n --ae-space:10px; --ae-control-height:32px; --ae-on-accent:#102d25;\n --ae-hover:#293339; --ae-subtle:#232d32; --ae-track:#7ce2c3;\n color:var(--ae-text); font:12px/1.5 var(--ae-font); color-scheme:dark;\n -webkit-font-smoothing:antialiased;\n}\n:host([data-theme=\"light\"]) {\n --ae-background:#f3f6f7; --ae-surface:#fff; --ae-input:#f8fafb;\n --ae-text:#203039; --ae-muted:#556b77; --ae-border:#cbd5db;\n --ae-accent:#08765c; --ae-danger:#b22e43; --ae-on-accent:#fff;\n --ae-hover:#e7edef; --ae-subtle:#e9eff1; --ae-track:#08765c; color-scheme:light;\n}\n* { box-sizing:border-box }\n[hidden] { display:none!important }\n.editor { container-type:inline-size; display:flex; flex-direction:column; height:100%; min-height:160px; min-width:0; background:var(--ae-background); overflow:hidden; border:1px solid var(--ae-border); border-radius:var(--ae-radius) }\n.toolbar,.tabs,.actions { display:flex; flex-wrap:wrap; align-items:center; gap:var(--ae-space) }\n.toolbar { padding:14px 16px; gap:8px; border-bottom:1px solid var(--ae-border); background:var(--ae-surface); flex-shrink:0 }\n.document-identity { display:flex; align-items:center; gap:10px; flex:1 1 150px; min-width:0; margin-inline-end:8px }\n.document-mark { width:32px; height:32px; display:grid; place-items:center; color:var(--ae-accent); background:color-mix(in srgb,var(--ae-accent) 10%,transparent); border:1px solid color-mix(in srgb,var(--ae-accent) 22%,transparent); border-radius:8px; flex-shrink:0 }\n.document-text { min-width:0 }\n.document-kind,.eyebrow { display:block; font-size:9px; font-weight:650; letter-spacing:.13em; text-transform:uppercase; color:var(--ae-muted) }\n.title { display:block; font-size:13px; font-weight:650; overflow-wrap:anywhere; letter-spacing:-.01em }\n.document-identity[data-dirty=true] .title::after { content:' \u2022'; color:var(--ae-accent) }\n.toolbar button { flex-shrink:0 }\n.toolbar button[data-command=Undo],.toolbar button[data-command=Redo] { padding-inline:7px; background:transparent; border-color:transparent; color:var(--ae-muted) }\n.toolbar button[data-command=Play] { background:var(--ae-accent); border-color:var(--ae-accent); color:var(--ae-on-accent); font-weight:700; padding-inline:14px }\n.toolbar button[data-command=Play]:hover:not(:disabled) { filter:brightness(1.08) }\n.toolbar button[data-command=Stop] { background:transparent }\n.tabs { padding:0 12px; gap:0; border-bottom:1px solid var(--ae-border); flex-shrink:0; background:var(--ae-surface) }\n.tabs button { flex:1 1 auto; border:0; border-radius:0; background:transparent; color:var(--ae-muted); padding:12px 8px; position:relative; font-weight:550; min-height:44px }\n.tabs button .icon { width:15px; height:15px }\n.tabs button[aria-selected=true] { color:var(--ae-accent); background:color-mix(in srgb,var(--ae-accent) 6%,transparent) }\n.tabs button[aria-selected=true]::after { content:''; position:absolute; bottom:0; inset-inline:12px; height:2px; background:var(--ae-accent); border-radius:2px }\n.body { overflow:auto; flex:1; min-height:0; padding:20px; scrollbar-gutter:stable; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent }\n.panel { min-width:0 }\nh2,h3,p { margin:0 0 var(--ae-space) }\nh2 { font-size:18px; font-weight:650; letter-spacing:-.03em } h3 { font-size:13px; font-weight:650 }\nbutton,input,select,textarea { font:inherit; color:inherit; max-width:100%; min-width:0 }\nbutton,select,input,textarea { border:1px solid var(--ae-border); border-radius:5px; background:var(--ae-input); padding:6px 9px }\nbutton,select,input:not([type=checkbox]) { min-height:var(--ae-control-height) }\nbutton { display:inline-flex; align-items:center; justify-content:center; gap:7px; cursor:pointer; white-space:normal; overflow-wrap:anywhere; line-height:1.35; transition:background-color .12s,border-color .12s }\nbutton:hover:not(:disabled) { background:var(--ae-hover); border-color:color-mix(in srgb,var(--ae-muted) 65%,var(--ae-border)) }\nbutton:active:not(:disabled) { background:var(--ae-subtle) }\nbutton:disabled { cursor:default; opacity:.42 }\nbutton.primary { border-color:var(--ae-accent); color:var(--ae-accent) }\nbutton[data-intent=danger] { color:var(--ae-muted); background:transparent; border-color:transparent }\nbutton[data-intent=danger]:hover:not(:disabled) { color:var(--ae-danger); background:color-mix(in srgb,var(--ae-danger) 8%,transparent) }\nbutton[data-intent=add] { color:var(--ae-accent); border-style:dashed; background:transparent; margin-top:6px }\n:focus-visible { outline:2px solid var(--ae-accent); outline-offset:3px }\n.icon { display:block; width:16px; height:16px; flex-shrink:0; fill:none; stroke:currentColor; stroke-width:1.65; stroke-linecap:round; stroke-linejoin:round }\ninput[type=checkbox] { width:17px; height:17px; accent-color:var(--ae-accent) }\ninput[type=range] { width:100%; accent-color:var(--ae-accent); padding:0; background:transparent; border:0; cursor:pointer }\ninput[type=number] { font-variant-numeric:tabular-nums; font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-size:11px }\ntextarea { width:100%; min-height:110px; resize:vertical; font:11px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace }\nlabel,.field { display:flex; flex-direction:column; gap:5px; min-width:0 }\n.field { margin:10px 0 }\n.field .field { margin:0 }\n.field-label { color:var(--ae-muted); font-size:11px; overflow-wrap:anywhere }\n.field input,.field select { width:100% }\n.field input[type=checkbox] { width:17px }\n.field>button { align-self:flex-start; min-height:24px; font-size:10px; padding:3px 6px }\n.authoring-layout { display:grid; grid-template-columns:minmax(0,1fr); gap:24px; align-items:start }\n.overview,.inspector { min-width:0 }\n.overview-heading { display:flex; flex-direction:column; align-items:flex-start; gap:12px; margin-bottom:22px }\n.overview-heading .eyebrow { color:var(--ae-accent); margin-bottom:4px }\n.overview-heading p { color:var(--ae-muted); margin:0; max-width:48ch; font-size:11px }\n.overview-heading h2 { margin-bottom:5px }\n.overview-count { color:var(--ae-muted); white-space:nowrap; font:10px/1.5 ui-monospace,monospace; padding-top:5px }\n.inspector { padding:16px; background:var(--ae-surface); border:1px solid var(--ae-border); border-radius:8px }\n.inspector-heading { padding-bottom:12px; margin-bottom:4px; border-bottom:1px solid var(--ae-border) }\n.inspector-heading h3 { margin:2px 0 0; font-size:13px }\n.properties { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr)); gap:0 16px; align-items:start }\n.properties>details { grid-column:1/-1 }\n.field-inline { display:flex; flex-wrap:wrap; align-items:center; gap:6px }\n.field-inline>* { flex:1 1 70px }\n.field-inline button { flex:0 1 auto }\n.hint,.status { color:var(--ae-muted); font-size:11px; overflow-wrap:anywhere }\n.status { padding:9px 14px; border-top:1px solid var(--ae-border); flex-shrink:0; background:var(--ae-surface); font-size:10px; line-height:1.6 }\n.status[data-state=playing] { border-top-color:color-mix(in srgb,var(--ae-accent) 55%,var(--ae-border)) }\n.error { color:var(--ae-danger); white-space:pre-wrap; overflow-wrap:anywhere }\n.notice { padding:12px; border:1px solid var(--ae-border); border-radius:6px; margin-bottom:var(--ae-space); background:var(--ae-surface) }\ndetails { margin:14px 0; border:0; border-top:1px solid var(--ae-border); padding:12px 0 0; min-width:0 }\ndetails details { margin:8px 0; border:0; padding:6px 0 6px 10px; border-inline-start:2px solid var(--ae-border) }\nsummary { cursor:pointer; overflow-wrap:anywhere; font-weight:600; min-height:26px; font-size:11px; color:var(--ae-text) }\nsummary::marker { color:var(--ae-muted); font-size:10px }\nsummary:hover { color:var(--ae-accent) }\n.list-item { padding:10px 0; border-bottom:1px solid var(--ae-border); min-width:0 }\n.list-item:last-child { border:0 }\n.item-title { display:flex; flex-wrap:wrap; gap:4px; align-items:center; margin-bottom:4px }\n.item-title span { overflow-wrap:anywhere; flex:1; font-weight:600 }\n.item-title button { padding:3px 5px; min-height:24px; font-size:10px }\n.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,230px),1fr)); gap:var(--ae-space) }\n.card { padding:14px; border:1px solid var(--ae-border); border-radius:7px; background:var(--ae-surface); min-width:0; margin-bottom:10px }\n.mixer-strips { display:grid; grid-auto-flow:column; grid-auto-columns:minmax(124px,1fr); overflow-x:auto; padding-bottom:8px; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent; gap:8px; margin-bottom:20px }\n.mixer-strip { background:var(--ae-surface); border:1px solid var(--ae-border); border-top:3px solid var(--ae-track); border-radius:6px; padding:14px 10px; min-width:0; text-align:center }\n.mixer-strip:nth-child(3n+2),.wave-lane:nth-child(3n+2) { --ae-track:#ab9ef0 }\n.mixer-strip:nth-child(3n+3),.wave-lane:nth-child(3n+3) { --ae-track:#e8b97b }\n:host([data-theme=light]) .mixer-strip:nth-child(3n+2),:host([data-theme=light]) .wave-lane:nth-child(3n+2) { --ae-track:#7059b5 }\n:host([data-theme=light]) .mixer-strip:nth-child(3n+3),:host([data-theme=light]) .wave-lane:nth-child(3n+3) { --ae-track:#936424 }\n.mixer-strip h3 { margin:0 0 3px; font-size:13px; overflow-wrap:anywhere }\n.bus-route { color:var(--ae-muted); font-size:10px; display:block; margin-bottom:12px }\n.fader-row { display:flex; flex-direction:column; align-items:center; gap:8px; min-width:0; padding-block:8px }\n.fader-row input[type=range] { writing-mode:vertical-lr; direction:rtl; width:24px; height:144px; min-height:0; accent-color:var(--ae-track) }\n.fader-row input[type=number] { width:70px; text-align:center }\n.fader-row>.hint { margin-top:-6px; font-size:9px; text-transform:uppercase }\n.meter-caption { display:block; font-size:9px; line-height:1.4; margin-top:5px; min-height:14px }\n.event-waves { display:grid; gap:12px; margin-bottom:20px }\n.wave-lane { position:relative; min-width:0; background:var(--ae-surface); border:1px solid var(--ae-border); border-radius:7px; overflow:hidden }\n.track-header { display:flex; gap:10px; align-items:center; padding:12px; border-bottom:1px solid var(--ae-border) }\n.track-number { font:10px ui-monospace,monospace; color:var(--ae-track); background:color-mix(in srgb,var(--ae-track) 10%,transparent); padding:5px 6px; border-radius:4px }\n.track-name { flex:1; min-width:0; font-size:12px; font-weight:600; overflow-wrap:anywhere }\n.track-name { justify-content:flex-start; padding:0; min-height:24px; background:transparent; border-color:transparent; text-align:start }\n.track-header .badge { color:var(--ae-track) }\n.wave-lane canvas { height:90px; border-radius:0; background:repeating-linear-gradient(90deg,transparent 0,transparent calc(25% - 1px),var(--ae-subtle) 25%),var(--ae-input) }\n.track-meta { display:flex; flex-wrap:wrap; gap:6px 14px; padding:9px 12px; color:var(--ae-muted); font:10px/1.5 ui-monospace,monospace; border-top:1px solid var(--ae-border) }\n.track-meta span:last-child { margin-inline-start:auto; color:var(--ae-track) }\n.timing-ruler { display:flex; justify-content:space-between; color:var(--ae-muted); font:10px ui-monospace,monospace; margin-bottom:10px }\n.timing-row { margin-bottom:12px }\n.timing-track { position:relative; height:12px; margin-top:4px; background:var(--ae-input); border-radius:3px }\n.timing-mark { position:absolute; height:100%; background:var(--ae-accent); border-radius:3px; max-width:100% }\n.timing-mark[data-source-length=true] { transform:translateX(-50%); border-radius:50% }\n.wave-placeholder { display:grid; place-items:center; min-height:90px; color:var(--ae-muted); font-size:11px; background:var(--ae-input) }\n.catalog { max-height:440px; overflow:auto; position:relative; margin-block:16px; border:1px solid var(--ae-border); border-radius:7px; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent }\n.catalog-row { display:flex; align-items:center; gap:10px; min-height:64px; height:64px; padding:8px 12px; overflow:hidden; border-bottom:1px solid var(--ae-border); background:var(--ae-surface) }\n.catalog-row:last-child { border-bottom:0 }\n.catalog-row:hover { background:var(--ae-hover) }\n.catalog-row[data-selected=true] { background:color-mix(in srgb,var(--ae-accent) 8%,var(--ae-surface)); box-shadow:inset 3px 0 var(--ae-accent) }\n.catalog-symbol { color:var(--ae-accent); background:var(--ae-subtle); padding:8px; border-radius:6px; flex-shrink:0 }\n.catalog-row button { flex:1; justify-content:flex-start; text-align:start; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; background:transparent; border-color:transparent; padding:4px; font-weight:550 }\n.badge { display:inline-block; color:var(--ae-muted); background:var(--ae-subtle); border-radius:4px; padding:3px 6px; font-size:9px; font-weight:500; white-space:nowrap }\ncanvas { display:block; width:100%; height:80px; background:var(--ae-input); border-radius:6px }\nmeter { display:block; width:100%; height:7px; accent-color:var(--ae-track); border:0; border-radius:3px; overflow:hidden; background:var(--ae-input) }\nmeter::-webkit-meter-bar { background:var(--ae-input); border:0; border-radius:3px; height:7px }\nmeter::-webkit-meter-optimum-value { background:var(--ae-track) }\nmeter::-moz-meter-bar { background:var(--ae-track) }\npre { white-space:pre-wrap; overflow-wrap:anywhere; max-height:320px; overflow:auto; font:10px/1.7 ui-monospace,monospace; padding:12px; background:var(--ae-input); border-radius:6px }\n.empty { padding:28px 12px; color:var(--ae-muted); text-align:center }\n.music-section { border-inline-start:3px solid var(--ae-accent) }\n.music-section:nth-of-type(2n) { border-inline-start-color:#ab9ef0 }\n.music-section h3 { font-size:14px; letter-spacing:-.02em }\n.music-section .hint { padding:8px 0; margin:0; border-bottom:1px solid var(--ae-border); font-family:ui-monospace,monospace; font-size:10px }\n.sr-only { position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0 }\n@container (min-width:760px) {\n .authoring-layout { grid-template-columns:minmax(0,1fr) 280px; gap:24px }\n .inspector { align-self:start }\n .overview { position:sticky; top:0 }\n}\n@container (max-width:540px) {\n .toolbar { padding:12px; gap:6px }\n .document-identity { flex-basis:100%; margin:0 0 5px }\n .toolbar button { flex:1 1 auto; padding-inline:8px }\n .toolbar button[data-command=Undo],.toolbar button[data-command=Redo],.toolbar button[data-command=Stop] { flex:0 0 auto; padding-inline:6px }\n .toolbar button[data-command=Stop] { width:32px }\n .toolbar button[data-command=Undo] .button-label,.toolbar button[data-command=Redo] .button-label,.toolbar button[data-command=Stop] .button-label { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%) }\n .tabs { padding:0 6px }\n .tabs button { flex:1 1 30%; padding:9px 5px; font-size:10px; min-height:36px; gap:5px }\n .tabs button .icon { width:13px; height:13px }\n .body { padding:14px }\n .authoring-layout { gap:20px }\n .overview-heading { margin-bottom:16px }\n .overview-heading h2 { font-size:17px }\n .overview-count { display:none }\n .inspector { padding:12px }\n .mixer-strips { grid-auto-flow:row; grid-auto-columns:auto; grid-template-columns:minmax(0,1fr); gap:8px; overflow:visible }\n .mixer-strip { text-align:start; padding:12px; border-top:1px solid var(--ae-border); border-inline-start:3px solid var(--ae-track) }\n .mixer-strip h3 { font-size:12px }\n .bus-route { margin-bottom:6px }\n .fader-row { flex-direction:row; padding-block:2px; gap:8px }\n .fader-row input[type=range] { writing-mode:horizontal-tb; direction:ltr; width:100%; height:26px; flex:1; min-width:40px }\n .fader-row input[type=number] { width:58px; flex:0 0 58px }\n .fader-row>.hint { margin:0; font-size:9px }\n .meter-caption { font-size:9px }\n .catalog-row { padding-inline:8px; gap:6px }\n .catalog-symbol { padding:6px }\n .catalog-row .badge { font-size:8px; padding:3px 4px }\n .field-inline { align-items:stretch }\n .track-header { padding:10px }\n .track-meta { padding:8px 10px; gap:5px 8px; font-size:9px }\n .status { padding:8px 12px; font-size:9px }\n}\n@media (prefers-reduced-motion:reduce) { * { scroll-behavior:auto!important; animation:none!important; transition:none!important } }\n@media (forced-colors:active) { button,input,select,details,.card,.mixer-strip,.wave-lane { border:1px solid ButtonText } .icon { stroke:ButtonText } }\n";
|
package/dist/styles.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const audioEditorStyles = `
|
|
2
|
+
:host {
|
|
3
|
+
display:block; min-width:0; min-height:0; height:100%;
|
|
4
|
+
--ae-background:#15191c; --ae-surface:#1d2327; --ae-input:#111619;
|
|
5
|
+
--ae-text:#e5ecef; --ae-muted:#9aaab2; --ae-border:#344148;
|
|
6
|
+
--ae-accent:#7ce2c3; --ae-danger:#ff9ca5; --ae-radius:10px;
|
|
7
|
+
--ae-font:ui-sans-serif,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
|
|
8
|
+
--ae-space:10px; --ae-control-height:32px; --ae-on-accent:#102d25;
|
|
9
|
+
--ae-hover:#293339; --ae-subtle:#232d32; --ae-track:#7ce2c3;
|
|
10
|
+
color:var(--ae-text); font:12px/1.5 var(--ae-font); color-scheme:dark;
|
|
11
|
+
-webkit-font-smoothing:antialiased;
|
|
12
|
+
}
|
|
13
|
+
:host([data-theme="light"]) {
|
|
14
|
+
--ae-background:#f3f6f7; --ae-surface:#fff; --ae-input:#f8fafb;
|
|
15
|
+
--ae-text:#203039; --ae-muted:#556b77; --ae-border:#cbd5db;
|
|
16
|
+
--ae-accent:#08765c; --ae-danger:#b22e43; --ae-on-accent:#fff;
|
|
17
|
+
--ae-hover:#e7edef; --ae-subtle:#e9eff1; --ae-track:#08765c; color-scheme:light;
|
|
18
|
+
}
|
|
19
|
+
* { box-sizing:border-box }
|
|
20
|
+
[hidden] { display:none!important }
|
|
21
|
+
.editor { container-type:inline-size; display:flex; flex-direction:column; height:100%; min-height:160px; min-width:0; background:var(--ae-background); overflow:hidden; border:1px solid var(--ae-border); border-radius:var(--ae-radius) }
|
|
22
|
+
.toolbar,.tabs,.actions { display:flex; flex-wrap:wrap; align-items:center; gap:var(--ae-space) }
|
|
23
|
+
.toolbar { padding:14px 16px; gap:8px; border-bottom:1px solid var(--ae-border); background:var(--ae-surface); flex-shrink:0 }
|
|
24
|
+
.document-identity { display:flex; align-items:center; gap:10px; flex:1 1 150px; min-width:0; margin-inline-end:8px }
|
|
25
|
+
.document-mark { width:32px; height:32px; display:grid; place-items:center; color:var(--ae-accent); background:color-mix(in srgb,var(--ae-accent) 10%,transparent); border:1px solid color-mix(in srgb,var(--ae-accent) 22%,transparent); border-radius:8px; flex-shrink:0 }
|
|
26
|
+
.document-text { min-width:0 }
|
|
27
|
+
.document-kind,.eyebrow { display:block; font-size:9px; font-weight:650; letter-spacing:.13em; text-transform:uppercase; color:var(--ae-muted) }
|
|
28
|
+
.title { display:block; font-size:13px; font-weight:650; overflow-wrap:anywhere; letter-spacing:-.01em }
|
|
29
|
+
.document-identity[data-dirty=true] .title::after { content:' \u2022'; color:var(--ae-accent) }
|
|
30
|
+
.toolbar button { flex-shrink:0 }
|
|
31
|
+
.toolbar button[data-command=Undo],.toolbar button[data-command=Redo] { padding-inline:7px; background:transparent; border-color:transparent; color:var(--ae-muted) }
|
|
32
|
+
.toolbar button[data-command=Play] { background:var(--ae-accent); border-color:var(--ae-accent); color:var(--ae-on-accent); font-weight:700; padding-inline:14px }
|
|
33
|
+
.toolbar button[data-command=Play]:hover:not(:disabled) { filter:brightness(1.08) }
|
|
34
|
+
.toolbar button[data-command=Stop] { background:transparent }
|
|
35
|
+
.tabs { padding:0 12px; gap:0; border-bottom:1px solid var(--ae-border); flex-shrink:0; background:var(--ae-surface) }
|
|
36
|
+
.tabs button { flex:1 1 auto; border:0; border-radius:0; background:transparent; color:var(--ae-muted); padding:12px 8px; position:relative; font-weight:550; min-height:44px }
|
|
37
|
+
.tabs button .icon { width:15px; height:15px }
|
|
38
|
+
.tabs button[aria-selected=true] { color:var(--ae-accent); background:color-mix(in srgb,var(--ae-accent) 6%,transparent) }
|
|
39
|
+
.tabs button[aria-selected=true]::after { content:''; position:absolute; bottom:0; inset-inline:12px; height:2px; background:var(--ae-accent); border-radius:2px }
|
|
40
|
+
.body { overflow:auto; flex:1; min-height:0; padding:20px; scrollbar-gutter:stable; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent }
|
|
41
|
+
.panel { min-width:0 }
|
|
42
|
+
h2,h3,p { margin:0 0 var(--ae-space) }
|
|
43
|
+
h2 { font-size:18px; font-weight:650; letter-spacing:-.03em } h3 { font-size:13px; font-weight:650 }
|
|
44
|
+
button,input,select,textarea { font:inherit; color:inherit; max-width:100%; min-width:0 }
|
|
45
|
+
button,select,input,textarea { border:1px solid var(--ae-border); border-radius:5px; background:var(--ae-input); padding:6px 9px }
|
|
46
|
+
button,select,input:not([type=checkbox]) { min-height:var(--ae-control-height) }
|
|
47
|
+
button { display:inline-flex; align-items:center; justify-content:center; gap:7px; cursor:pointer; white-space:normal; overflow-wrap:anywhere; line-height:1.35; transition:background-color .12s,border-color .12s }
|
|
48
|
+
button:hover:not(:disabled) { background:var(--ae-hover); border-color:color-mix(in srgb,var(--ae-muted) 65%,var(--ae-border)) }
|
|
49
|
+
button:active:not(:disabled) { background:var(--ae-subtle) }
|
|
50
|
+
button:disabled { cursor:default; opacity:.42 }
|
|
51
|
+
button.primary { border-color:var(--ae-accent); color:var(--ae-accent) }
|
|
52
|
+
button[data-intent=danger] { color:var(--ae-muted); background:transparent; border-color:transparent }
|
|
53
|
+
button[data-intent=danger]:hover:not(:disabled) { color:var(--ae-danger); background:color-mix(in srgb,var(--ae-danger) 8%,transparent) }
|
|
54
|
+
button[data-intent=add] { color:var(--ae-accent); border-style:dashed; background:transparent; margin-top:6px }
|
|
55
|
+
:focus-visible { outline:2px solid var(--ae-accent); outline-offset:3px }
|
|
56
|
+
.icon { display:block; width:16px; height:16px; flex-shrink:0; fill:none; stroke:currentColor; stroke-width:1.65; stroke-linecap:round; stroke-linejoin:round }
|
|
57
|
+
input[type=checkbox] { width:17px; height:17px; accent-color:var(--ae-accent) }
|
|
58
|
+
input[type=range] { width:100%; accent-color:var(--ae-accent); padding:0; background:transparent; border:0; cursor:pointer }
|
|
59
|
+
input[type=number] { font-variant-numeric:tabular-nums; font-family:ui-monospace,SFMono-Regular,Consolas,monospace; font-size:11px }
|
|
60
|
+
textarea { width:100%; min-height:110px; resize:vertical; font:11px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace }
|
|
61
|
+
label,.field { display:flex; flex-direction:column; gap:5px; min-width:0 }
|
|
62
|
+
.field { margin:10px 0 }
|
|
63
|
+
.field .field { margin:0 }
|
|
64
|
+
.field-label { color:var(--ae-muted); font-size:11px; overflow-wrap:anywhere }
|
|
65
|
+
.field input,.field select { width:100% }
|
|
66
|
+
.field input[type=checkbox] { width:17px }
|
|
67
|
+
.field>button { align-self:flex-start; min-height:24px; font-size:10px; padding:3px 6px }
|
|
68
|
+
.authoring-layout { display:grid; grid-template-columns:minmax(0,1fr); gap:24px; align-items:start }
|
|
69
|
+
.overview,.inspector { min-width:0 }
|
|
70
|
+
.overview-heading { display:flex; flex-direction:column; align-items:flex-start; gap:12px; margin-bottom:22px }
|
|
71
|
+
.overview-heading .eyebrow { color:var(--ae-accent); margin-bottom:4px }
|
|
72
|
+
.overview-heading p { color:var(--ae-muted); margin:0; max-width:48ch; font-size:11px }
|
|
73
|
+
.overview-heading h2 { margin-bottom:5px }
|
|
74
|
+
.overview-count { color:var(--ae-muted); white-space:nowrap; font:10px/1.5 ui-monospace,monospace; padding-top:5px }
|
|
75
|
+
.inspector { padding:16px; background:var(--ae-surface); border:1px solid var(--ae-border); border-radius:8px }
|
|
76
|
+
.inspector-heading { padding-bottom:12px; margin-bottom:4px; border-bottom:1px solid var(--ae-border) }
|
|
77
|
+
.inspector-heading h3 { margin:2px 0 0; font-size:13px }
|
|
78
|
+
.properties { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr)); gap:0 16px; align-items:start }
|
|
79
|
+
.properties>details { grid-column:1/-1 }
|
|
80
|
+
.field-inline { display:flex; flex-wrap:wrap; align-items:center; gap:6px }
|
|
81
|
+
.field-inline>* { flex:1 1 70px }
|
|
82
|
+
.field-inline button { flex:0 1 auto }
|
|
83
|
+
.hint,.status { color:var(--ae-muted); font-size:11px; overflow-wrap:anywhere }
|
|
84
|
+
.status { padding:9px 14px; border-top:1px solid var(--ae-border); flex-shrink:0; background:var(--ae-surface); font-size:10px; line-height:1.6 }
|
|
85
|
+
.status[data-state=playing] { border-top-color:color-mix(in srgb,var(--ae-accent) 55%,var(--ae-border)) }
|
|
86
|
+
.error { color:var(--ae-danger); white-space:pre-wrap; overflow-wrap:anywhere }
|
|
87
|
+
.notice { padding:12px; border:1px solid var(--ae-border); border-radius:6px; margin-bottom:var(--ae-space); background:var(--ae-surface) }
|
|
88
|
+
details { margin:14px 0; border:0; border-top:1px solid var(--ae-border); padding:12px 0 0; min-width:0 }
|
|
89
|
+
details details { margin:8px 0; border:0; padding:6px 0 6px 10px; border-inline-start:2px solid var(--ae-border) }
|
|
90
|
+
summary { cursor:pointer; overflow-wrap:anywhere; font-weight:600; min-height:26px; font-size:11px; color:var(--ae-text) }
|
|
91
|
+
summary::marker { color:var(--ae-muted); font-size:10px }
|
|
92
|
+
summary:hover { color:var(--ae-accent) }
|
|
93
|
+
.list-item { padding:10px 0; border-bottom:1px solid var(--ae-border); min-width:0 }
|
|
94
|
+
.list-item:last-child { border:0 }
|
|
95
|
+
.item-title { display:flex; flex-wrap:wrap; gap:4px; align-items:center; margin-bottom:4px }
|
|
96
|
+
.item-title span { overflow-wrap:anywhere; flex:1; font-weight:600 }
|
|
97
|
+
.item-title button { padding:3px 5px; min-height:24px; font-size:10px }
|
|
98
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,230px),1fr)); gap:var(--ae-space) }
|
|
99
|
+
.card { padding:14px; border:1px solid var(--ae-border); border-radius:7px; background:var(--ae-surface); min-width:0; margin-bottom:10px }
|
|
100
|
+
.mixer-strips { display:grid; grid-auto-flow:column; grid-auto-columns:minmax(124px,1fr); overflow-x:auto; padding-bottom:8px; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent; gap:8px; margin-bottom:20px }
|
|
101
|
+
.mixer-strip { background:var(--ae-surface); border:1px solid var(--ae-border); border-top:3px solid var(--ae-track); border-radius:6px; padding:14px 10px; min-width:0; text-align:center }
|
|
102
|
+
.mixer-strip:nth-child(3n+2),.wave-lane:nth-child(3n+2) { --ae-track:#ab9ef0 }
|
|
103
|
+
.mixer-strip:nth-child(3n+3),.wave-lane:nth-child(3n+3) { --ae-track:#e8b97b }
|
|
104
|
+
:host([data-theme=light]) .mixer-strip:nth-child(3n+2),:host([data-theme=light]) .wave-lane:nth-child(3n+2) { --ae-track:#7059b5 }
|
|
105
|
+
:host([data-theme=light]) .mixer-strip:nth-child(3n+3),:host([data-theme=light]) .wave-lane:nth-child(3n+3) { --ae-track:#936424 }
|
|
106
|
+
.mixer-strip h3 { margin:0 0 3px; font-size:13px; overflow-wrap:anywhere }
|
|
107
|
+
.bus-route { color:var(--ae-muted); font-size:10px; display:block; margin-bottom:12px }
|
|
108
|
+
.fader-row { display:flex; flex-direction:column; align-items:center; gap:8px; min-width:0; padding-block:8px }
|
|
109
|
+
.fader-row input[type=range] { writing-mode:vertical-lr; direction:rtl; width:24px; height:144px; min-height:0; accent-color:var(--ae-track) }
|
|
110
|
+
.fader-row input[type=number] { width:70px; text-align:center }
|
|
111
|
+
.fader-row>.hint { margin-top:-6px; font-size:9px; text-transform:uppercase }
|
|
112
|
+
.meter-caption { display:block; font-size:9px; line-height:1.4; margin-top:5px; min-height:14px }
|
|
113
|
+
.event-waves { display:grid; gap:12px; margin-bottom:20px }
|
|
114
|
+
.wave-lane { position:relative; min-width:0; background:var(--ae-surface); border:1px solid var(--ae-border); border-radius:7px; overflow:hidden }
|
|
115
|
+
.track-header { display:flex; gap:10px; align-items:center; padding:12px; border-bottom:1px solid var(--ae-border) }
|
|
116
|
+
.track-number { font:10px ui-monospace,monospace; color:var(--ae-track); background:color-mix(in srgb,var(--ae-track) 10%,transparent); padding:5px 6px; border-radius:4px }
|
|
117
|
+
.track-name { flex:1; min-width:0; font-size:12px; font-weight:600; overflow-wrap:anywhere }
|
|
118
|
+
.track-name { justify-content:flex-start; padding:0; min-height:24px; background:transparent; border-color:transparent; text-align:start }
|
|
119
|
+
.track-header .badge { color:var(--ae-track) }
|
|
120
|
+
.wave-lane canvas { height:90px; border-radius:0; background:repeating-linear-gradient(90deg,transparent 0,transparent calc(25% - 1px),var(--ae-subtle) 25%),var(--ae-input) }
|
|
121
|
+
.track-meta { display:flex; flex-wrap:wrap; gap:6px 14px; padding:9px 12px; color:var(--ae-muted); font:10px/1.5 ui-monospace,monospace; border-top:1px solid var(--ae-border) }
|
|
122
|
+
.track-meta span:last-child { margin-inline-start:auto; color:var(--ae-track) }
|
|
123
|
+
.timing-ruler { display:flex; justify-content:space-between; color:var(--ae-muted); font:10px ui-monospace,monospace; margin-bottom:10px }
|
|
124
|
+
.timing-row { margin-bottom:12px }
|
|
125
|
+
.timing-track { position:relative; height:12px; margin-top:4px; background:var(--ae-input); border-radius:3px }
|
|
126
|
+
.timing-mark { position:absolute; height:100%; background:var(--ae-accent); border-radius:3px; max-width:100% }
|
|
127
|
+
.timing-mark[data-source-length=true] { transform:translateX(-50%); border-radius:50% }
|
|
128
|
+
.wave-placeholder { display:grid; place-items:center; min-height:90px; color:var(--ae-muted); font-size:11px; background:var(--ae-input) }
|
|
129
|
+
.catalog { max-height:440px; overflow:auto; position:relative; margin-block:16px; border:1px solid var(--ae-border); border-radius:7px; scrollbar-width:thin; scrollbar-color:var(--ae-border) transparent }
|
|
130
|
+
.catalog-row { display:flex; align-items:center; gap:10px; min-height:64px; height:64px; padding:8px 12px; overflow:hidden; border-bottom:1px solid var(--ae-border); background:var(--ae-surface) }
|
|
131
|
+
.catalog-row:last-child { border-bottom:0 }
|
|
132
|
+
.catalog-row:hover { background:var(--ae-hover) }
|
|
133
|
+
.catalog-row[data-selected=true] { background:color-mix(in srgb,var(--ae-accent) 8%,var(--ae-surface)); box-shadow:inset 3px 0 var(--ae-accent) }
|
|
134
|
+
.catalog-symbol { color:var(--ae-accent); background:var(--ae-subtle); padding:8px; border-radius:6px; flex-shrink:0 }
|
|
135
|
+
.catalog-row button { flex:1; justify-content:flex-start; text-align:start; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; background:transparent; border-color:transparent; padding:4px; font-weight:550 }
|
|
136
|
+
.badge { display:inline-block; color:var(--ae-muted); background:var(--ae-subtle); border-radius:4px; padding:3px 6px; font-size:9px; font-weight:500; white-space:nowrap }
|
|
137
|
+
canvas { display:block; width:100%; height:80px; background:var(--ae-input); border-radius:6px }
|
|
138
|
+
meter { display:block; width:100%; height:7px; accent-color:var(--ae-track); border:0; border-radius:3px; overflow:hidden; background:var(--ae-input) }
|
|
139
|
+
meter::-webkit-meter-bar { background:var(--ae-input); border:0; border-radius:3px; height:7px }
|
|
140
|
+
meter::-webkit-meter-optimum-value { background:var(--ae-track) }
|
|
141
|
+
meter::-moz-meter-bar { background:var(--ae-track) }
|
|
142
|
+
pre { white-space:pre-wrap; overflow-wrap:anywhere; max-height:320px; overflow:auto; font:10px/1.7 ui-monospace,monospace; padding:12px; background:var(--ae-input); border-radius:6px }
|
|
143
|
+
.empty { padding:28px 12px; color:var(--ae-muted); text-align:center }
|
|
144
|
+
.music-section { border-inline-start:3px solid var(--ae-accent) }
|
|
145
|
+
.music-section:nth-of-type(2n) { border-inline-start-color:#ab9ef0 }
|
|
146
|
+
.music-section h3 { font-size:14px; letter-spacing:-.02em }
|
|
147
|
+
.music-section .hint { padding:8px 0; margin:0; border-bottom:1px solid var(--ae-border); font-family:ui-monospace,monospace; font-size:10px }
|
|
148
|
+
.sr-only { position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0 }
|
|
149
|
+
@container (min-width:760px) {
|
|
150
|
+
.authoring-layout { grid-template-columns:minmax(0,1fr) 280px; gap:24px }
|
|
151
|
+
.inspector { align-self:start }
|
|
152
|
+
.overview { position:sticky; top:0 }
|
|
153
|
+
}
|
|
154
|
+
@container (max-width:540px) {
|
|
155
|
+
.toolbar { padding:12px; gap:6px }
|
|
156
|
+
.document-identity { flex-basis:100%; margin:0 0 5px }
|
|
157
|
+
.toolbar button { flex:1 1 auto; padding-inline:8px }
|
|
158
|
+
.toolbar button[data-command=Undo],.toolbar button[data-command=Redo],.toolbar button[data-command=Stop] { flex:0 0 auto; padding-inline:6px }
|
|
159
|
+
.toolbar button[data-command=Stop] { width:32px }
|
|
160
|
+
.toolbar button[data-command=Undo] .button-label,.toolbar button[data-command=Redo] .button-label,.toolbar button[data-command=Stop] .button-label { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%) }
|
|
161
|
+
.tabs { padding:0 6px }
|
|
162
|
+
.tabs button { flex:1 1 30%; padding:9px 5px; font-size:10px; min-height:36px; gap:5px }
|
|
163
|
+
.tabs button .icon { width:13px; height:13px }
|
|
164
|
+
.body { padding:14px }
|
|
165
|
+
.authoring-layout { gap:20px }
|
|
166
|
+
.overview-heading { margin-bottom:16px }
|
|
167
|
+
.overview-heading h2 { font-size:17px }
|
|
168
|
+
.overview-count { display:none }
|
|
169
|
+
.inspector { padding:12px }
|
|
170
|
+
.mixer-strips { grid-auto-flow:row; grid-auto-columns:auto; grid-template-columns:minmax(0,1fr); gap:8px; overflow:visible }
|
|
171
|
+
.mixer-strip { text-align:start; padding:12px; border-top:1px solid var(--ae-border); border-inline-start:3px solid var(--ae-track) }
|
|
172
|
+
.mixer-strip h3 { font-size:12px }
|
|
173
|
+
.bus-route { margin-bottom:6px }
|
|
174
|
+
.fader-row { flex-direction:row; padding-block:2px; gap:8px }
|
|
175
|
+
.fader-row input[type=range] { writing-mode:horizontal-tb; direction:ltr; width:100%; height:26px; flex:1; min-width:40px }
|
|
176
|
+
.fader-row input[type=number] { width:58px; flex:0 0 58px }
|
|
177
|
+
.fader-row>.hint { margin:0; font-size:9px }
|
|
178
|
+
.meter-caption { font-size:9px }
|
|
179
|
+
.catalog-row { padding-inline:8px; gap:6px }
|
|
180
|
+
.catalog-symbol { padding:6px }
|
|
181
|
+
.catalog-row .badge { font-size:8px; padding:3px 4px }
|
|
182
|
+
.field-inline { align-items:stretch }
|
|
183
|
+
.track-header { padding:10px }
|
|
184
|
+
.track-meta { padding:8px 10px; gap:5px 8px; font-size:9px }
|
|
185
|
+
.status { padding:8px 12px; font-size:9px }
|
|
186
|
+
}
|
|
187
|
+
@media (prefers-reduced-motion:reduce) { * { scroll-behavior:auto!important; animation:none!important; transition:none!important } }
|
|
188
|
+
@media (forced-colors:active) { button,input,select,details,.card,.mixer-strip,.wave-lane { border:1px solid ButtonText } .icon { stroke:ButtonText } }
|
|
189
|
+
`;
|
|
190
|
+
export {
|
|
191
|
+
audioEditorStyles
|
|
192
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { ComponentDef, JsonObject, JsonValue, ShardError } from '@aethervtt/shard-core';
|
|
2
|
+
/** IDs and revisions are opaque host values. They are never interpreted as URLs or paths. */
|
|
3
|
+
export interface AudioDocument {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
revision: string;
|
|
8
|
+
schemaVersion?: number;
|
|
9
|
+
value: JsonObject;
|
|
10
|
+
sourceText?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface AudioDocumentChange {
|
|
13
|
+
document: AudioDocument;
|
|
14
|
+
expectedRevision: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface AudioDocumentStore {
|
|
17
|
+
readonly scope: string;
|
|
18
|
+
readonly atomic?: boolean;
|
|
19
|
+
read(id: string, signal?: AbortSignal): Promise<AudioDocument>;
|
|
20
|
+
commit?(changes: readonly AudioDocumentChange[], signal?: AbortSignal): Promise<AudioDocument[]>;
|
|
21
|
+
subscribe?(listener: (ids: readonly string[]) => void): () => void;
|
|
22
|
+
}
|
|
23
|
+
export interface AudioAssetEntry {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
type: string;
|
|
27
|
+
revision: string;
|
|
28
|
+
duration?: number;
|
|
29
|
+
sampleRate?: number;
|
|
30
|
+
channels?: number;
|
|
31
|
+
bytes?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface AudioAssetQuery {
|
|
34
|
+
search?: string;
|
|
35
|
+
type?: string;
|
|
36
|
+
cursor?: string;
|
|
37
|
+
limit?: number;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}
|
|
40
|
+
export interface AudioAssetPage {
|
|
41
|
+
items: AudioAssetEntry[];
|
|
42
|
+
nextCursor?: string;
|
|
43
|
+
total?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface AudioAssetContent {
|
|
46
|
+
bytes?: Uint8Array;
|
|
47
|
+
url?: string;
|
|
48
|
+
mime?: string;
|
|
49
|
+
/** Host-authorized import settings; identity remains the catalog ID. */
|
|
50
|
+
settings?: {
|
|
51
|
+
mode?: 'decoded' | 'stream';
|
|
52
|
+
normalize?: boolean;
|
|
53
|
+
loopStart?: number;
|
|
54
|
+
loopEnd?: number;
|
|
55
|
+
};
|
|
56
|
+
release?(): void;
|
|
57
|
+
}
|
|
58
|
+
export interface AudioAssetCatalog {
|
|
59
|
+
readonly scope: string;
|
|
60
|
+
list(query: AudioAssetQuery): Promise<AudioAssetPage>;
|
|
61
|
+
get(id: string, signal?: AbortSignal): Promise<AudioAssetEntry>;
|
|
62
|
+
resolve(id: string, signal?: AbortSignal): Promise<AudioAssetContent>;
|
|
63
|
+
pick?(type: string, signal?: AbortSignal): Promise<string | undefined>;
|
|
64
|
+
subscribe?(listener: (ids: readonly string[]) => void): () => void;
|
|
65
|
+
}
|
|
66
|
+
export interface AudioWaveform {
|
|
67
|
+
peaks: Float32Array;
|
|
68
|
+
duration: number;
|
|
69
|
+
sampleRate: number;
|
|
70
|
+
}
|
|
71
|
+
export interface AudioPreviewAdapter {
|
|
72
|
+
readonly mode: 'audition' | 'attached';
|
|
73
|
+
readonly target?: string;
|
|
74
|
+
readonly capabilities: Readonly<Record<string, boolean>>;
|
|
75
|
+
play(document: AudioDocument, signal?: AbortSignal): Promise<unknown>;
|
|
76
|
+
stop(): void | Promise<void>;
|
|
77
|
+
inspect(): unknown | Promise<unknown>;
|
|
78
|
+
command?(name: string, params: Record<string, unknown>): unknown | Promise<unknown>;
|
|
79
|
+
waveform?(id: string, bins: number, signal?: AbortSignal): Promise<AudioWaveform>;
|
|
80
|
+
export?(document: AudioDocument, plan: JsonObject, signal?: AbortSignal): Promise<unknown>;
|
|
81
|
+
dispose(): void | Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
export interface AudioEditorState {
|
|
84
|
+
document?: AudioDocument;
|
|
85
|
+
/** Resolved inherited values plus schema defaults, for display only. */
|
|
86
|
+
effective?: JsonObject;
|
|
87
|
+
schema?: ComponentDef;
|
|
88
|
+
dirty: boolean;
|
|
89
|
+
readOnly: boolean;
|
|
90
|
+
loading: boolean;
|
|
91
|
+
saving: boolean;
|
|
92
|
+
conflict?: AudioDocument;
|
|
93
|
+
diagnostics: ShardError[];
|
|
94
|
+
canUndo: boolean;
|
|
95
|
+
canRedo: boolean;
|
|
96
|
+
historyEvicted: number;
|
|
97
|
+
previewState: 'idle' | 'preparing' | 'playing' | 'failed';
|
|
98
|
+
previewResult?: unknown;
|
|
99
|
+
/** Document that owns the current preview result, independent of editor selection. */
|
|
100
|
+
previewDocumentId?: string;
|
|
101
|
+
}
|
|
102
|
+
export interface AudioEditorOptions {
|
|
103
|
+
documents: AudioDocumentStore;
|
|
104
|
+
assets: AudioAssetCatalog;
|
|
105
|
+
preview?: AudioPreviewAdapter;
|
|
106
|
+
historyLimit?: number;
|
|
107
|
+
historyBytes?: number;
|
|
108
|
+
}
|
|
109
|
+
export interface AudioEditorEdit {
|
|
110
|
+
path: string;
|
|
111
|
+
value?: JsonValue;
|
|
112
|
+
remove?: boolean;
|
|
113
|
+
}
|
package/dist/types.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AudioAssetCatalog, AudioWaveform } from './types.js';
|
|
2
|
+
export type AudioWaveformDecoder = (bytes: Uint8Array, signal?: AbortSignal) => Promise<{
|
|
3
|
+
channels: readonly Float32Array[];
|
|
4
|
+
sampleRate: number;
|
|
5
|
+
}>;
|
|
6
|
+
/** Metadata listing never calls this cache. A requested waveform owns only its envelope. */
|
|
7
|
+
export declare function createAudioWaveformCache(assets: AudioAssetCatalog, options?: {
|
|
8
|
+
maxBytes?: number;
|
|
9
|
+
decode?: AudioWaveformDecoder;
|
|
10
|
+
}): {
|
|
11
|
+
readonly bytes: number;
|
|
12
|
+
readonly count: number;
|
|
13
|
+
clear: () => void;
|
|
14
|
+
get(id: string, bins: number, signal?: AbortSignal, channel?: number): Promise<AudioWaveform>;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
};
|