diff --git a/app/ui/features/composer/cuts.js b/app/ui/features/composer/cuts.js new file mode 100644 index 00000000..fac6f7ae --- /dev/null +++ b/app/ui/features/composer/cuts.js @@ -0,0 +1,23 @@ +// Cuts: the words that cut a story into scenes. The first is the one written between scenes. +import { composer as c } from "../../composer/composer.js"; + +export function cuts(app) { + const page = c.region.stack({ gap: "sm" }); + let words = ""; + const save = async () => { + try { + const r = await app.api.saveStoryMarkers(words.split("\n").map((w) => w.trim()).filter(Boolean)); + words = r.markers.join("\n"); + c.toast.good({ text: "Cut words saved." }); + draw(); + } catch (err) { c.toast.warn({ text: err.message }); } + }; + const draw = () => page.set([ + c.label.section({ text: "Cut words" }), + c.textarea.md({ label: "Cut words", rows: 5, value: words, onInput: (v) => { words = v; } }), + c.hint.default({ text: "One per line. A whole word typed in the Story starts a new scene and is never part of a scene's text. The first is the one written between scenes." }), + c.button.sm({ label: "Save", tone: "primary", onClick: save }), + ]); + app.api.storyMarkers().then((r) => { words = (r.markers || []).join("\n"); draw(); }).catch((err) => page.set([c.banner.warn({ text: `Could not load the cut words: ${err.message}` })])); + return page; +} diff --git a/app/ui/features/composer/index.js b/app/ui/features/composer/index.js index 1f354f1f..01ad347f 100644 --- a/app/ui/features/composer/index.js +++ b/app/ui/features/composer/index.js @@ -2,6 +2,7 @@ import { composer as c } from "../../composer/composer.js"; import { applyStory, joinStory } from "./story.js"; import { shortcuts } from "./shortcuts.js"; +import { cuts } from "./cuts.js"; const TABS = [{ value: "story", label: "Story" }, { value: "shortcuts", label: "Shortcuts" }, { value: "cuts", label: "Cuts" }, { value: "enhance", label: "Enhance" }, { value: "chat", label: "Chat" }]; const inert = (label, tone = "ghost") => c.button.sm({ label, tone, disabled: true }); @@ -38,7 +39,7 @@ const story = (app, own) => { ] }); }; -const sheets = { story, shortcuts, cuts: () => later("Where a story splits into shots."), +const sheets = { story, shortcuts, cuts, enhance: () => later("Rewrite a prompt with a language model."), chat: () => later("Talk a scene through with the enhancer.") }; export default { diff --git a/app/ui/shell/api.js b/app/ui/shell/api.js index e7a63475..34ee5854 100644 --- a/app/ui/shell/api.js +++ b/app/ui/shell/api.js @@ -26,6 +26,7 @@ export const api = { describeNodes: (classes) => call("GET", `/api/nodes?classes=${encodeURIComponent(classes.join(","))}`), system: () => call("GET", "/api/system"), storyMarkers: () => call("GET", "/api/story/markers"), + saveStoryMarkers: (markers) => call("POST", "/api/story/markers", { markers }), storySplit: (text) => call("POST", "/api/story/split", { text }), shortcuts: () => call("GET", "/api/shortcuts"), saveShortcut: (shortcut, originalName) => call("POST", "/api/shortcuts", { ...shortcut, original_name: originalName }),