From ec70bc8a2b2d602c8f4a47f9b44e36d372a2d0ea Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Mon, 17 Aug 2026 15:20:09 -0400 Subject: [PATCH 01/27] WIP book mode --- src/lib/settings.ts | 2 ++ src/routes/+page.svelte | 26 +++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/lib/settings.ts b/src/lib/settings.ts index 264d089..968fde6 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -7,6 +7,7 @@ export type Settings = { realisticLineHeight: boolean; // whether to use smaller line height hideSelectionExport: boolean; // whether to hide the export this button fontSize: number; // 0 = small, 1 = default, 2 = large + mode: "normal" | "book" | "lore" }; function createPersistentStore(key: string, startValue: any) { @@ -35,4 +36,5 @@ export const appSettings: Writable = createPersistentStore("settings", realisticLineHeight: false, hideSelectionExport: true, fontSize: 1, + mode: "book" }); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index a3d964e..d257f39 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -81,6 +81,10 @@ onMount(async () => { await loadData(); + function convertRemToPixels(rem: number): number { + return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); + } + editor = new Editor({ element: element, content: tiptapJSON, @@ -128,24 +132,34 @@ editor = newEditor; }, onUpdate: ({ editor }) => { + let el = document.querySelector(".tiptap") as HTMLElement; + let pageCount = 1; tiptapJSON = editor.getJSON(); debounce(saveContent, 1000)(); + + const metrics = element.getBoundingClientRect() + const maxHeight = parseInt(getComputedStyle(el).fontSize) * 14 + + while((metrics.height - (maxHeight * pageCount)) >= 0) { + pageCount++; + } + console.log("split result", metrics, maxHeight, pageCount) } }); appSettings.subscribe(() => { - var el = document.querySelector(".tiptap") as HTMLElement; + let el = document.querySelector(".tiptap") as HTMLElement; if ($appSettings.realisticLineHeight == true) { - var lineHeight = 0.8 + 0.2 * $appSettings.fontSize; + let lineHeight = 0.8 + 0.2 * $appSettings.fontSize; el.style.lineHeight = lineHeight.toString() + "rem"; // TODO fix the overlap from objects and event marks } else { - var lineHeight = 1.25 + 0.25 * $appSettings.fontSize; + let lineHeight = 1.25 + 0.25 * $appSettings.fontSize; el.style.lineHeight = lineHeight.toString() + "rem"; } - var fontSize = 1 + 0.25 * $appSettings.fontSize; + let fontSize = 1 + 0.25 * $appSettings.fontSize; el.style.fontSize = fontSize.toString() + "rem"; }); }); @@ -233,13 +247,15 @@ +
+
{#if page.url.searchParams.has("dev")} Date: Tue, 18 Aug 2026 10:57:47 -0400 Subject: [PATCH 02/27] WIP: another step towards book mode --- src/app.css | 2 +- src/routes/+page.svelte | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/app.css b/src/app.css index e7ca08d..6c4500f 100644 --- a/src/app.css +++ b/src/app.css @@ -48,7 +48,7 @@ strong { /* Editor */ .tiptap { - @apply h-full w-full p-4; + @apply h-fit w-full p-4; @apply focus:outline-none; font-family: Minecraft; @apply text-xl; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d257f39..3d18a12 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -133,17 +133,27 @@ }, onUpdate: ({ editor }) => { let el = document.querySelector(".tiptap") as HTMLElement; - let pageCount = 1; tiptapJSON = editor.getJSON(); debounce(saveContent, 1000)(); - const metrics = element.getBoundingClientRect() + // TODO: actually fill with content + let splitPages = [[]] + + // const metrics = el.getBoundingClientRect() const maxHeight = parseInt(getComputedStyle(el).fontSize) * 14 - while((metrics.height - (maxHeight * pageCount)) >= 0) { - pageCount++; + // const pages = Math.ceil(metrics.height / maxHeight) + let currentHeight = 0; + for(let child of el.children) { + const metrics = child.getBoundingClientRect() + currentHeight += metrics.height + if(currentHeight > maxHeight) { + currentHeight = 0 + // TODO: actually fill with the content + splitPages.push([]) + } } - console.log("split result", metrics, maxHeight, pageCount) + console.log("results", currentHeight, maxHeight, splitPages) } }); From 8e291456f33ffe19d9d2954d037c6c463a3e66e7 Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Thu, 20 Aug 2026 17:12:45 -0400 Subject: [PATCH 03/27] naive splitting --- src/routes/+page.svelte | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 3d18a12..83a8f39 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -132,25 +132,25 @@ editor = newEditor; }, onUpdate: ({ editor }) => { - let el = document.querySelector(".tiptap") as HTMLElement; + const el = editor.view.dom; tiptapJSON = editor.getJSON(); debounce(saveContent, 1000)(); // TODO: actually fill with content - let splitPages = [[]] - - // const metrics = el.getBoundingClientRect() - const maxHeight = parseInt(getComputedStyle(el).fontSize) * 14 - - // const pages = Math.ceil(metrics.height / maxHeight) + const splitPages: JSONContent[][] = [[]] + const maxHeight = parseInt(getComputedStyle(el).lineHeight) * 14 let currentHeight = 0; - for(let child of el.children) { + let currentPage = 0; + + for(let i = 0; i < el.children.length; i++) { + const child = el.children[i]; const metrics = child.getBoundingClientRect() currentHeight += metrics.height + splitPages[currentPage].push(editor.getJSON().content[i]) if(currentHeight > maxHeight) { currentHeight = 0 - // TODO: actually fill with the content splitPages.push([]) + currentPage++ } } console.log("results", currentHeight, maxHeight, splitPages) @@ -158,7 +158,11 @@ }); appSettings.subscribe(() => { - let el = document.querySelector(".tiptap") as HTMLElement; + const el = editor?.view.dom + + if(!el) { + return; + } if ($appSettings.realisticLineHeight == true) { let lineHeight = 0.8 + 0.2 * $appSettings.fontSize; From 5e5fc2a05e05a7c0488f7f1a8e9ee391d4843433 Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Sun, 23 Aug 2026 12:42:25 -0400 Subject: [PATCH 04/27] backup point --- src/routes/+page.svelte | 54 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 83a8f39..503165f 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -81,10 +81,6 @@ onMount(async () => { await loadData(); - function convertRemToPixels(rem: number): number { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); - } - editor = new Editor({ element: element, content: tiptapJSON, @@ -132,28 +128,10 @@ editor = newEditor; }, onUpdate: ({ editor }) => { - const el = editor.view.dom; + const pageOutputs = calculateBookOutput(editor) + console.log("pageOutputs", `[written_book_content={pages:[${pageOutputs}],title:"Hello World",author:"Datapack Hub"}]`) tiptapJSON = editor.getJSON(); debounce(saveContent, 1000)(); - - // TODO: actually fill with content - const splitPages: JSONContent[][] = [[]] - const maxHeight = parseInt(getComputedStyle(el).lineHeight) * 14 - let currentHeight = 0; - let currentPage = 0; - - for(let i = 0; i < el.children.length; i++) { - const child = el.children[i]; - const metrics = child.getBoundingClientRect() - currentHeight += metrics.height - splitPages[currentPage].push(editor.getJSON().content[i]) - if(currentHeight > maxHeight) { - currentHeight = 0 - splitPages.push([]) - currentPage++ - } - } - console.log("results", currentHeight, maxHeight, splitPages) } }); @@ -204,6 +182,31 @@ : event.ctrlKey; } + function calculateBookOutput(edit: Editor): string[][] { + const el = edit.view.dom; + + // TODO: actually fill with content + const splitPages: JSONContent[][] = [[]] + const maxHeight = parseInt(getComputedStyle(el).lineHeight) * 14 + let currentHeight = 0; + let currentPage = 0; + + for(let i = 0; i < el.children.length; i++) { + const child = el.children[i]; + const metrics = child.getBoundingClientRect() + currentHeight += metrics.height + splitPages[currentPage].push(edit.getJSON().content[i]) + if(currentHeight > maxHeight) { + currentHeight = 0 + splitPages.push([]) + currentPage++ + } + } + + console.log("results", currentHeight, maxHeight, splitPages) + return splitPages.filter((page) => page.length > 0).map((page) => [convert({type: "doc", content: page }, shouldOptimise)]) + } + function clearMarksHandler(event: KeyboardEvent) { if (modifierPressed(event) && event.shiftKey && event.key === "X") { editor!.commands.unsetAllMarks(); @@ -256,7 +259,7 @@ -
+
@@ -264,6 +267,7 @@
From 625cc04516910191a55c811d3ba0fa75063fbc4c Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Sun, 23 Aug 2026 14:07:53 -0400 Subject: [PATCH 05/27] format and somewhat complete book mode? --- src/app.css | 6 +- src/lib/components/Modal.svelte | 7 +- .../modals/ColorGradientModal.svelte | 33 +++-- .../modals/ExportSelectionModal.svelte | 132 ++++++++---------- .../components/modals/InsertImageModal.svelte | 24 ++-- .../modals/topbar/ExportModal.svelte | 60 +++----- .../modals/topbar/SettingsModal.svelte | 17 ++- src/lib/components/text/MiniEditor.svelte | 4 +- src/lib/components/text/MiniRenderer.svelte | 2 +- src/lib/components/toolbar/Toolbar.svelte | 23 ++- .../components/toolbar/ToolbarButton.svelte | 2 +- src/lib/settings.ts | 4 +- src/lib/text/nbt/export.ts | 21 +-- src/lib/text/nbt/optimiser.ts | 2 +- src/lib/text/utils.ts | 18 +-- src/lib/tiptap/extensions/ExportButton.ts | 42 +++--- .../extensions/marks/ObfuscationMark.ts | 4 +- src/routes/+page.svelte | 78 +++++++---- .../exporting/translating_mc_json.spec.ts | 4 +- 19 files changed, 263 insertions(+), 220 deletions(-) diff --git a/src/app.css b/src/app.css index 6c4500f..410769a 100644 --- a/src/app.css +++ b/src/app.css @@ -48,7 +48,7 @@ strong { /* Editor */ .tiptap { - @apply h-fit w-full p-4; + @apply h-full w-full p-4; @apply focus:outline-none; font-family: Minecraft; @apply text-xl; @@ -100,8 +100,8 @@ strong { } .export-button { - @apply items-center p-1 px-1.5 bg-zinc-900 text-zinc-200 hover:text-white rounded-md font-lexend text-xs h-6; - @apply w-0 invisible sm:w-fit sm:visible; /* hide on mobile */ + @apply font-lexend h-6 items-center rounded-md bg-zinc-900 p-1 px-1.5 text-xs text-zinc-200 hover:text-white; + @apply invisible w-0 sm:visible sm:w-fit; /* hide on mobile */ } /* Useful */ diff --git a/src/lib/components/Modal.svelte b/src/lib/components/Modal.svelte index a841731..9e44b0b 100644 --- a/src/lib/components/Modal.svelte +++ b/src/lib/components/Modal.svelte @@ -81,9 +81,12 @@ : ''} {flexible ? 'w-fit! max-w-[95%]' : ''} m-auto py-4">
{title} - +
{@render children()} diff --git a/src/lib/components/modals/ColorGradientModal.svelte b/src/lib/components/modals/ColorGradientModal.svelte index f068286..c28bfb8 100644 --- a/src/lib/components/modals/ColorGradientModal.svelte +++ b/src/lib/components/modals/ColorGradientModal.svelte @@ -15,7 +15,7 @@ let recentsPageOpen: boolean = $state(false); let recentGradients: Array = $state([]); - let gradientSteps: { id: number; color: string }[] = $state([{ id: 0, color: "#ffffff"}]); + let gradientSteps: { id: number; color: string }[] = $state([{ id: 0, color: "#ffffff" }]); interface Props { gradientDialog: Modal; @@ -81,14 +81,16 @@ chain.run(); // Add to recents if necessary - let gradientHexes = gradientSteps.map((step) => step.color) + let gradientHexes = gradientSteps.map((step) => step.color); - const alreadyAppeared = recentGradients.some(elem =>{ + const alreadyAppeared = recentGradients.some((elem) => { return JSON.stringify(gradientHexes) === JSON.stringify(elem); }); if (alreadyAppeared) { - recentGradients = recentGradients.filter((grad) => JSON.stringify(grad) !== JSON.stringify(gradientHexes)); + recentGradients = recentGradients.filter( + (grad) => JSON.stringify(grad) !== JSON.stringify(gradientHexes), + ); } recentGradients.push(gradientHexes); @@ -120,7 +122,10 @@ if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) { return; } - const [movedItem]: { id: number; color: string }[] = gradientSteps.splice(oldIndex, 1); + const [movedItem]: { id: number; color: string }[] = gradientSteps.splice( + oldIndex, + 1, + ); gradientSteps.splice(newIndex, 0, movedItem); gradientSteps = gradientSteps; }, @@ -173,9 +178,11 @@

Preview

{editor?.state.doc.textBetween( editor.state.selection.from, editor.state.selection.to, @@ -190,7 +197,10 @@
- - {/key} -
+ + {/key} +
diff --git a/src/lib/components/modals/InsertImageModal.svelte b/src/lib/components/modals/InsertImageModal.svelte index 9ca0a7c..ab2dea8 100644 --- a/src/lib/components/modals/InsertImageModal.svelte +++ b/src/lib/components/modals/InsertImageModal.svelte @@ -36,7 +36,7 @@ image.remove(); }; }); - + reader.readAsDataURL(files[0]); } } @@ -47,7 +47,7 @@ const image = new Image(); image.src = reader.result as string; image.onload = () => { - sizeWarning = image.width > 24 || image.height > 24 + sizeWarning = image.width > 24 || image.height > 24; image.remove(); }; }; @@ -65,11 +65,11 @@ } } } - + function processImage(image: HTMLImageElement): JSONContent[] { if (!files || !editor) return []; let completeContent: JSONContent[] = []; - + const canvas = new OffscreenCanvas(image.width, image.height); const ctx = canvas.getContext("2d", { willReadFrequently: true }); ctx?.reset(); @@ -116,12 +116,12 @@ } $effect(() => { - if(files && files.length > 0) { + if (files && files.length > 0) { checkFileSize(files[0]); } else { sizeWarning = false; } - }) + }); e.preventDefault()} /> @@ -150,10 +150,16 @@

