Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,25 @@ strong {
}

.clickEvent {
background-color: #3c3c40aa;
border-bottom: solid #ff5555 2px;
padding-top: 2px;
padding-bottom: 2px;
background-color: #45454e80;
border: solid #45454e 1px;
border-bottom: solid #a868e7 2px;
z-index: 10;
--custom-source-align: top;
padding-bottom: 1px
}

.clickEvent:has(.hoverEvent) {
padding-bottom: 3px;
}

.hoverEvent {
background-color: #3c3c40aa;
border-bottom: solid #ffff55 2px;
padding-top: 4px;
background-color: #45454e80;
border: solid #45454e 1px;
border-bottom: solid #e57e2b 2px;
z-index: 10;
--custom-source-align: top;
padding-bottom: 1px
}

.obfuscated {
Expand Down
2 changes: 0 additions & 2 deletions src/lib/components/TopUI.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@

function importToEditor() {
const jsonContent = snbtToDocument(convertToTextOrEmpty(importText));
console.log("importing to editor", jsonContent);
editor?.commands.setContent(jsonContent, { emitUpdate: true });
importDialog?.close();
console.log("imported to editor");
}
</script>

Expand Down
165 changes: 165 additions & 0 deletions src/lib/components/modals/InsertImageModal.svelte

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might make sense to add a warning or limit to the image size. the minecraft chat can only really render images of a certain size, and i tried to upload a random image I had lying around and it took AGES for it to load in the editor. ik there is some text there already but there should be a hard limit, or at least a visual warning if you upload a file thats too big

Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<script lang="ts">
import type { Editor, JSONContent } from "@tiptap/core";
import Modal from "../Modal.svelte";
import IconUploadImage from "~icons/tabler/file-upload";

interface Props {
insertImageDialog?: Modal;
editor: Editor | undefined;
}

let { insertImageDialog = $bindable(), editor }: Props = $props();

let files: FileList | null = $state(null);
let dropZone: HTMLLabelElement | null = $state(null);
let sizeWarning = $state(false);

function handleDrop(event: DragEvent) {
event.preventDefault();
if ([...event.dataTransfer!.items].some((item) => item.kind === "file")) {
files = event.dataTransfer!.files;
event.dataTransfer!.clearData();
}
}

function insertImage() {
if (files && editor) {
const image = new Image();
const reader = new FileReader();

reader.addEventListener("load", () => {
const imageDataUrl = reader.result as string;

image.src = imageDataUrl;
image.onload = () => {
processImage(image);
image.remove();
};
});

reader.readAsDataURL(files[0]);
}
}

function checkFileSize(file: File) {
const reader = new FileReader();
reader.onload = () => {
const image = new Image();
image.src = reader.result as string;
image.onload = () => {
sizeWarning = image.width > 24 || image.height > 24
image.remove();
};
};
reader.readAsDataURL(file);
}

function handleDragOver(e: DragEvent) {
const fileItems = [...e.dataTransfer!.items].filter((item) => item.kind === "file");
if (fileItems.length > 0) {
e.preventDefault();
if (fileItems.some((item) => item.type.startsWith("image/"))) {
e.dataTransfer!.dropEffect = "copy";
} else {
e.dataTransfer!.dropEffect = "none";
}
}
}

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();
ctx?.drawImage(image, 0, 0);

if (!ctx) return [];

for (let y = 0; y < image.height; y++) {
for (let x = 0; x < image.width; x++) {
const pixel = ctx.getImageData(x, y, 1, 1);
const data = pixel.data;
if (data[3] !== 0) {
const hexColor = `#${data[0].toString(16).padStart(2, "0")}${data[1]
.toString(16)
.padStart(2, "0")}${data[2].toString(16).padStart(2, "0")}`;

completeContent.push({
type: "text",
marks: [{ type: "textStyle", attrs: { color: hexColor } }],
text: "█",
});
} else {
completeContent.push({ type: "text", text: " " });
}
}
completeContent.push({ type: "text", text: "\n" });
}

editor?.commands.insertContent(completeContent);

insertImageDialog?.close();
files = null;
return completeContent;
}

