From 61e8bf051b7976747451e41143c0a14b61331989 Mon Sep 17 00:00:00 2001 From: Jim Zhou Date: Wed, 29 Apr 2026 19:40:05 +1200 Subject: [PATCH 1/5] Fixed Text highlight tool --- backend/src/db/db-connect.js | 8 +++- frontend/src/features/authoring/text/Text.tsx | 2 + .../features/authoring/text/TextHighlight.tsx | 44 +++++++++++++++++++ frontend/src/features/authoring/text/build.ts | 21 +++++++-- frontend/src/features/authoring/text/types.ts | 1 + 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 frontend/src/features/authoring/text/TextHighlight.tsx diff --git a/backend/src/db/db-connect.js b/backend/src/db/db-connect.js index 9a3f6235..aaf93e1b 100644 --- a/backend/src/db/db-connect.js +++ b/backend/src/db/db-connect.js @@ -3,7 +3,13 @@ import dotenv from "dotenv"; dotenv.config(); -const DEFAULT_CONNECTION_STRING = `mongodb+srv://${process.env.MONGODB_USER}:${process.env.MONGODB_PASSWORD}@primary.rjlaw.mongodb.net/primary?retryWrites=true&w=majority&appName=Primary`; +const DEFAULT_CONNECTION_STRING = process.env.MONGODB_URI; + +if (!DEFAULT_CONNECTION_STRING) { + throw new Error( + "Missing MONGODB_URI. Add it to backend/.env before starting the server." + ); +} /** * This function begins the process of connecting to the database, and returns a promise that will diff --git a/frontend/src/features/authoring/text/Text.tsx b/frontend/src/features/authoring/text/Text.tsx index 2d224fdd..fcb3e479 100644 --- a/frontend/src/features/authoring/text/Text.tsx +++ b/frontend/src/features/authoring/text/Text.tsx @@ -1,6 +1,7 @@ import type { VisualDocument } from "./types"; import Cursor from "./Cursor.tsx"; import Highlight from "./Highlight"; +import TextHighlight from "./TextHighlight"; import Rectangle from "../canvas/Rectangle"; import { buildStyle } from "./build"; import useEditorStore from "../stores/editor"; @@ -57,6 +58,7 @@ function Text({ doc, editable }: { doc: VisualDocument; editable?: boolean }) { return ( + {isSelected && } {buildGroups(doc)} diff --git a/frontend/src/features/authoring/text/TextHighlight.tsx b/frontend/src/features/authoring/text/TextHighlight.tsx new file mode 100644 index 00000000..4e097f32 --- /dev/null +++ b/frontend/src/features/authoring/text/TextHighlight.tsx @@ -0,0 +1,44 @@ +import type { VisualDocument } from "./types"; +import { expandToPath } from "../util"; + +function TextHighlight({ doc }: { doc: VisualDocument }) { + const { bounds, blocks } = doc; + const origin = { + x: bounds.x + bounds.width / 2, + y: bounds.y + bounds.height / 2, + }; + + const highlights = []; + + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i]; + for (let j = 0; j < block.lines.length; j++) { + const line = block.lines[j]; + for (let k = 0; k < line.spans.length; k++) { + const span = line.spans[k]; + if (!span.highlightColor) continue; + + highlights.push( + + ); + } + } + } + + return <>{highlights}; +} + +export default TextHighlight; + diff --git a/frontend/src/features/authoring/text/build.ts b/frontend/src/features/authoring/text/build.ts index 5345da8a..e91f88e3 100644 --- a/frontend/src/features/authoring/text/build.ts +++ b/frontend/src/features/authoring/text/build.ts @@ -30,6 +30,14 @@ function buildFont(styles: Partial) { return `${fontStyle} ${fontWeight} ${fontSize}px/${lineHeight! * fontSize!}px "${fontFamily}"`; } +function getHighlightColor( + docStyle?: Partial, + blockStyle?: Partial, + spanStyle?: Partial +) { + return spanStyle?.highlightColor ?? blockStyle?.highlightColor ?? docStyle?.highlightColor; +} + function setFont(style?: Partial) { if (!style?.fontFamily || !style?.fontSize) return; ctx.font = buildFont(style); @@ -95,7 +103,9 @@ interface SpanRef { function buildVisualLines( spans: ModelSpan[], maxWidth: number, - blockStyle: BaseTextStyle + blockStyle: BaseTextStyle, + docStyle?: Partial, + rawBlockStyle?: Partial ) { const lines: VisualLine[] = []; const { alignment, lineHeight } = blockStyle; @@ -136,6 +146,7 @@ function buildVisualLines( text: ref.text, charOffsets: generateOffsets(ref.text, style), style, + highlightColor: getHighlightColor(docStyle, rawBlockStyle, ref.span.style), width: metrics.width, x: currentLine.width, parentId: ref.index, @@ -170,6 +181,7 @@ function buildVisualLines( currentLine.spans.push({ text: token, style, + highlightColor: getHighlightColor(docStyle, rawBlockStyle, span.style), width: spaceWidth, x: currentLine.width, parentId: j, @@ -208,7 +220,8 @@ function buildBlock( block: ModelBlock, offset: number, maxWidth: number, - blockStyle: BaseTextStyle + blockStyle: BaseTextStyle, + docStyle?: Partial ) { const visualBlock: VisualBlock = { lines: [], @@ -217,7 +230,7 @@ function buildBlock( height: 0, }; - const lines = buildVisualLines(block.spans, maxWidth, blockStyle); + const lines = buildVisualLines(block.spans, maxWidth, blockStyle, docStyle, block.style); const { y, height } = lines[lines.length - 1]; visualBlock.height = y + height; @@ -233,7 +246,7 @@ export function buildVisualDocument(doc: ModelDocument): VisualDocument { let offset = 0; for (let i = 0; i < blocks.length; i++) { const squashed = squash(style, blocks[i].style); - const visual = buildBlock(blocks[i], offset, bounds.width, squashed); + const visual = buildBlock(blocks[i], offset, bounds.width, squashed, style); offset += visual.height; visualBlocks.push(visual); } diff --git a/frontend/src/features/authoring/text/types.ts b/frontend/src/features/authoring/text/types.ts index 885a3b41..d0d87654 100644 --- a/frontend/src/features/authoring/text/types.ts +++ b/frontend/src/features/authoring/text/types.ts @@ -3,6 +3,7 @@ import type { BaseTextStyle, RelativeBounds } from "../types"; export interface VisualSpan { text: string; style: BaseTextStyle; + highlightColor?: HexString; x: number; width: number; charOffsets: number[]; From 4c0fb2347a233cd3220aeacff289d6da8b5d516b Mon Sep 17 00:00:00 2001 From: Jim Zhou Date: Wed, 6 May 2026 20:50:22 +1200 Subject: [PATCH 2/5] Removed redudant work --- .../features/authoring/text/TextHighlight.tsx | 5 ++-- frontend/src/features/authoring/text/build.ts | 23 ++++--------------- frontend/src/features/authoring/text/types.ts | 1 - 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/frontend/src/features/authoring/text/TextHighlight.tsx b/frontend/src/features/authoring/text/TextHighlight.tsx index 4e097f32..c297e488 100644 --- a/frontend/src/features/authoring/text/TextHighlight.tsx +++ b/frontend/src/features/authoring/text/TextHighlight.tsx @@ -16,7 +16,8 @@ function TextHighlight({ doc }: { doc: VisualDocument }) { const line = block.lines[j]; for (let k = 0; k < line.spans.length; k++) { const span = line.spans[k]; - if (!span.highlightColor) continue; + const highlightColor = span.style.highlightColor; + if (!highlightColor) continue; highlights.push( ); diff --git a/frontend/src/features/authoring/text/build.ts b/frontend/src/features/authoring/text/build.ts index e91f88e3..f5545a92 100644 --- a/frontend/src/features/authoring/text/build.ts +++ b/frontend/src/features/authoring/text/build.ts @@ -18,7 +18,7 @@ const fallback: BaseTextStyle = { fontStyle: "normal", textDecoration: "none", textColor: "#000000", - highlightColor: "#000000", + highlightColor: "#00000000", }; function measure(text: string) { @@ -30,14 +30,6 @@ function buildFont(styles: Partial) { return `${fontStyle} ${fontWeight} ${fontSize}px/${lineHeight! * fontSize!}px "${fontFamily}"`; } -function getHighlightColor( - docStyle?: Partial, - blockStyle?: Partial, - spanStyle?: Partial -) { - return spanStyle?.highlightColor ?? blockStyle?.highlightColor ?? docStyle?.highlightColor; -} - function setFont(style?: Partial) { if (!style?.fontFamily || !style?.fontSize) return; ctx.font = buildFont(style); @@ -103,9 +95,7 @@ interface SpanRef { function buildVisualLines( spans: ModelSpan[], maxWidth: number, - blockStyle: BaseTextStyle, - docStyle?: Partial, - rawBlockStyle?: Partial + blockStyle: BaseTextStyle ) { const lines: VisualLine[] = []; const { alignment, lineHeight } = blockStyle; @@ -146,7 +136,6 @@ function buildVisualLines( text: ref.text, charOffsets: generateOffsets(ref.text, style), style, - highlightColor: getHighlightColor(docStyle, rawBlockStyle, ref.span.style), width: metrics.width, x: currentLine.width, parentId: ref.index, @@ -181,7 +170,6 @@ function buildVisualLines( currentLine.spans.push({ text: token, style, - highlightColor: getHighlightColor(docStyle, rawBlockStyle, span.style), width: spaceWidth, x: currentLine.width, parentId: j, @@ -220,8 +208,7 @@ function buildBlock( block: ModelBlock, offset: number, maxWidth: number, - blockStyle: BaseTextStyle, - docStyle?: Partial + blockStyle: BaseTextStyle ) { const visualBlock: VisualBlock = { lines: [], @@ -230,7 +217,7 @@ function buildBlock( height: 0, }; - const lines = buildVisualLines(block.spans, maxWidth, blockStyle, docStyle, block.style); + const lines = buildVisualLines(block.spans, maxWidth, blockStyle); const { y, height } = lines[lines.length - 1]; visualBlock.height = y + height; @@ -246,7 +233,7 @@ export function buildVisualDocument(doc: ModelDocument): VisualDocument { let offset = 0; for (let i = 0; i < blocks.length; i++) { const squashed = squash(style, blocks[i].style); - const visual = buildBlock(blocks[i], offset, bounds.width, squashed, style); + const visual = buildBlock(blocks[i], offset, bounds.width, squashed); offset += visual.height; visualBlocks.push(visual); } diff --git a/frontend/src/features/authoring/text/types.ts b/frontend/src/features/authoring/text/types.ts index d0d87654..885a3b41 100644 --- a/frontend/src/features/authoring/text/types.ts +++ b/frontend/src/features/authoring/text/types.ts @@ -3,7 +3,6 @@ import type { BaseTextStyle, RelativeBounds } from "../types"; export interface VisualSpan { text: string; style: BaseTextStyle; - highlightColor?: HexString; x: number; width: number; charOffsets: number[]; From 9d0732a69c7c2a7d4f83f917563a2c704978509b Mon Sep 17 00:00:00 2001 From: Jim Zhou Date: Wed, 6 May 2026 21:00:16 +1200 Subject: [PATCH 3/5] Formatting --- frontend/src/features/authoring/text/TextHighlight.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/features/authoring/text/TextHighlight.tsx b/frontend/src/features/authoring/text/TextHighlight.tsx index c297e488..4f3dbd4e 100644 --- a/frontend/src/features/authoring/text/TextHighlight.tsx +++ b/frontend/src/features/authoring/text/TextHighlight.tsx @@ -42,4 +42,3 @@ function TextHighlight({ doc }: { doc: VisualDocument }) { } export default TextHighlight; - From 319bd0c06a2d31aaffca4b0fc3783166923bf6e5 Mon Sep 17 00:00:00 2001 From: Jim Zhou Date: Mon, 18 May 2026 19:47:20 +1200 Subject: [PATCH 4/5] feat(authoring): add reusable keyboard shortcut system --- .../authoring/handlers/keyboard/keyboard.ts | 37 +---- .../authoring/handlers/keyboard/shortcuts.ts | 148 ++++++++++++++++++ .../authoring/handlers/keyboard/text.ts | 5 +- .../authoring/handlers/keyboard/utils.ts | 67 ++++++++ frontend/src/features/authoring/text/style.ts | 38 +++++ .../features/authoring/topbar/TextSection.tsx | 26 +-- 6 files changed, 262 insertions(+), 59 deletions(-) create mode 100644 frontend/src/features/authoring/handlers/keyboard/shortcuts.ts create mode 100644 frontend/src/features/authoring/handlers/keyboard/utils.ts create mode 100644 frontend/src/features/authoring/text/style.ts diff --git a/frontend/src/features/authoring/handlers/keyboard/keyboard.ts b/frontend/src/features/authoring/handlers/keyboard/keyboard.ts index f623c0c8..d667d7f0 100644 --- a/frontend/src/features/authoring/handlers/keyboard/keyboard.ts +++ b/frontend/src/features/authoring/handlers/keyboard/keyboard.ts @@ -1,50 +1,25 @@ -import { redo, undo } from "../../scene/history"; -import { - bringForward, - bringToFront, - duplicateComponent, - modifyComponentProp, - sendBackward, - sendToBack, -} from "../../scene/operations/component"; +import { modifyComponentProp } from "../../scene/operations/component"; import { remove } from "../../scene/operations/modifiers"; import useEditorStore from "../../stores/editor"; import type { Vec2 } from "../../types"; import { translate } from "../../util"; +import { handleShortcut } from "./shortcuts"; import { handleTextMode } from "./text"; +import { isEditableShortcutTarget } from "./utils"; export function handleGlobal(e: KeyboardEvent) { const mode = useEditorStore.getState().mode; const { selected } = useEditorStore.getState(); // don't want to interfere with input elements - const target = e.target as HTMLElement; - if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") return; + if (isEditableShortcutTarget(e.target)) return; + + if (handleShortcut(e)) return; if (mode.includes("text")) handleTextMode(e); - else if (e.ctrlKey || e.metaKey) handleCtrlOperations(e); else if (selected) handleComponentOperations(e, selected); } -function handleCtrlOperations(e: KeyboardEvent) { - const { selected, setSelected } = useEditorStore.getState(); - - // TODO ADD e.key === "a" for all components or create new function for cmd - if (e.key === "z") undo(); - else if (e.key === "y") redo(); - else if (e.key === "d" && selected) { - e.preventDefault(); - const id = duplicateComponent(selected); - setSelected(id); - } else if (e.key === "ArrowUp" && selected) { - if (e.shiftKey) bringToFront(selected); - else bringForward(selected); - } else if (e.key === "ArrowDown" && selected) { - if (e.shiftKey) sendToBack(selected); - else sendBackward(selected); - } -} - function handleComponentOperations(e: KeyboardEvent, selected: string) { const { setSelected } = useEditorStore.getState(); diff --git a/frontend/src/features/authoring/handlers/keyboard/shortcuts.ts b/frontend/src/features/authoring/handlers/keyboard/shortcuts.ts new file mode 100644 index 00000000..ff59af47 --- /dev/null +++ b/frontend/src/features/authoring/handlers/keyboard/shortcuts.ts @@ -0,0 +1,148 @@ +import { redo, undo } from "../../scene/history"; +import { + bringForward, + bringToFront, + duplicateComponent, + sendBackward, + sendToBack, +} from "../../scene/operations/component"; +import { remove } from "../../scene/operations/modifiers"; +import useEditorStore from "../../stores/editor"; +import { handleSelectAll } from "./text"; +import { matchesShortcut } from "./utils"; +import { setTextStyle } from "../../text/style"; + +type Shortcut = { + combos: string[]; + when?: () => boolean; + run: () => void; +}; + +function toggleTextStyle( + selected: string, + prop: "fontWeight" | "fontStyle" | "textDecoration", + enabledValue: "bold" | "italic" | "underline", + disabledValue: "normal" | "none" +) { + const current = useEditorStore.getState().activeStyle; + const nextValue = + current?.[prop] === enabledValue ? disabledValue : enabledValue; + setTextStyle(selected, prop, nextValue); +} + +const shortcuts: Shortcut[] = [ + { + combos: ["mod+z"], + run: () => undo(), + }, + { + combos: ["mod+shift+z", "mod+y"], + run: () => redo(), + }, + { + combos: ["mod+d"], + when: () => Boolean(useEditorStore.getState().selected), + run: () => { + const { selected, setSelected } = useEditorStore.getState(); + if (!selected) return; + setSelected(duplicateComponent(selected)); + }, + }, + { + combos: ["backspace", "delete"], + when: () => { + const { mode, selected } = useEditorStore.getState(); + return !mode.includes("text") && Boolean(selected); + }, + run: () => { + const { selected, setSelected } = useEditorStore.getState(); + if (!selected) return; + remove(selected); + setSelected(null); + }, + }, + { + combos: ["mod+arrowup"], + when: () => Boolean(useEditorStore.getState().selected), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + bringForward(selected); + }, + }, + { + combos: ["mod+shift+arrowup"], + when: () => Boolean(useEditorStore.getState().selected), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + bringToFront(selected); + }, + }, + { + combos: ["mod+arrowdown"], + when: () => Boolean(useEditorStore.getState().selected), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + sendBackward(selected); + }, + }, + { + combos: ["mod+shift+arrowdown"], + when: () => Boolean(useEditorStore.getState().selected), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + sendToBack(selected); + }, + }, + { + combos: ["mod+a"], + when: () => useEditorStore.getState().mode.includes("text"), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + handleSelectAll(selected); + }, + }, + { + combos: ["mod+b"], + when: () => useEditorStore.getState().mode.includes("text"), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + toggleTextStyle(selected, "fontWeight", "bold", "normal"); + }, + }, + { + combos: ["mod+i"], + when: () => useEditorStore.getState().mode.includes("text"), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + toggleTextStyle(selected, "fontStyle", "italic", "normal"); + }, + }, + { + combos: ["mod+u"], + when: () => useEditorStore.getState().mode.includes("text"), + run: () => { + const { selected } = useEditorStore.getState(); + if (!selected) return; + toggleTextStyle(selected, "textDecoration", "underline", "none"); + }, + }, +]; + +export function handleShortcut(e: KeyboardEvent) { + for (const shortcut of shortcuts) { + if (!shortcut.combos.some((combo) => matchesShortcut(e, combo))) continue; + if (shortcut.when && !shortcut.when()) continue; + e.preventDefault(); + shortcut.run(); + return true; + } + + return false; +} diff --git a/frontend/src/features/authoring/handlers/keyboard/text.ts b/frontend/src/features/authoring/handlers/keyboard/text.ts index 3af194d1..3c90410d 100644 --- a/frontend/src/features/authoring/handlers/keyboard/text.ts +++ b/frontend/src/features/authoring/handlers/keyboard/text.ts @@ -23,10 +23,7 @@ export function handleTextMode(e: KeyboardEvent) { const { selected } = useEditorStore.getState(); if (!selected) return; - if ((e.metaKey || e.ctrlKey) && e.key == "a") { - e.preventDefault(); - handleSelectAll(selected); - } else if (e.key.startsWith("Arrow") || ["Home", "End"].includes(e.key)) { + if (e.key.startsWith("Arrow") || ["Home", "End"].includes(e.key)) { handleNavigation(e, selected); } else { handleEditing(e, selected); diff --git a/frontend/src/features/authoring/handlers/keyboard/utils.ts b/frontend/src/features/authoring/handlers/keyboard/utils.ts new file mode 100644 index 00000000..748ff641 --- /dev/null +++ b/frontend/src/features/authoring/handlers/keyboard/utils.ts @@ -0,0 +1,67 @@ +const MAC_PLATFORMS = ["mac", "iphone", "ipad", "ipod"]; + +function getPlatform() { + return ( + globalThis.navigator?.platform ?? + globalThis.navigator?.userAgent ?? + "" + ).toLowerCase(); +} + +export function isMacPlatform() { + const platform = getPlatform(); + return MAC_PLATFORMS.some((name) => platform.includes(name)); +} + +export function isPrimaryShortcutModifier(e: KeyboardEvent) { + return isMacPlatform() ? e.metaKey : e.ctrlKey; +} + +export function isEditableShortcutTarget(target: EventTarget | null) { + if (!(target instanceof HTMLElement)) return false; + + return ( + target.isContentEditable || + ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName) + ); +} + +function normalizeKey(key: string) { + return key.trim().toLowerCase(); +} + +function hasOnlyRequestedModifiers(e: KeyboardEvent, modifiers: Set) { + const usesPrimary = modifiers.has("mod"); + const usesCtrl = modifiers.has("ctrl"); + const usesMeta = modifiers.has("meta"); + const usesShift = modifiers.has("shift"); + const usesAlt = modifiers.has("alt") || modifiers.has("option"); + + if (usesPrimary) { + if (!isPrimaryShortcutModifier(e)) return false; + } else { + if (usesCtrl !== e.ctrlKey) return false; + if (usesMeta !== e.metaKey) return false; + } + if (usesShift !== e.shiftKey) return false; + if (usesAlt !== e.altKey) return false; + + return true; +} + +export function matchesShortcut(e: KeyboardEvent, combo: string) { + if (e.repeat) return false; + + const parts = combo + .toLowerCase() + .split("+") + .map((part) => part.trim()) + .filter(Boolean); + + const key = normalizeKey(parts.pop() ?? ""); + const modifiers = new Set(parts); + + if (!hasOnlyRequestedModifiers(e, modifiers)) return false; + + return normalizeKey(e.key) === key; +} diff --git a/frontend/src/features/authoring/text/style.ts b/frontend/src/features/authoring/text/style.ts new file mode 100644 index 00000000..d1eacc36 --- /dev/null +++ b/frontend/src/features/authoring/text/style.ts @@ -0,0 +1,38 @@ +import { modifyComponentProp } from "../scene/operations/component"; +import { applySelectionStyle } from "../scene/operations/text"; +import useEditorStore from "../stores/editor"; +import { syncVisualCursor } from "./cursor"; +import type { BaseTextStyle } from "../types"; + +type TextStyleValue = BaseTextStyle[keyof BaseTextStyle]; + +export function setTextStyle( + selected: string, + prop: keyof BaseTextStyle, + value: TextStyleValue +) { + const { selection, activeStyle, setActiveStyle, setSelection } = + useEditorStore.getState(); + + if (selection?.end) { + const newSelection = applySelectionStyle(selected, selection, { + [prop]: value, + }); + setSelection(newSelection); + syncVisualCursor(); + } else if (selection?.start) { + if (prop === "lineHeight" || prop === "alignment") { + modifyComponentProp( + selected, + `document.blocks.${selection.start.blockI}.style.${prop}`, + value + ); + } else { + modifyComponentProp(selected, `document.style.${prop}`, value); + } + } else { + modifyComponentProp(selected, `document.style.${prop}`, value); + } + + setActiveStyle({ ...(activeStyle ?? {}), [prop]: value } as BaseTextStyle); +} diff --git a/frontend/src/features/authoring/topbar/TextSection.tsx b/frontend/src/features/authoring/topbar/TextSection.tsx index e9fc81d7..4ee88e5e 100644 --- a/frontend/src/features/authoring/topbar/TextSection.tsx +++ b/frontend/src/features/authoring/topbar/TextSection.tsx @@ -14,40 +14,18 @@ import NumberInput from "../wrapper/NumberInput"; import ToggleInput from "../wrapper/ToggleInput"; import ChromePicker from "../wrapper/ChromePicker"; import MultiInput from "../wrapper/MultiInput"; -import { applySelectionStyle } from "../scene/operations/text"; -import { modifyComponentProp } from "../scene/operations/component"; import type { BaseTextStyle } from "../types"; -import { syncVisualCursor } from "../text/cursor"; +import { setTextStyle } from "../text/style"; function TextSection() { const selected = useEditorStore((state) => state.selected)!; // this comp only renders when a text el is selected - const selection = useEditorStore((state) => state.selection); const style = useEditorStore((state) => state.activeStyle); - const setStyle = useEditorStore((state) => state.setActiveStyle); if (!style) return null; function modifyStyle(prop: keyof BaseTextStyle, value: string | number) { - if (selection?.end) { - const newSelection = applySelectionStyle(selected, selection, { - [prop]: value, - }); - useEditorStore.getState().setSelection(newSelection); - syncVisualCursor(); - } else if (selection?.start) { - if (prop === "lineHeight" || prop === "alignment") { - modifyComponentProp( - selected, - `document.blocks.${selection.start.blockI}.style.${prop}`, - value - ); - } - } else { - modifyComponentProp(selected, `document.style.${prop}`, value); - } - - setStyle({ ...style!, [prop]: value }); + setTextStyle(selected, prop, value); } return ( From 45b30f0e241fe4c6ad097c5189cec20fb9db3e3c Mon Sep 17 00:00:00 2001 From: FridgeProtector1 <81726935+FridgeProtector1@users.noreply.github.com> Date: Tue, 19 May 2026 23:25:14 +1200 Subject: [PATCH 5/5] Removing mongoDB connection difference It was in the git ignore so I dont know why its still here like its haunting me or smth. --- backend/src/db/db-connect.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/backend/src/db/db-connect.js b/backend/src/db/db-connect.js index aaf93e1b..9a3f6235 100644 --- a/backend/src/db/db-connect.js +++ b/backend/src/db/db-connect.js @@ -3,13 +3,7 @@ import dotenv from "dotenv"; dotenv.config(); -const DEFAULT_CONNECTION_STRING = process.env.MONGODB_URI; - -if (!DEFAULT_CONNECTION_STRING) { - throw new Error( - "Missing MONGODB_URI. Add it to backend/.env before starting the server." - ); -} +const DEFAULT_CONNECTION_STRING = `mongodb+srv://${process.env.MONGODB_USER}:${process.env.MONGODB_PASSWORD}@primary.rjlaw.mongodb.net/primary?retryWrites=true&w=majority&appName=Primary`; /** * This function begins the process of connecting to the database, and returns a promise that will