Drag and drop an image here

{:else} -

Selected file: {files[0].name}

+

+ Selected file: {files[0].name} +

{#if sizeWarning} -
-

This image may be too large to display properly, and may also cause performance issues to process, you have been warned!

+
+

+ This image may be too large to display properly, and may also cause + performance issues to process, you have been warned! +

{/if}
diff --git a/src/lib/components/modals/topbar/ExportModal.svelte b/src/lib/components/modals/topbar/ExportModal.svelte index e42c976..6baded0 100644 --- a/src/lib/components/modals/topbar/ExportModal.svelte +++ b/src/lib/components/modals/topbar/ExportModal.svelte @@ -3,7 +3,7 @@ import { translateMOTD } from "$lib/text/motd"; import { convert } from "$lib/text/nbt/export"; import CheckBox from "../../CheckBox.svelte"; - import { domToPng, type Options } from "modern-screenshot"; + import { domToPng, type Options } from "modern-screenshot"; import { Highlight } from "svelte-highlight"; import typescript from "svelte-highlight/languages/typescript"; import { appSettings } from "$lib/settings"; @@ -29,7 +29,6 @@ shouldOptimise = true, } = $props(); - // Image output let imgPreview: HTMLImageElement | undefined = $state(); @@ -140,37 +139,23 @@
{#if $appSettings.syntaxHighlight} - + indent ? 4 : undefined, + )} /> {:else}
{editor
-                        ? JSON.stringify(
-                            JSON.parse(
-                                convert(
-                                    editor.getJSON(),
-                                    shouldOptimise,
-                                    "standard",
-                                    true,
-                                )
-                            ),
-                            null,
-                            indent ? 4 : undefined
-                        )
-                        : "Loading..."}
+ ? JSON.stringify( + JSON.parse( + convert(editor.getJSON(), shouldOptimise, "standard", true), + ), + null, + indent ? 4 : undefined, + ) + : "Loading..."} {/if}
@@ -184,17 +169,10 @@ onclick={() => { navigator.clipboard.writeText( JSON.stringify( - JSON.parse( - convert( - editor.getJSON(), - shouldOptimise, - "standard", - true, - ) - ), + JSON.parse(convert(editor.getJSON(), shouldOptimise, "standard", true)), null, - indent ? 4 : undefined - ) + indent ? 4 : undefined, + ), ); recentlyCopied = true; setTimeout(() => (recentlyCopied = false), 2000); @@ -346,4 +324,4 @@
{/if}
- \ No newline at end of file + diff --git a/src/lib/components/modals/topbar/SettingsModal.svelte b/src/lib/components/modals/topbar/SettingsModal.svelte index 6f0d927..f36efc1 100644 --- a/src/lib/components/modals/topbar/SettingsModal.svelte +++ b/src/lib/components/modals/topbar/SettingsModal.svelte @@ -47,7 +47,8 @@
@@ -66,5 +67,19 @@ >This alters the font size in the editor, but not the output.
+
+ + +
diff --git a/src/lib/components/text/MiniEditor.svelte b/src/lib/components/text/MiniEditor.svelte index 782956e..7c86be4 100644 --- a/src/lib/components/text/MiniEditor.svelte +++ b/src/lib/components/text/MiniEditor.svelte @@ -70,7 +70,9 @@ content.forEach((c) => { current = { - color: defaultColorLUT(c.marks?.find(obj => obj.type == "textStyle")?.attrs?.color || undefined), + color: defaultColorLUT( + c.marks?.find((obj) => obj.type == "textStyle")?.attrs?.color || undefined, + ), bold: trueMarkOrUndefined(c, "bold"), italic: trueMarkOrUndefined(c, "italic"), strikethrough: trueMarkOrUndefined(c, "strike"), diff --git a/src/lib/components/text/MiniRenderer.svelte b/src/lib/components/text/MiniRenderer.svelte index 061b3f8..b176e6b 100644 --- a/src/lib/components/text/MiniRenderer.svelte +++ b/src/lib/components/text/MiniRenderer.svelte @@ -61,7 +61,7 @@ content: value, editorProps: { attributes: { - class: 'tiptap-minirenderer', + class: "tiptap-minirenderer", }, }, }).setEditable(false); diff --git a/src/lib/components/toolbar/Toolbar.svelte b/src/lib/components/toolbar/Toolbar.svelte index 9584c24..590222a 100644 --- a/src/lib/components/toolbar/Toolbar.svelte +++ b/src/lib/components/toolbar/Toolbar.svelte @@ -160,14 +160,19 @@ Icon={IconGradient} onClick={gradientDialog.open} ariaLabel="Color Gradient" /> -
+
{#each colourMap as colour} {/each} @@ -262,9 +267,17 @@ editor?.commands.redo()} ariaLabel="Redo" Icon={IconRedo} />
- - - + + + {/if}
diff --git a/src/lib/components/toolbar/ToolbarButton.svelte b/src/lib/components/toolbar/ToolbarButton.svelte index b263653..2392b61 100644 --- a/src/lib/components/toolbar/ToolbarButton.svelte +++ b/src/lib/components/toolbar/ToolbarButton.svelte @@ -8,7 +8,7 @@ ariaLabel: string; Icon: Component; colour?: string; - desktopOnly?: boolean + desktopOnly?: boolean; }; const { onClick, styleVar, Icon, ariaLabel, colour, desktopOnly }: Props = $props(); diff --git a/src/lib/settings.ts b/src/lib/settings.ts index 968fde6..278c57c 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -7,7 +7,7 @@ export type Settings = { realisticLineHeight: boolean; // whether to use smaller line height hideSelectionExport: boolean; // whether to hide the export this button fontSize: number; // 0 = small, 1 = default, 2 = large - mode: "normal" | "book" | "lore" + mode: "normal" | "book"; }; function createPersistentStore(key: string, startValue: any) { @@ -36,5 +36,5 @@ export const appSettings: Writable = createPersistentStore("settings", realisticLineHeight: false, hideSelectionExport: true, fontSize: 1, - mode: "book" + mode: "book", }); diff --git a/src/lib/text/nbt/export.ts b/src/lib/text/nbt/export.ts index 1de6a97..91596ba 100644 --- a/src/lib/text/nbt/export.ts +++ b/src/lib/text/nbt/export.ts @@ -176,14 +176,17 @@ export function convert( // nbt number type fix for shadow colour // moved from translateJSON function const shadowColorMatches = out.matchAll(/"shadow_color":(-?\d+)/gu); - const relevantShadowColorMatches = shadowColorMatches.filter(item => (parseInt(item[1]) > 2 ** 31 - 1 || parseInt(item[1]) < (-2) ** 31)).toArray().map(item => item[1]) + const relevantShadowColorMatches = shadowColorMatches + .filter((item) => parseInt(item[1]) > 2 ** 31 - 1 || parseInt(item[1]) < (-2) ** 31) + .toArray() + .map((item) => item[1]); const deduplicatedRelevantShadowColorMatches = [...new Set(relevantShadowColorMatches)]; for (const match of deduplicatedRelevantShadowColorMatches) { - const num = parseInt(match) + const num = parseInt(match); out = out.replaceAll(`"shadow_color":${match}`, `"shadow_color":${num}L`); } - + // remove string marks from json keys only out = out.replaceAll(/(?<=[{,]\s*)"[^"]*"\s*:/gu, (match) => match.replaceAll(`"`, "")); } @@ -223,7 +226,7 @@ export function translateJSON(json: JSONContent, options: TranslateOptions): str if (data.length === 2 && data[0] == "") { return JSON.stringify(data[1]); } else if (data.length === 1) { - return JSON.stringify(data[0]) + return JSON.stringify(data[0]); } return JSON.stringify(data); @@ -257,13 +260,15 @@ export function translateJSON(json: JSONContent, options: TranslateOptions): str function constructComponent(content: JSONContent, includeInteractivity: boolean = true) { // Construct basic styled component let currentComponent: MinecraftText = { - color: defaultColorLUT(content.marks?.find(obj => obj.type == "textStyle")?.attrs?.color || undefined), + color: defaultColorLUT( + content.marks?.find((obj) => obj.type == "textStyle")?.attrs?.color || undefined, + ), bold: trueMarkOrUndefined(content, "bold"), italic: trueMarkOrUndefined(content, "italic"), strikethrough: trueMarkOrUndefined(content, "strike"), underlined: trueMarkOrUndefined(content, "underline"), obfuscated: trueMarkOrUndefined(content, "obfuscated"), - font: content.marks?.find(obj => obj.type == "textStyle")?.attrs?.font || undefined, + font: content.marks?.find((obj) => obj.type == "textStyle")?.attrs?.font || undefined, }; // Add shadow colour @@ -281,5 +286,5 @@ function constructComponent(content: JSONContent, includeInteractivity: boolean // Add content values (e.g. text and custom sources) depending on the component type currentComponent = addTypeSpecificValues(currentComponent, content, includeInteractivity); - return currentComponent -} \ No newline at end of file + return currentComponent; +} diff --git a/src/lib/text/nbt/optimiser.ts b/src/lib/text/nbt/optimiser.ts index 7c99cb5..b70e268 100644 --- a/src/lib/text/nbt/optimiser.ts +++ b/src/lib/text/nbt/optimiser.ts @@ -45,7 +45,7 @@ export function optimise(stringyTextElements: StringyMCText[], lore = false): St if (lore) { output.unshift({ italic: false, color: "white", text: "" }); } - + return output; } diff --git a/src/lib/text/utils.ts b/src/lib/text/utils.ts index 9153a70..f0cf057 100644 --- a/src/lib/text/utils.ts +++ b/src/lib/text/utils.ts @@ -54,7 +54,7 @@ export function defaultColorLUT(color: string): string | undefined { return; } - color = rgbToHex(color) + color = rgbToHex(color); return colourMap.find((e) => e.value.toUpperCase() === color)?.name || color; } @@ -167,16 +167,16 @@ export function rgbaToArgbHex(rgbaHex: string): string { } export function rgbToHex(color: string): string { - if (color.startsWith('rgb')) { + if (color.startsWith("rgb")) { const result = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/u.exec(color); return result - ? "#" + - [1, 2, 3] - .map((n) => parseInt(result[n]).toString(16).padStart(2, "0")) - .join("") - .toUpperCase() - : color; + ? "#" + + [1, 2, 3] + .map((n) => parseInt(result[n]).toString(16).padStart(2, "0")) + .join("") + .toUpperCase() + : color; } else { - return color.toUpperCase() + return color.toUpperCase(); } } diff --git a/src/lib/tiptap/extensions/ExportButton.ts b/src/lib/tiptap/extensions/ExportButton.ts index 7fd5c03..c260af6 100644 --- a/src/lib/tiptap/extensions/ExportButton.ts +++ b/src/lib/tiptap/extensions/ExportButton.ts @@ -1,8 +1,8 @@ -import { Extension } from '@tiptap/core'; -import { Plugin, PluginKey } from '@tiptap/pm/state'; -import { EditorView } from '@tiptap/pm/view'; +import { Extension } from "@tiptap/core"; +import { Plugin, PluginKey } from "@tiptap/pm/state"; +import { EditorView } from "@tiptap/pm/view"; import { appSettings } from "$lib/settings"; -import { get } from 'svelte/store'; +import { get } from "svelte/store"; // Define the shape of configuration options for type safety export interface ExportButtonOptions { @@ -10,11 +10,11 @@ export interface ExportButtonOptions { } export const ExportButtonExtension = Extension.create({ - name: 'ExportButton', + name: "ExportButton", addOptions() { return { - onClick: () => { }, + onClick: () => {}, }; }, @@ -24,14 +24,14 @@ export const ExportButtonExtension = Extension.create({ return [ new Plugin({ - key: new PluginKey('ExportButton'), + key: new PluginKey("ExportButton"), view(editorView: EditorView) { return { update(view: EditorView) { if (get(appSettings).hideSelectionExport == true) { return; } - + const { state } = view; const { selection } = state; @@ -39,20 +39,22 @@ export const ExportButtonExtension = Extension.create({ if (!parentNode) return; if ((selection.empty || !view.hasFocus()) && buttonDom) { - buttonDom.style.display = 'none'; + buttonDom.style.display = "none"; return; } if (!buttonDom) { - buttonDom = document.createElement('button'); - buttonDom.innerText = '↪ Export this'; - buttonDom.className = 'export-button'; - buttonDom.style.position = 'absolute'; - buttonDom.style.zIndex = '10'; + buttonDom = document.createElement("button"); + buttonDom.innerText = "↪ Export this"; + buttonDom.className = "export-button"; + buttonDom.style.position = "absolute"; + buttonDom.style.zIndex = "10"; + + buttonDom.addEventListener("mousedown", (e: MouseEvent) => + e.preventDefault(), + ); + buttonDom.addEventListener("click", onClick); - buttonDom.addEventListener('mousedown', (e: MouseEvent) => e.preventDefault()); - buttonDom.addEventListener('click', onClick); - parentNode.appendChild(buttonDom); } @@ -60,9 +62,9 @@ export const ExportButtonExtension = Extension.create({ const top = coords.top + 19; const left = coords.left - 90; - buttonDom.style.display = 'block'; + buttonDom.style.display = "block"; buttonDom.style.top = `${top}px`; - buttonDom.style.left = `${left + 5}px`; + buttonDom.style.left = `${left + 5}px`; }, destroy() { @@ -70,7 +72,7 @@ export const ExportButtonExtension = Extension.create({ buttonDom.remove(); buttonDom = null; } - } + }, }; }, }), diff --git a/src/lib/tiptap/extensions/marks/ObfuscationMark.ts b/src/lib/tiptap/extensions/marks/ObfuscationMark.ts index 87687de..460ff80 100644 --- a/src/lib/tiptap/extensions/marks/ObfuscationMark.ts +++ b/src/lib/tiptap/extensions/marks/ObfuscationMark.ts @@ -2,13 +2,13 @@ import { Mark, mergeAttributes } from "@tiptap/core"; export const Obfuscation = Mark.create({ name: "obfuscated", - + renderHTML({ HTMLAttributes }) { return [ "span", mergeAttributes( { - class: "obfuscated" + class: "obfuscated", }, HTMLAttributes, ), diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 503165f..22ed29c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -50,7 +50,8 @@ let recentlyCopied = $state(false); let finalOutput = $derived(editor ? convert(tiptapJSON, shouldOptimise) : "Loading..."); - + let pageOutputs = $derived(editor ? calculateBookOutput(editor) : [["Loading..."]]); + let exportSelectionDialog: Modal = $state()!; async function loadData() { @@ -118,27 +119,29 @@ }), ExportButtonExtension.configure({ onClick: () => { - exportSelectionDialog.open() + exportSelectionDialog.open(); }, }), - ], onTransaction: ({ editor: newEditor }) => { editor = undefined; editor = newEditor; }, onUpdate: ({ editor }) => { - const pageOutputs = calculateBookOutput(editor) - console.log("pageOutputs", `[written_book_content={pages:[${pageOutputs}],title:"Hello World",author:"Datapack Hub"}]`) + console.log( + "pageOutputs", + `[written_book_content={pages:[${pageOutputs}],title:"Your Title Here",author:"You"}]`, + ); + pageOutputs = calculateBookOutput(editor); tiptapJSON = editor.getJSON(); debounce(saveContent, 1000)(); - } + }, }); appSettings.subscribe(() => { - const el = editor?.view.dom + const el = document.querySelector(".tiptap") as HTMLElement; - if(!el) { + if (!el) { return; } @@ -153,6 +156,17 @@ let fontSize = 1 + 0.25 * $appSettings.fontSize; el.style.fontSize = fontSize.toString() + "rem"; + if ($appSettings.mode === "book") { + el.style.width = 11.5 + 2.3 * $appSettings.fontSize + "rem"; + el.style.borderRight = "1px solid #676767"; + el.style.backgroundColor = "#fdf8ed"; + el.style.color = "#000000"; + } else { + el.style.width = "100%"; + el.style.borderRight = "none"; + el.style.backgroundColor = ""; + el.style.color = ""; + } }); }); @@ -184,27 +198,28 @@ function calculateBookOutput(edit: Editor): string[][] { const el = edit.view.dom; - + // TODO: actually fill with content - const splitPages: JSONContent[][] = [[]] - const maxHeight = parseInt(getComputedStyle(el).lineHeight) * 14 + const splitPages: JSONContent[][] = [[]]; + const maxHeight = parseInt(getComputedStyle(el).lineHeight) * 14; let currentHeight = 0; let currentPage = 0; - - for(let i = 0; i < el.children.length; i++) { + + for (let i = 0; i < el.children.length; i++) { const child = el.children[i]; - const metrics = child.getBoundingClientRect() - currentHeight += metrics.height - splitPages[currentPage].push(edit.getJSON().content[i]) - if(currentHeight > maxHeight) { - currentHeight = 0 - splitPages.push([]) - currentPage++ + const metrics = child.getBoundingClientRect(); + currentHeight += metrics.height; + splitPages[currentPage].push(edit.getJSON().content[i]); + if (currentHeight > maxHeight) { + currentHeight = 0; + splitPages.push([]); + currentPage++; } } - console.log("results", currentHeight, maxHeight, splitPages) - return splitPages.filter((page) => page.length > 0).map((page) => [convert({type: "doc", content: page }, shouldOptimise)]) + return splitPages + .filter((page) => page.length > 0) + .map((page) => [convert({ type: "doc", content: page }, shouldOptimise)]); } function clearMarksHandler(event: KeyboardEvent) { @@ -266,8 +281,7 @@
@@ -280,8 +294,8 @@ >DEV ONLY: {tiptapJSON ? JSON.stringify(tiptapJSON) : "Loading..."}
{/if} -
-
+
+
-
- - -
diff --git a/src/lib/settings.ts b/src/lib/settings.ts index 278c57c..ff48ad3 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -7,7 +7,6 @@ export type Settings = { realisticLineHeight: boolean; // whether to use smaller line height hideSelectionExport: boolean; // whether to hide the export this button fontSize: number; // 0 = small, 1 = default, 2 = large - mode: "normal" | "book"; }; function createPersistentStore(key: string, startValue: any) { @@ -35,6 +34,5 @@ export const appSettings: Writable = createPersistentStore("settings", syntaxHighlight: true, realisticLineHeight: false, hideSelectionExport: true, - fontSize: 1, - mode: "book", + fontSize: 1 }); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 22ed29c..f85699c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -85,6 +85,11 @@ editor = new Editor({ element: element, content: tiptapJSON, + editorProps: { + attributes: { + class: "tiptap-base", + }, + }, extensions: [ StarterKit.configure({ blockquote: false, @@ -156,17 +161,6 @@ let fontSize = 1 + 0.25 * $appSettings.fontSize; el.style.fontSize = fontSize.toString() + "rem"; - if ($appSettings.mode === "book") { - el.style.width = 11.5 + 2.3 * $appSettings.fontSize + "rem"; - el.style.borderRight = "1px solid #676767"; - el.style.backgroundColor = "#fdf8ed"; - el.style.color = "#000000"; - } else { - el.style.width = "100%"; - el.style.borderRight = "none"; - el.style.backgroundColor = ""; - el.style.color = ""; - } }); }); @@ -315,14 +309,10 @@ {#if $appSettings.syntaxHighlight} + code={finalOutput} /> {:else}
{editor
-                                ? $appSettings.mode === "book"
-                                    ? `[written_book_content={pages:[${pageOutputs}],title:"Hello World",author:"Datapack Hub"}]`
-                                    : finalOutput
+                                ? finalOutput
                                 : "Loading..."}
{/if} From 32f2530114b35c91215625be49cbd25053e20b54 Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Fri, 28 Aug 2026 16:15:58 -0400 Subject: [PATCH 07/27] thank you git for having -a to add all files which doesnt add untracked files --- src/routes/book/+page.svelte | 452 +++++++++++++++++++++++++++++++++++ static/book.png | Bin 0 -> 4948 bytes 2 files changed, 452 insertions(+) create mode 100644 src/routes/book/+page.svelte create mode 100644 static/book.png diff --git a/src/routes/book/+page.svelte b/src/routes/book/+page.svelte new file mode 100644 index 0000000..e263c78 --- /dev/null +++ b/src/routes/book/+page.svelte @@ -0,0 +1,452 @@ + + + + +
+ + + + + +
+
+
+
+ +

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+

xxxxxxxxxxxxxxxxxxxx

+
+
+
+
+
+
+ + +
+ {#if page.url.searchParams.has("dev")} + DEV ONLY: {tiptapJSON ? JSON.stringify(tiptapJSON) : "Loading..."} +
+ {/if} +
+
+ + + + {#if $appSettings.syntaxHighlight} + + {:else} +
{editor
+                                ? `[written_book_content={pages:[${pageOutputs}],title:"Hello World",author:"Datapack Hub"}]`
+                                : "Loading..."}
+ {/if} +
+
+
+

+ click to change output settings: +

+ +
+ {#if versionPopup} +
+ {#if versionPopupConfirmationVisible} +
+
+ Warning: + Changing to an earlier version could remove some + elements of your text that are unsupported in this + version. +
+ + +
+
+
+ {/if} +
+
+ version + description +
+ {#each versions as v} + + {/each} + * unreleased minecraft version +
+
+ {/if} + + +
+ + + +

+ + + + {#if $appSettings.showCharacterCount} +

+ +

+ {finalOutput.length} characters +

+ {/if} +
+
+
+
+ + + +{#await import("$lib/components/modals/topbar/ExportModal.svelte") then modal} + +{/await} + +{#await import("$lib/components/modals/ExportSelectionModal.svelte") then modal} + +{/await} diff --git a/static/book.png b/static/book.png new file mode 100644 index 0000000000000000000000000000000000000000..e045fcb2e4e2c49c8e66207a4e245f7ecdcea675 GIT binary patch literal 4948 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 B1X=(9 literal 0 HcmV?d00001 From d2ac0cd615424f9e556bc4b3b4d0ed2776fd0be4 Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Tue, 1 Sep 2026 10:26:13 -0400 Subject: [PATCH 08/27] part 2 --- src/app.css | 4 +- src/lib/components/TopUI.svelte | 20 ++-- .../modals/topbar/ImportModal.svelte | 109 ++++++++++++++---- src/lib/text/nbt/export.ts | 4 +- src/lib/text/nbt/import.ts | 11 ++ src/routes/+page.svelte | 19 ++- src/routes/book/+page.svelte | 93 ++++++++------- .../exporting/translating_mc_json.spec.ts | 5 +- 8 files changed, 174 insertions(+), 91 deletions(-) diff --git a/src/app.css b/src/app.css index bab8ec0..8776428 100644 --- a/src/app.css +++ b/src/app.css @@ -63,7 +63,7 @@ strong { } .tiptap-book { - @apply h-115 w-94 pt-20 px-12 text-black; + @apply h-115 w-94 px-12 pt-20 text-black; background-image: url("./book.png"); background-repeat: no-repeat; background-size: cover; @@ -83,7 +83,7 @@ strong { } .tiptap-minirenderer { - @apply h-full w-full p-2; + @apply h-full w-full p-2 text-white; @apply focus:outline-none; font-family: Minecraft; @apply text-xl; diff --git a/src/lib/components/TopUI.svelte b/src/lib/components/TopUI.svelte index 8a7d83d..0fee9ad 100644 --- a/src/lib/components/TopUI.svelte +++ b/src/lib/components/TopUI.svelte @@ -1,6 +1,5 @@ -
-

Paste text components here to import them into the editor. This action is undoable.

- - -
- - + {#if page.url.pathname === "/book"} +
+

+ Paste text components here to import them into the book editor. This action is + undoable. +

+ + +
+ + + +
+ +
+ {:else} +
+

+ Paste text components here to import them into the editor. This action is undoable. +

+ - -
+
+ + +
+ +
+ {/if} diff --git a/src/lib/text/nbt/export.ts b/src/lib/text/nbt/export.ts index 91596ba..eb4ad62 100644 --- a/src/lib/text/nbt/export.ts +++ b/src/lib/text/nbt/export.ts @@ -212,9 +212,7 @@ export function translateJSON(json: JSONContent, options: TranslateOptions): str } if (data.length === 0) { - return Math.random() < 0.002 - ? "🤓 <- kevin is waiting for you to type something" - : "waiting for input..."; + return JSON.stringify(""); } if (options.optimise) { diff --git a/src/lib/text/nbt/import.ts b/src/lib/text/nbt/import.ts index c959d4e..3b27e54 100644 --- a/src/lib/text/nbt/import.ts +++ b/src/lib/text/nbt/import.ts @@ -9,6 +9,17 @@ import { import { type MinecraftText, type OldMinecraftText } from "../../types"; import { type StringyMCText } from "../../types"; +export function importBook(raw: StringyMCText[]): JSONContent[] { + const pages: JSONContent[] = []; + + raw.forEach((page) => { + const pageDocument = snbtToDocument([page]); + pages.push(pageDocument); + }); + + return pages; +} + export function snbtToDocument(raw: StringyMCText[]): JSONContent { let baseDocument: JSONContent = { type: "doc", diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 77d402a..0930561 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,5 +1,5 @@ {@render children()} diff --git a/src/lib/components/WelcomeScreen.svelte b/src/lib/components/WelcomeScreen.svelte index c86839e..ce509a3 100644 --- a/src/lib/components/WelcomeScreen.svelte +++ b/src/lib/components/WelcomeScreen.svelte @@ -5,7 +5,7 @@ import IconCode from "~icons/tabler/code"; import IconClose from "~icons/tabler/X" import { onMount } from "svelte"; - import { welcomeScreenFormat } from "$lib/globals"; + import { welcomeScreenFormat } from "$lib/settings"; let { visible = $bindable(false) }: { visible: boolean } = $props() diff --git a/src/lib/components/modals/BookDetailsModal.svelte b/src/lib/components/modals/BookDetailsModal.svelte new file mode 100644 index 0000000..7ab4007 --- /dev/null +++ b/src/lib/components/modals/BookDetailsModal.svelte @@ -0,0 +1,30 @@ + + + +
+ + + + +
+
diff --git a/src/lib/components/modals/CustomSourceModal.svelte b/src/lib/components/modals/CustomSourceModal.svelte index aa76349..a39594d 100644 --- a/src/lib/components/modals/CustomSourceModal.svelte +++ b/src/lib/components/modals/CustomSourceModal.svelte @@ -3,18 +3,18 @@ import type { ExternalSources } from "$lib/types"; import IconScore from "~icons/tabler/123"; + import IconBack from "~icons/tabler/arrow-back-up"; import IconSelector from "~icons/tabler/at"; + import IconObject from "~icons/tabler/box"; import IconNBT from "~icons/tabler/braces"; + import IconInfo from "~icons/tabler/info-circle"; import IconKeybind from "~icons/tabler/keyboard"; import IconTranslate from "~icons/tabler/language"; - import IconObject from "~icons/tabler/box"; - import IconInfo from "~icons/tabler/info-circle"; - import IconBack from "~icons/tabler/arrow-back-up"; + import { outputVersion } from "$lib/settings"; + import { tooltip_right } from "$lib/tooltip"; import CheckBox from "../CheckBox.svelte"; import MiniEditor from "../text/MiniEditor.svelte"; - import { outputVersion } from "$lib/stores"; - import { tooltip, tooltip_right } from "$lib/tooltip"; let { customDialog = $bindable(), editor, customType = $bindable() } = $props(); diff --git a/src/lib/components/modals/KeybindModal.svelte b/src/lib/components/modals/KeybindModal.svelte index ef8aa14..a61fa4f 100644 --- a/src/lib/components/modals/KeybindModal.svelte +++ b/src/lib/components/modals/KeybindModal.svelte @@ -20,26 +20,28 @@ { keys: [modifierKey, "Shift", "X"], action: "Clear all formatting" }, ]; const modalKeybinds = [ - { keys: [modifierKey, "Shift", "G"], action: "Add Color Gradient" }, - { keys: [modifierKey, "Shift", "K"], action: "View Keybinds" }, - { keys: [modifierKey, "Shift", "T"], action: "Add Click Event" }, - { keys: [modifierKey, "Shift", "H"], action: "Add Hover Event" }, { keys: [modifierKey, "Shift", "C"], action: "Add Custom Color" }, + { keys: [modifierKey, "Shift", "D"], action: "Open Book Details (book mode)" }, + { keys: [modifierKey, "Shift", "E"], action: "Export Menu" }, { keys: [modifierKey, "Shift", "F"], action: "Add a font" }, - { keys: [modifierKey, "Shift", "W"], action: "Add Custom Source" }, - { keys: [modifierKey, "Shift", "U"], action: "Unicode Menu" }, + { keys: [modifierKey, "Shift", "G"], action: "Add Color Gradient" }, + { keys: [modifierKey, "Shift", "H"], action: "Add Hover Event" }, { keys: [modifierKey, "Shift", "I"], action: "Import Menu" }, - { keys: [modifierKey, "Shift", "E"], action: "Export Menu" }, + { keys: [modifierKey, "Shift", "K"], action: "View Keybinds" }, { keys: [modifierKey, "Shift", "L"], action: "Load a snapshot" }, - { keys: [modifierKey, "Shift", "M"], action: "Load an image (with block characters)" }, + { keys: [modifierKey, "Shift", "M"], action: "Load an image" }, + { keys: [modifierKey, "Shift", "R"], action: "Open Settings" }, + { keys: [modifierKey, "Shift", "T"], action: "Add Click Event" }, + { keys: [modifierKey, "Shift", "U"], action: "Unicode Menu" }, + { keys: [modifierKey, "Shift", "W"], action: "Add Custom Source" }, ];

Formatting Keybinds

-
+
{#each keysAndActions as { keys, action }} -
+
{#each keys as key} {key} {/each} @@ -49,7 +51,7 @@ {/each}

Modal Keybinds

-
+
{#each modalKeybinds as { keys, action }}
{#each keys as key} diff --git a/src/lib/components/modals/topbar/SavedTextsModal.svelte b/src/lib/components/modals/topbar/SavedTextsModal.svelte index a753689..ffba0c3 100644 --- a/src/lib/components/modals/topbar/SavedTextsModal.svelte +++ b/src/lib/components/modals/topbar/SavedTextsModal.svelte @@ -3,8 +3,6 @@ import MiniRenderer from "$lib/components/text/MiniRenderer.svelte"; import { tooltip } from "$lib/tooltip"; import type { Editor } from "@tiptap/core"; - import IconDelete from "~icons/tabler/trash"; - import IconLoad from "~icons/tabler/upload"; interface Props { editor: Editor | undefined; diff --git a/src/lib/components/modals/topbar/SettingsModal.svelte b/src/lib/components/modals/topbar/SettingsModal.svelte index 03eeb59..f53dfbc 100644 --- a/src/lib/components/modals/topbar/SettingsModal.svelte +++ b/src/lib/components/modals/topbar/SettingsModal.svelte @@ -10,7 +10,7 @@ let { settingsDialog = $bindable() }: Props = $props(); - +
diff --git a/src/lib/globals.ts b/src/lib/globals.ts deleted file mode 100644 index b74b696..0000000 --- a/src/lib/globals.ts +++ /dev/null @@ -1 +0,0 @@ -export const welcomeScreenFormat = "1"; diff --git a/src/lib/settings.ts b/src/lib/settings.ts index ff48ad3..b9c7f05 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -1,5 +1,6 @@ -import { writable, type Writable } from "svelte/store"; import { browser } from "$app/environment"; +import { writable, type Writable } from "svelte/store"; +import { versions } from "./types"; export type Settings = { showCharacterCount: boolean; // whether the character count is shown @@ -36,3 +37,6 @@ export const appSettings: Writable = createPersistentStore("settings", hideSelectionExport: true, fontSize: 1 }); + +export const outputVersion = writable(versions[versions.length - 1]); +export const welcomeScreenFormat = "1"; \ No newline at end of file diff --git a/src/lib/stores.ts b/src/lib/stores.ts deleted file mode 100644 index 9cc9915..0000000 --- a/src/lib/stores.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { writable } from "svelte/store"; -import { versions } from "./types"; - -export const outputVersion = writable(versions[versions.length - 1]); diff --git a/src/lib/text/nbt/export.ts b/src/lib/text/nbt/export.ts index eb4ad62..5f8d89c 100644 --- a/src/lib/text/nbt/export.ts +++ b/src/lib/text/nbt/export.ts @@ -7,7 +7,7 @@ import { trueMarkOrUndefined, unescapeUnicode, } from "../utils"; -import { outputVersion } from "$lib/stores"; +import { outputVersion } from "$lib/settings"; import { get } from "svelte/store"; import { optimise } from "./optimiser"; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0930561..f8a524e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -7,7 +7,7 @@ import TopUI from "$lib/components/TopUI.svelte"; import { openDataStore } from "$lib/db"; import { appSettings } from "$lib/settings"; - import { outputVersion } from "$lib/stores"; + import { outputVersion } from "$lib/settings"; import { convert } from "$lib/text/nbt/export"; import { ExportButtonExtension } from "$lib/tiptap/extensions/ExportButton"; import { fontLUT } from "$lib/tiptap/extensions/fonts"; diff --git a/src/routes/book/+page.svelte b/src/routes/book/+page.svelte index c1b8a9e..2287572 100644 --- a/src/routes/book/+page.svelte +++ b/src/routes/book/+page.svelte @@ -7,7 +7,7 @@ import WelcomeScreen from "$lib/components/WelcomeScreen.svelte"; import { openDataStore } from "$lib/db"; import { appSettings } from "$lib/settings"; - import { outputVersion } from "$lib/stores"; + import { outputVersion } from "$lib/settings"; import { convert } from "$lib/text/nbt/export"; import { ExportButtonExtension } from "$lib/tiptap/extensions/ExportButton"; import { fontLUT } from "$lib/tiptap/extensions/fonts"; @@ -55,6 +55,8 @@ let recentlyCopied = $state(false); let exportSelectionDialog: Modal = $state()!; + let bookDetailsDialog: Modal = $state()!; + let versionPopupConfirmationVisible = $state(false); let temporaryVersionConfirmation: Version | undefined = $state(); @@ -242,13 +244,14 @@ {editor} {welcomeScreenVisible} /> +
(BETA) The book editor is in active development. Report bugs and expect incomplete/broken features (also keep backups!).
+ class="flex h-[calc(100vh-13rem)] w-80 flex-col items-center gap-4 overflow-y-scroll p-4"> {#each pageJSONs as page, index}

Page {index + 1}

@@ -462,3 +465,7 @@ {#await import("$lib/components/modals/ExportSelectionModal.svelte") then modal} {/await} + +{#await import("$lib/components/modals/BookDetailsModal.svelte") then modal} + +{/await} \ No newline at end of file diff --git a/src/tests/unit/exporting/translating_mc_json.spec.ts b/src/tests/unit/exporting/translating_mc_json.spec.ts index 28aa7f4..5ac4c7f 100644 --- a/src/tests/unit/exporting/translating_mc_json.spec.ts +++ b/src/tests/unit/exporting/translating_mc_json.spec.ts @@ -1,4 +1,4 @@ -import { outputVersion } from "$lib/stores"; +import { outputVersion } from "$lib/settings"; import { convert, translateJSON } from "$lib/text/nbt/export"; import { versions, type TranslateOptions } from "$lib/types"; import type { JSONContent } from "@tiptap/core"; diff --git a/src/tests/unit/exporting/type_props.spec.ts b/src/tests/unit/exporting/type_props.spec.ts index 353a2b9..37af9b1 100644 --- a/src/tests/unit/exporting/type_props.spec.ts +++ b/src/tests/unit/exporting/type_props.spec.ts @@ -1,4 +1,4 @@ -import { outputVersion } from "$lib/stores"; +import { outputVersion } from "$lib/settings"; import { addTypeSpecificValues } from "$lib/text/nbt/export"; import { versions, type MinecraftText, type VersionAgnosticText } from "$lib/types"; import type { JSONContent } from "@tiptap/core"; From 238945fe99b1e918930dcad19ddc57a6d29d3247 Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Tue, 1 Sep 2026 11:24:45 -0400 Subject: [PATCH 10/27] cool thing --- src/app.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.css b/src/app.css index 8776428..a6f08e0 100644 --- a/src/app.css +++ b/src/app.css @@ -64,7 +64,7 @@ strong { .tiptap-book { @apply h-115 w-94 px-12 pt-20 text-black; - background-image: url("./book.png"); + background-image: url("/book.png"); background-repeat: no-repeat; background-size: cover; image-rendering: pixelated; @@ -74,7 +74,7 @@ strong { } .page-preview { - background-image: url("./book.png"); + background-image: url("/book.png"); background-repeat: no-repeat; background-size: cover; image-rendering: pixelated; From e57488f5df3f2d118d80d42bbbbf701cd45d4aba Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Tue, 1 Sep 2026 11:45:35 -0400 Subject: [PATCH 11/27] final try --- src/app.css | 4 ++-- {static => src/lib/assets}/book.png | Bin 2 files changed, 2 insertions(+), 2 deletions(-) rename {static => src/lib/assets}/book.png (100%) diff --git a/src/app.css b/src/app.css index a6f08e0..8791402 100644 --- a/src/app.css +++ b/src/app.css @@ -64,7 +64,7 @@ strong { .tiptap-book { @apply h-115 w-94 px-12 pt-20 text-black; - background-image: url("/book.png"); + background-image: url("$lib/assets/book.png"); background-repeat: no-repeat; background-size: cover; image-rendering: pixelated; @@ -74,7 +74,7 @@ strong { } .page-preview { - background-image: url("/book.png"); + background-image: url("$lib/assets/book.png"); background-repeat: no-repeat; background-size: cover; image-rendering: pixelated; diff --git a/static/book.png b/src/lib/assets/book.png similarity index 100% rename from static/book.png rename to src/lib/assets/book.png From 3df932738820ca7c643f60a7ebe3071af59448dc Mon Sep 17 00:00:00 2001 From: Cobblestone Date: Tue, 1 Sep 2026 12:26:27 -0400 Subject: [PATCH 12/27] editable book details --- .../components/modals/BookDetailsModal.svelte | 6 +++--- src/lib/components/toolbar/Toolbar.svelte | 11 +++++------ src/routes/book/+page.svelte | 19 +++++++++++++------ 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/lib/components/modals/BookDetailsModal.svelte b/src/lib/components/modals/BookDetailsModal.svelte index 7ab4007..ce0360b 100644 --- a/src/lib/components/modals/BookDetailsModal.svelte +++ b/src/lib/components/modals/BookDetailsModal.svelte @@ -14,17 +14,17 @@ }: Props = $props(); - +
- + - +
diff --git a/src/lib/components/toolbar/Toolbar.svelte b/src/lib/components/toolbar/Toolbar.svelte index be768f7..75e06b8 100644 --- a/src/lib/components/toolbar/Toolbar.svelte +++ b/src/lib/components/toolbar/Toolbar.svelte @@ -1,27 +1,26 @@ {@render children()} diff --git a/src/lib/components/Modal.svelte b/src/lib/components/Modal.svelte index b529de5..7ff89d4 100644 --- a/src/lib/components/Modal.svelte +++ b/src/lib/components/Modal.svelte @@ -88,7 +88,10 @@ class="rounded-md p-2 hover:bg-black/15" onclick={close}>
-
+
{@render children()}
diff --git a/src/lib/components/TopUI.svelte b/src/lib/components/TopUI.svelte index 0fee9ad..86e2027 100644 --- a/src/lib/components/TopUI.svelte +++ b/src/lib/components/TopUI.svelte @@ -14,7 +14,12 @@ pageIndex?: number; } - let { editor, welcomeScreenVisible = $bindable(), pages = $bindable(), pageIndex = $bindable() }: Props = $props(); + let { + editor, + welcomeScreenVisible = $bindable(), + pages = $bindable(), + pageIndex = $bindable(), + }: Props = $props(); let snapshots = $state([]); @@ -33,7 +38,9 @@
- diff --git a/src/lib/components/WelcomeScreen.svelte b/src/lib/components/WelcomeScreen.svelte index ce509a3..cda3182 100644 --- a/src/lib/components/WelcomeScreen.svelte +++ b/src/lib/components/WelcomeScreen.svelte @@ -3,11 +3,11 @@ import IconGradient from "~icons/tabler/contrast-2"; import IconLore from "~icons/tabler/swords"; import IconCode from "~icons/tabler/code"; - import IconClose from "~icons/tabler/X" + import IconClose from "~icons/tabler/X"; import { onMount } from "svelte"; - import { welcomeScreenFormat } from "$lib/settings"; + import { welcomeScreenFormat } from "$lib/globals"; - let { visible = $bindable(false) }: { visible: boolean } = $props() + let { visible = $bindable(false) }: { visible: boolean } = $props(); function handleKeydown(event: KeyboardEvent) { if (event.key === "Escape" && visible) { @@ -16,90 +16,131 @@ } onMount(() => { - let seenWelcome = localStorage.getItem("seen_welcome") + let seenWelcome = localStorage.getItem("seen_welcome"); if (seenWelcome && seenWelcome == welcomeScreenFormat) { - visible = false + visible = false; } else { - visible = true + visible = true; localStorage.setItem("seen_welcome", welcomeScreenFormat); } - }) + }); {#if visible} -
-
- +
-
- logo - Minecraft Text Editor -
- + class="fixed top-0 left-0 z-40 flex h-screen w-screen flex-col items-center overflow-auto bg-black/65 text-zinc-100" + style="font-family: Lexend"> + -
-

Welcome!

-

With this tool, you can easily generate text components for Minecraft datapacks, tellraw commands, and more. Here's what makes us unique:

- -
+
+
+ logo + Minecraft Text Editor +
+ +
+
+

Welcome!

+

+ With this tool, you can easily generate text components for Minecraft + datapacks, tellraw commands, and more. Here's what makes us unique: +

-
- -
- Import - Import text components from any version into the editor. +
+
+ +
+ Import + Import text components from any version into the editor. +
-
-
- -
- Gradient - Add gradients to text, while keeping styles and interactivity. +
+ +
+ Gradient + Add gradients to text, while keeping styles and interactivity. +
-
-
- -
- Lore output - Get an output correctly formatted for item lore components. +
+ +
+ Lore output + Get an output correctly formatted for item lore components. +
-
-
- -
- Open source - All the code is public, and we maintain and update it regularly. +
+ +
+ Open source + All the code is public, and we maintain and update it + regularly. +
-
-

Feedback? We are a small team and would love to hear any feedback you have! Let us know what you think on our Discord server.

+

+ Feedback? We are a small team and would love to + hear any feedback you have! Let us know what you think on our + Discord server. +

-

Bugs? Report them on our Discord server, or create an issue on Github.

+

+ Bugs? Report them on our + Discord server, + or create an issue on + Github. +

-

This site is maintained by Datapack Hub, and is not endorsed by Mojang Studios.

- - +

+ This site is maintained by Datapack Hub, and is not endorsed by Mojang + Studios. +

+ + +
-
-{/if} \ No newline at end of file +{/if} diff --git a/src/lib/components/modals/BookDetailsModal.svelte b/src/lib/components/modals/BookDetailsModal.svelte index ce0360b..3dba977 100644 --- a/src/lib/components/modals/BookDetailsModal.svelte +++ b/src/lib/components/modals/BookDetailsModal.svelte @@ -20,11 +20,21 @@ Book title The title of the book. - + - +
diff --git a/src/lib/components/modals/ClickEventModal.svelte b/src/lib/components/modals/ClickEventModal.svelte index 1edaaf0..15cb82f 100644 --- a/src/lib/components/modals/ClickEventModal.svelte +++ b/src/lib/components/modals/ClickEventModal.svelte @@ -53,11 +53,7 @@ bind:value={clickEventValue} /> {:else if clickEventType == "change_page"}

Page to go to

- + {:else if clickEventType == "open_dialog"}

Dialog ID

{/if} -
+
{#if clickEventType} - - - - {#if $outputVersion.index >= 2} - {/if} @@ -143,20 +152,24 @@ {/if} {#if customType === "translate"} -
- -
+
+ +
Translate Key - A translate key changes based on the player's language. + A translate key changes based on the player's language.
-
-
+
Translate Key - +
- -
+
Fallback - +
-
+
Parameters - +
{#each customValues.translate.params ?? [] as p, i} @@ -226,20 +244,24 @@ Add Translate Key {:else if customType === "score"} -
- -
+
+ +
Scoreboard Value - Display a number from a scoreboard. This only works in certain contexts. + Display a number from a scoreboard. This only works in certain contexts.
-
-
+
Objective - +
-
+
Player Name or Selector - +
{:else if customType === "nbt"} -
- -
+
+ +
NBT Value - Display a value from an NBT store. This only works in certain contexts. + Display a value from an NBT store. This only works in certain contexts.
-
-
+
NBT Source Type - +
{#if customValues.nbt.sourceType === "storage"} -
+
Storage ID - +
-
+
NBT Path - +
@@ -339,9 +376,12 @@ Add NBT Value {:else if customValues.nbt.sourceType === "entity"} -
+
Entity Selector - +
-
+
NBT Path - +
@@ -384,9 +429,12 @@ Add NBT Value {:else if customValues.nbt.sourceType === "block"} -
+
Block - +
-
+
NBT Path - +
@@ -430,20 +483,25 @@ {/if} {:else if customType === "keybind"} -
- -
+
+ +
Keybind - Display the player's keybind for an action (e.g. "key.jump" would be "Space" by default). + Display the player's keybind for an action (e.g. "key.jump" would be "Space" by + default).
-
-
+
Keybind - +
{:else if customType === "selector"} -
- -
+
+ +
Selector - Display an entity name, or a list of entity names. This only works in certain contexts. + Display an entity name, or a list of entity names. This only works in certain + contexts.
-
-
+
Selector - +
{:else if customType === "object"} -
- -
+
+ +
Object - Display either a game texture (e.g. a block) or the front of a player head. + Display either a game texture (e.g. a block) or the front of a player head.
-
-
+
Object Type - +
{#if customValues.object.object == "atlas"} -
+
Atlas - +
-
+
Sprite - +
- + {:else if customValues.object.object == "player"} -
+
Username - +
diff --git a/src/lib/components/modals/FontPickerModal.svelte b/src/lib/components/modals/FontPickerModal.svelte index 5c43c0f..fbb55dc 100644 --- a/src/lib/components/modals/FontPickerModal.svelte +++ b/src/lib/components/modals/FontPickerModal.svelte @@ -97,11 +97,7 @@

Or, if you want to use a custom font without importing it, enter the ID:

- +

Note: in order for a custom font to show up ingame, you will need to add it with a diff --git a/src/lib/components/modals/InsertImageModal.svelte b/src/lib/components/modals/InsertImageModal.svelte index a647626..6f21bb1 100644 --- a/src/lib/components/modals/InsertImageModal.svelte +++ b/src/lib/components/modals/InsertImageModal.svelte @@ -139,14 +139,14 @@

Click to upload image

or drag and drop an image here

- + {:else} diff --git a/src/lib/components/modals/topbar/SettingsModal.svelte b/src/lib/components/modals/topbar/SettingsModal.svelte index f53dfbc..89125f9 100644 --- a/src/lib/components/modals/topbar/SettingsModal.svelte +++ b/src/lib/components/modals/topbar/SettingsModal.svelte @@ -53,10 +53,7 @@
- diff --git a/src/lib/components/text/BookMiniRenderer.svelte b/src/lib/components/text/BookMiniRenderer.svelte index 823ad9f..199f6a3 100644 --- a/src/lib/components/text/BookMiniRenderer.svelte +++ b/src/lib/components/text/BookMiniRenderer.svelte @@ -26,35 +26,37 @@ let { value }: { value: JSONContent } = $props(); let html: string = $derived( - browser ? generateHTML(value, [ - StarterKit.configure({ - blockquote: false, - bulletList: false, - codeBlock: false, - hardBreak: false, - heading: false, - horizontalRule: false, - listItem: false, - orderedList: false, - link: false, - }), - Color, - FixedTextStyle, - Obfuscation, - ClickEventMark, - HoverEventMark, - ShadowColorMark, - ScoreNode, - TranslateNode, - BlockNBTNode, - StorageNBTNode, - EntityNBTNode, - KeybindNode, - SelectorNode, - AtlasObjectNode, - PlayerObjectNode, - FontsExtension, - ]) : "" + browser + ? generateHTML(value, [ + StarterKit.configure({ + blockquote: false, + bulletList: false, + codeBlock: false, + hardBreak: false, + heading: false, + horizontalRule: false, + listItem: false, + orderedList: false, + link: false, + }), + Color, + FixedTextStyle, + Obfuscation, + ClickEventMark, + HoverEventMark, + ShadowColorMark, + ScoreNode, + TranslateNode, + BlockNBTNode, + StorageNBTNode, + EntityNBTNode, + KeybindNode, + SelectorNode, + AtlasObjectNode, + PlayerObjectNode, + FontsExtension, + ]) + : "", ); onMount(() => { diff --git a/src/lib/globals.ts b/src/lib/globals.ts new file mode 100644 index 0000000..b74b696 --- /dev/null +++ b/src/lib/globals.ts @@ -0,0 +1 @@ +export const welcomeScreenFormat = "1"; diff --git a/src/lib/settings.ts b/src/lib/settings.ts index b9c7f05..51262a5 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -35,8 +35,7 @@ export const appSettings: Writable = createPersistentStore("settings", syntaxHighlight: true, realisticLineHeight: false, hideSelectionExport: true, - fontSize: 1 + fontSize: 1, }); export const outputVersion = writable(versions[versions.length - 1]); -export const welcomeScreenFormat = "1"; \ No newline at end of file diff --git a/src/routes/book/+page.svelte b/src/routes/book/+page.svelte index e1cb319..2c93cd0 100644 --- a/src/routes/book/+page.svelte +++ b/src/routes/book/+page.svelte @@ -248,7 +248,10 @@ {editor} {welcomeScreenVisible} /> -
(BETA) The book editor is in active development. Report bugs and expect incomplete/broken features (also keep backups!).
+
+ (BETA) The book editor is in active development. Report bugs and expect incomplete/broken + features (also keep backups!). +
@@ -269,7 +272,7 @@ }} class="page-preview">
+ class="font-minecraft text-book h-61 overflow-clip px-6 pt-11 leading-3.5 wrap-break-word">
@@ -300,14 +303,22 @@
{/each}
-
+
+
+
+
+
- +
@@ -427,7 +438,9 @@

- {pageJSONs.map((j) => [convert(j, shouldOptimise)]).join(",").length + title.length + author.length} characters + {pageJSONs.map((j) => [convert(j, shouldOptimise)]).join(",").length + + title.length + + author.length} characters

{/if}
diff --git a/src/tests/e2e/basic.test.ts b/src/tests/e2e/basic.e2e.ts similarity index 98% rename from src/tests/e2e/basic.test.ts rename to src/tests/e2e/basic.e2e.ts index 92d8a75..63549a5 100644 --- a/src/tests/e2e/basic.test.ts +++ b/src/tests/e2e/basic.e2e.ts @@ -6,7 +6,7 @@ test.beforeEach(async ({ page }) => { await page.waitForLoadState(); await page.evaluate((format) => { - localStorage.setItem('hasSeenWelcome', format); + localStorage.setItem("hasSeenWelcome", format); }, welcomeScreenFormat); await page.reload(); diff --git a/src/tests/e2e/modals.test.ts b/src/tests/e2e/modals.e2e.ts similarity index 95% rename from src/tests/e2e/modals.test.ts rename to src/tests/e2e/modals.e2e.ts index 0afb629..5201c40 100644 --- a/src/tests/e2e/modals.test.ts +++ b/src/tests/e2e/modals.e2e.ts @@ -6,7 +6,7 @@ test.beforeEach(async ({ page }) => { await page.waitForLoadState(); await page.evaluate((format) => { - localStorage.setItem('hasSeenWelcome', format); + localStorage.setItem("hasSeenWelcome", format); }, welcomeScreenFormat); await page.reload(); From b259675883e4bc7bbe6930893a97703bcab5923a Mon Sep 17 00:00:00 2001 From: Silabear <56885288+Silabear@users.noreply.github.com> Date: Tue, 1 Sep 2026 22:17:03 +0100 Subject: [PATCH 14/27] fix sidebar height problem --- src/routes/book/+page.svelte | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/book/+page.svelte b/src/routes/book/+page.svelte index 2c93cd0..16d56db 100644 --- a/src/routes/book/+page.svelte +++ b/src/routes/book/+page.svelte @@ -255,12 +255,12 @@ -
+