function handleWindowDrag(e: DragEvent) {
const fileItems = [...e.dataTransfer!.items].filter((item) => item.kind === "file");
if (fileItems.length > 0) {
e.preventDefault();
if (!dropZone?.contains(e.target as Node)) {
e.dataTransfer!.dropEffect = "none";
}
}
}

$effect(() => {
if(files && files.length > 0) {
checkFileSize(files[0]);
} else {
sizeWarning = false;
}
})
</script>

<svelte:window ondragover={handleWindowDrag} ondrop={(e) => e.preventDefault()} />

<Modal title="Insert Image" bind:this={insertImageDialog} key="M">
<div class="flex w-full flex-col space-y-2">
<p class="mb-2">A few things to note:</p>
<ul class="mb-4 list-inside list-disc text-sm text-zinc-400">
<li>Total transparency will become empty spaces</li>
<li>Non-zero alpha pixels will be converted to full alpha.</li>
<li>Large or complex images will produce a very long output</li>
<li>This tool makes no attempt to resize images</li>
</ul>
{#if !files}
<label
bind:this={dropZone}
for="image-upload"
class="flex h-32 cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed border-zinc-600"
ondragover={(e) => handleDragOver(e)}
ondrop={(e) => handleDrop(e)}>
<label for="image-upload" class="btn flex items-center gap-2">
<IconUploadImage /> Upload Image
</label>
<input type="file" accept="image/*" class="hidden" id="image-upload" bind:files />
<p class="my-2 text-sm text-zinc-400">OR</p>
<p>Drag and drop an image here</p>
</label>
{:else}
<p>Selected file: <span class="font-mono bg-zinc-900 p-1 rounded-md text-orange-300">{files[0].name}</span></p>
{#if sizeWarning}
<div class="border-red-500 border-2 bg-stone-900 p-2 rounded-md">
<p>This image may be too large to display properly, and may also cause performance issues to process, you have been warned!</p>
</div>
{/if}
<div class="flex gap-2">
<button class="btn" onclick={() => (files = null)}>Remove</button>
<button class="btn" onclick={insertImage}>Insert</button>
</div>
{/if}
</div>
</Modal>
1 change: 1 addition & 0 deletions src/lib/components/modals/KeybindModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{ keys: [modifierKey, "Shift", "I"], action: "Import Menu" },
{ keys: [modifierKey, "Shift", "E"], action: "Export Menu" },
{ keys: [modifierKey, "Shift", "L"], action: "Load a snapshot" },
{ keys: [modifierKey, "Shift", "M"], action: "Load an image (with block characters)" },
];
</script>

Expand Down
58 changes: 30 additions & 28 deletions src/lib/components/modals/topbar/SavedTextsModal.svelte

@Silabear Silabear Aug 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im not entirely sure about whether the design of this fits with the rest of the site, it feels kinda clunky now with the padding. i think maybe something can be done to improve this (e.g. having the delete/use button have the same style as the rest of the site, and putting both the renderer + buttons in a visible container)

Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,39 @@
</script>

<Modal title="Saved texts" bind:this={loadDialog} key="L">
<div class="flex w-full flex-col space-y-2">
<div class="">
{#if snapshots.length == 0}
<p>You have not saved anything yet!</p>
{/if}
{#each snapshots as snapshot (snapshot)}
<div class="flex flex-col">
<div class="rounded-t-md rounded-br-md bg-zinc-900">
<MiniRenderer value={snapshot} />
<div class="flex max-h-[50vh] flex-col overflow-y-auto">
{#each snapshots as snapshot (snapshot)}
<div class="flex flex-col border-b-2 border-zinc-600 py-2">
<div class="rounded-md bg-zinc-900">
<MiniRenderer value={snapshot} />
</div>
<div class="mt-1 flex w-fit gap-1">
<button
{@attach tooltip}
aria-label="Load snapshot"
class="btn"
onclick={() => {
editor?.commands.setContent(snapshot);
editor?.commands.focus();
}}>Load</button>
<button
{@attach tooltip}
aria-label="Delete snapshot"
class="btn"
onclick={() => {
snapshots = snapshots.filter(
(_, index) => index !== snapshots.indexOf(snapshot),
);
localStorage.setItem("snapshots", JSON.stringify(snapshots));
}}>Delete</button>
</div>
</div>
<div class="flex w-fit rounded-b-md bg-zinc-950">
<button
{@attach tooltip}
aria-label="Load snapshot"
class="border-r border-zinc-800 px-3 py-2 hover:bg-white/3"
onclick={() => {
editor?.commands.setContent(snapshot);
editor?.commands.focus();
}}><IconLoad /></button>
<button
{@attach tooltip}
aria-label="Delete snapshot"
class="px-3 py-2 hover:bg-white/3"
onclick={() => {
snapshots = snapshots.filter(
(_, index) => index !== snapshots.indexOf(snapshot),
);
localStorage.setItem("snapshots", JSON.stringify(snapshots));
}}><IconDelete /></button>
</div>
</div>
{/each}
<button class="btn" onclick={saveSnapshot}>Save current text</button>
{/each}
</div>
<button class="btn mt-6" onclick={saveSnapshot}>Save current text</button>
</div>
</Modal>
22 changes: 22 additions & 0 deletions src/lib/components/text/MiniRenderer.svelte

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually thank you so much, this was causing some problems in my other branch <33

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">
import { appSettings } from "$lib/settings";
import {
AtlasObjectNode,
BlockNBTNode,
Expand Down Expand Up @@ -59,6 +60,27 @@
],
content: value,
}).setEditable(false);

appSettings.subscribe(() => {
var el = document.querySelectorAll(".tiptap") as NodeListOf<HTMLElement>;

if ($appSettings.realisticLineHeight == true) {
var lineHeight = 0.8 + 0.2 * $appSettings.fontSize;
el.forEach((e) => {
e.style.lineHeight = lineHeight.toString() + "rem";
});
} else {
var lineHeight = 1.25 + 0.25 * $appSettings.fontSize;
el.forEach((e) => {
e.style.lineHeight = lineHeight.toString() + "rem";
});
}

var fontSize = 1 + 0.25 * $appSettings.fontSize;
el.forEach((e) => {
e.style.fontSize = fontSize.toString() + "rem";
});
});
});
</script>

Expand Down
11 changes: 11 additions & 0 deletions src/lib/components/toolbar/Toolbar.svelte

@Silabear Silabear Aug 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no problem with this, but its worth noting that this toolbar is getting insanely long now. We might have to collapse the colours into a dropdown-like thing in order to fit this all in. On some computer screens this will deffo wrap around, which isnt ideal

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import IconHoverEvent from "~icons/tabler/pointer";
import IconSquare from "~icons/tabler/square-filled";
import IconHollow from "~icons/tabler/square-x";
import IconUploadImage from "~icons/tabler/photo-scan";
import Modal from "../Modal.svelte";
import TextStyleButtons from "./TextStyleButtons.svelte";
import ToolbarButton from "./ToolbarButton.svelte";
Expand Down Expand Up @@ -47,6 +48,7 @@
let customDialog: Modal = $state()!;

let unicodeSelectorDialog: Modal = $state()!;
let insertImageDialog: Modal = $state()!;

function toTitleCase(str: string) {
return str.replace(
Expand Down Expand Up @@ -259,6 +261,11 @@

<div class="grow"></div>

<button
{@attach tooltip}
class="toolbar-btn nomob"
onclick={insertImageDialog?.open}
aria-label="Insert Image"><IconUploadImage /></button>
<button
{@attach tooltip}
class="toolbar-btn nomob"
Expand Down Expand Up @@ -297,6 +304,10 @@
<modal.default bind:customDialog bind:customType {editor} />
{/await}

{#await import("$lib/components/modals/InsertImageModal.svelte") then modal}
<modal.default bind:insertImageDialog {editor} />
{/await}

<Modal title="Custom Colour" bind:this={colourDialog} small nopad key="C">
<div class="flex w-full flex-col py-4">
<ColorPicker
Expand Down