From 613f9e12362b8f4a4e88f996dff40f6ad0352992 Mon Sep 17 00:00:00 2001 From: fccview Date: Thu, 16 Apr 2026 19:23:21 +0100 Subject: [PATCH 01/16] BOARD IS THE BAN IN KANBAN YOU NOOB --- app/_translations/en.json | 4 ++-- app/api/kanban/[boardId]/calendar/route.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/_translations/en.json b/app/_translations/en.json index 140e3720..80f08e50 100644 --- a/app/_translations/en.json +++ b/app/_translations/en.json @@ -469,7 +469,7 @@ "checklistType": "Checklist Type", "simpleChecklist": "Simple Checklist", "taskProject": "Task Project", - "kanbanBoard": "Kanban Board", + "kanbanBoard": "Kanban", "basicTodoItems": "Basic todo items", "withTimeTracking": "With time tracking", "noDescription": "No description", @@ -513,7 +513,7 @@ "convert": "Convert", "convertToSimpleChecklist": "Convert to simple checklist", "convertToTaskProject": "Convert to task project", - "convertToKanbanBoard": "Convert to Kanban Board", + "convertToKanbanBoard": "Convert to Kanban", "pasteYourList": "Paste your list (one item per line)", "addItems": "Add {count} Items", "itemsWillBeAdded": "{count} items will be added", diff --git a/app/api/kanban/[boardId]/calendar/route.ts b/app/api/kanban/[boardId]/calendar/route.ts index 2a3ec06e..d7340b16 100644 --- a/app/api/kanban/[boardId]/calendar/route.ts +++ b/app/api/kanban/[boardId]/calendar/route.ts @@ -31,7 +31,7 @@ export async function GET( const accept = request.headers.get("accept") || ""; if (accept.includes("text/calendar")) { - const ics = generateICS(board.items, board.title || "Kanban Board"); + const ics = generateICS(board.items, board.title || "Kanban"); return new NextResponse(ics, { headers: { "Content-Type": "text/calendar; charset=utf-8", From b17bd7bb542aa4a4a03545a66d321e48c70eb1e2 Mon Sep 17 00:00:00 2001 From: Hannes Hauswedell Date: Thu, 16 Apr 2026 21:17:21 +0200 Subject: [PATCH 02/16] feature: FreeBSD support assisted-by: Claude Opus 4.6 --- package.json | 7 +-- scripts/next-runner.js | 17 ++++++ scripts/postinstall.js | 114 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 scripts/next-runner.js create mode 100644 scripts/postinstall.js diff --git a/package.json b/package.json index 00da247b..fbe35ef8 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "1.22.0", "private": true, "scripts": { - "dev": "next dev", - "build": "next build && node scripts/postbuild.js", + "dev": "node scripts/next-runner.js dev", + "build": "node scripts/next-runner.js build && node scripts/postbuild.js", "start": "next start", "lint": "eslint .", "test": "vitest", @@ -14,7 +14,8 @@ "mock:data:notes": "tsx tests/mock-data/note-generator.ts", "docker:test": "docker compose -f docker-compose.test.yml down && docker compose -f docker-compose.test.yml build && docker compose -f docker-compose.test.yml up -d", "docker:test:logs": "docker compose -f docker-compose.test.yml logs -f", - "docker:test:restart": "docker compose -f docker-compose.test.yml down && docker compose -f docker-compose.test.yml up -d" + "docker:test:restart": "docker compose -f docker-compose.test.yml down && docker compose -f docker-compose.test.yml up -d", + "postinstall": "node scripts/postinstall.js" }, "dependencies": { "@dnd-kit/core": "6.3.1", diff --git a/scripts/next-runner.js b/scripts/next-runner.js new file mode 100644 index 00000000..ce2a1390 --- /dev/null +++ b/scripts/next-runner.js @@ -0,0 +1,17 @@ +'use strict'; + +/** + * Wrapper around `next` that appends --webpack on FreeBSD, where Turbopack is + * unavailable due to missing native bindings. On all other platforms the + * arguments are passed through unchanged, preserving Turbopack as the default. + */ + +const { spawnSync } = require('child_process'); + +const args = process.argv.slice(2); +if (process.platform === 'freebsd') { + args.push('--webpack'); +} + +const result = spawnSync('next', args, { stdio: 'inherit', shell: true }); +process.exit(result.status ?? 1); diff --git a/scripts/postinstall.js b/scripts/postinstall.js new file mode 100644 index 00000000..8637958d --- /dev/null +++ b/scripts/postinstall.js @@ -0,0 +1,114 @@ +'use strict'; + +/** + * Patches @swc/core/binding.js on FreeBSD so that modules importing @swc/core + * do not crash at require time when no native binary is available. + * + * Context: next-intl (and @serwist/turbopack) depend on @swc/core, whose + * binding.js throws immediately on unsupported platforms. On FreeBSD there is + * no native binary and no published WASM fallback package, so the import fails + * before the build even starts. The patch makes binding.js return a lazy stub + * on FreeBSD instead — methods only throw when actually called, which is never + * the case in this project (next-intl's experimental.extract is not enabled). + */ + +const fs = require('fs'); +const path = require('path'); + +if (process.platform !== 'freebsd') { + process.exit(0); +} + +const NEEDLE = `if (!nativeBinding) { + if (loadErrors.length > 0) { + // TODO Link to documentation with potential fixes + // - The package owner could build/publish bindings for this arch + // - The user may need to bundle the correct files + // - The user may need to re-install node_modules to get new packages + throw new Error('Failed to load native binding', { cause: loadErrors }) + } + throw new Error(\`Failed to load native binding\`) +}`; + +const REPLACEMENT = `if (!nativeBinding) { + if (process.platform === 'freebsd') { + // FreeBSD: no native or WASM binary is available from @swc/core. + // Return a stub so modules that import @swc/core without using it can load. + // Any actual call to @swc/core methods will throw at call time. + const noBinding = () => { + throw new Error( + '@swc/core: no native or WASM binding available for FreeBSD. ' + + 'Build @swc/core from source or provide a WASM fallback.' + ) + } + nativeBinding = { + Compiler: class { constructor() { noBinding() } }, + JsCompiler: class { constructor() { noBinding() } }, + analyze: noBinding, bundle: noBinding, + getTargetTriple: () => 'x86_64-unknown-freebsd', + initCustomTraceSubscriber: noBinding, minify: noBinding, + minifySync: noBinding, newMangleNameCache: noBinding, + parse: noBinding, parseFile: noBinding, + parseFileSync: noBinding, parseSync: noBinding, + print: noBinding, printSync: noBinding, + transform: noBinding, transformFile: noBinding, + transformFileSync: noBinding, transformSync: noBinding, + } + } else if (loadErrors.length > 0) { + // TODO Link to documentation with potential fixes + // - The package owner could build/publish bindings for this arch + // - The user may need to bundle the correct files + // - The user may need to re-install node_modules to get new packages + throw new Error('Failed to load native binding', { cause: loadErrors }) + } else { + throw new Error(\`Failed to load native binding\`) + } +}`; + +// Find all @swc/core/binding.js files under node_modules (handles hoisted and +// nested installs, e.g. node_modules/@swc/core and +// node_modules/next-intl/node_modules/@swc/core). +function findBindingFiles(dir, results = []) { + const swcCore = path.join(dir, '@swc', 'core', 'binding.js'); + if (fs.existsSync(swcCore)) { + results.push(swcCore); + } + try { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + if (entry.name === '@swc') continue; // already checked above + const nested = path.join(dir, entry.name, 'node_modules'); + if (fs.existsSync(nested)) { + findBindingFiles(nested, results); + } + } + } catch (_) {} + return results; +} + +const nodeModules = path.join(__dirname, '..', 'node_modules'); +const bindingFiles = findBindingFiles(nodeModules); + +if (bindingFiles.length === 0) { + console.warn('postinstall: no @swc/core/binding.js found — skipping FreeBSD patch.'); + process.exit(0); +} + +let patched = 0; +for (const file of bindingFiles) { + const src = fs.readFileSync(file, 'utf8'); + if (src.includes('x86_64-unknown-freebsd')) { + continue; // already patched + } + if (!src.includes(NEEDLE)) { + console.warn(`postinstall: unexpected binding.js format at ${file} — skipping.`); + continue; + } + fs.writeFileSync(file, src.replace(NEEDLE, REPLACEMENT)); + patched++; + console.log(`postinstall: patched ${file}`); +} + +if (patched === 0) { + console.log('postinstall: @swc/core/binding.js already patched, nothing to do.'); +} From aa7e584a673caf0d1c7bd6da7c271afb2cd7401f Mon Sep 17 00:00:00 2001 From: Seth Gregory Date: Fri, 17 Apr 2026 21:57:30 -0400 Subject: [PATCH 03/16] Add tzdata package to Dockerfile Required to be able to set time zone properly in container --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0ffc01e0..15dd9668 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,7 +27,7 @@ WORKDIR /app ENV NODE_ENV=production ENV NEXT_TELEMETRY_DISABLED=1 -RUN apk add --no-cache su-exec git grep sed +RUN apk add --no-cache su-exec git grep sed tzdata RUN if ! getent group 1000 > /dev/null 2>&1; then \ addgroup --system --gid 1000 appgroup; \ @@ -60,4 +60,4 @@ ENV PORT=3000 ENV HOSTNAME="0.0.0.0" ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] -CMD ["node", "server.js"] \ No newline at end of file +CMD ["node", "server.js"] From 2fa66f5afdcd0297a885213b43e6cddbce62bc79 Mon Sep 17 00:00:00 2001 From: fccview Date: Sat, 18 Apr 2026 14:48:37 +0100 Subject: [PATCH 04/16] Potentially fix IOS scroll issue - cant test will need to ask some nice volunteer for help --- .../Notes/Parts/NoteEditor/NoteEditor.tsx | 4 ++-- .../Notes/Parts/NoteEditor/NoteEditorContent.tsx | 5 ++--- .../Notes/Parts/TipTap/MinimalModeEditor.tsx | 6 +++--- .../Notes/Parts/TipTap/SyntaxHighlightedEditor.tsx | 2 +- .../Notes/Parts/TipTap/TipTapEditor.tsx | 12 +++++------- .../Notes/Parts/TipTap/VisualEditor.tsx | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/_components/FeatureComponents/Notes/Parts/NoteEditor/NoteEditor.tsx b/app/_components/FeatureComponents/Notes/Parts/NoteEditor/NoteEditor.tsx index 3a43aa6b..d177cfc2 100644 --- a/app/_components/FeatureComponents/Notes/Parts/NoteEditor/NoteEditor.tsx +++ b/app/_components/FeatureComponents/Notes/Parts/NoteEditor/NoteEditor.tsx @@ -47,8 +47,8 @@ export const NoteEditor = ({ onOpenViewModal={viewModalRef} /> -
-
+
+
@@ -129,10 +129,10 @@ export const MinimalModeEditor = ({ showLineNumbers={showLineNumbers} /> )} -
+
{showPreview ? (
diff --git a/app/_components/FeatureComponents/Notes/Parts/TipTap/SyntaxHighlightedEditor.tsx b/app/_components/FeatureComponents/Notes/Parts/TipTap/SyntaxHighlightedEditor.tsx index 1767001a..5d197004 100644 --- a/app/_components/FeatureComponents/Notes/Parts/TipTap/SyntaxHighlightedEditor.tsx +++ b/app/_components/FeatureComponents/Notes/Parts/TipTap/SyntaxHighlightedEditor.tsx @@ -180,7 +180,7 @@ export const SyntaxHighlightedEditor = ({ return (
{ e.preventDefault(); e.stopPropagation(); diff --git a/app/_components/FeatureComponents/Notes/Parts/TipTap/TipTapEditor.tsx b/app/_components/FeatureComponents/Notes/Parts/TipTap/TipTapEditor.tsx index c003cb13..90ad661b 100644 --- a/app/_components/FeatureComponents/Notes/Parts/TipTap/TipTapEditor.tsx +++ b/app/_components/FeatureComponents/Notes/Parts/TipTap/TipTapEditor.tsx @@ -146,9 +146,8 @@ export const TiptapEditor = forwardRef( }, editorProps: { attributes: { - class: `prose prose-sm px-6 pt-6 pb-12 sm:prose-base lg:prose-lg xl:prose-2xl dark:prose-invert [&_ul]:list-disc [&_ol]:list-decimal [&_table]:border-collapse [&_table]:w-full [&_table]:my-4 [&_th]:border [&_th]:border-border [&_th]:px-3 [&_th]:py-2 [&_th]:bg-muted [&_th]:font-semibold [&_th]:text-left [&_td]:border [&_td]:border-border [&_td]:px-3 [&_td]:py-2 [&_tr:nth-child(even)]:bg-muted/50 w-full max-w-none focus:outline-none ${ - compactMode ? "!max-w-[900px] mx-auto" : "" - }`, + class: `prose prose-sm px-6 pt-6 pb-4 sm:prose-base lg:prose-lg xl:prose-2xl dark:prose-invert [&_ul]:list-disc [&_ol]:list-decimal [&_table]:border-collapse [&_table]:w-full [&_table]:my-4 [&_th]:border [&_th]:border-border [&_th]:px-3 [&_th]:py-2 [&_th]:bg-muted [&_th]:font-semibold [&_th]:text-left [&_td]:border [&_td]:border-border [&_td]:px-3 [&_td]:py-2 [&_tr:nth-child(even)]:bg-muted/50 w-full max-w-none focus:outline-none ${compactMode ? "!max-w-[900px] mx-auto" : "" + }`, }, handleKeyDown: (view, event) => { return createKeyDownHandler(editor)(view, event); @@ -349,7 +348,7 @@ export const TiptapEditor = forwardRef( ); return ( -
+
@@ -386,9 +385,8 @@ export const TiptapEditor = forwardRef( {isMarkdownMode && showPreview ? (
diff --git a/app/_components/FeatureComponents/Notes/Parts/TipTap/VisualEditor.tsx b/app/_components/FeatureComponents/Notes/Parts/TipTap/VisualEditor.tsx index ea405563..470c6007 100644 --- a/app/_components/FeatureComponents/Notes/Parts/TipTap/VisualEditor.tsx +++ b/app/_components/FeatureComponents/Notes/Parts/TipTap/VisualEditor.tsx @@ -42,7 +42,7 @@ export const VisualEditor = ({ }, [editor, onTextSelection]); return (
{ e.preventDefault(); e.stopPropagation(); From c9f1b4d04dcbd99e4df41542e1e99953eb76caad Mon Sep 17 00:00:00 2001 From: fccview Date: Sat, 18 Apr 2026 15:19:28 +0100 Subject: [PATCH 05/16] edit on click for checklists and try to identify another potantial issue for ios scrolling --- .../Parts/Simple/NestedChecklistItem.tsx | 62 ++++++++++++---- .../Notes/Parts/SwipeNavigationWrapper.tsx | 5 +- .../Profile/Parts/UserPreferencesTab.tsx | 30 ++++++++ .../GlobalComponents/User/UserAvatar.tsx | 7 +- app/_hooks/useSwipeNavigation.ts | 18 +++++ app/_schemas/user-schemas.ts | 3 + app/_server/actions/mfa/index.ts | 2 - app/_server/actions/note/queries.ts | 74 +++++++------------ app/_translations/de.json | 7 +- app/_translations/en.json | 7 +- app/_translations/es.json | 7 +- app/_translations/fr.json | 7 +- app/_translations/it.json | 7 +- app/_translations/klingon.json | 7 +- app/_translations/ko.json | 7 +- app/_translations/nl.json | 7 +- app/_translations/pirate.json | 7 +- app/_translations/pl.json | 7 +- app/_translations/ru.json | 7 +- app/_translations/tr.json | 7 +- app/_translations/zh.json | 7 +- app/_types/index.ts | 1 + app/_types/user.ts | 2 + 23 files changed, 213 insertions(+), 82 deletions(-) diff --git a/app/_components/FeatureComponents/Checklists/Parts/Simple/NestedChecklistItem.tsx b/app/_components/FeatureComponents/Checklists/Parts/Simple/NestedChecklistItem.tsx index e2f43ce5..a967b0fe 100644 --- a/app/_components/FeatureComponents/Checklists/Parts/Simple/NestedChecklistItem.tsx +++ b/app/_components/FeatureComponents/Checklists/Parts/Simple/NestedChecklistItem.tsx @@ -327,13 +327,13 @@ const NestedChecklistItemComponent = ({ className={cn( "relative my-1", hasChildren && - !isChild && - "border-l-2 bg-muted/30 border-l-primary/70 rounded-jotty border-dashed border-t", + !isChild && + "border-l-2 bg-muted/30 border-l-primary/70 rounded-jotty border-dashed border-t", !hasChildren && - !isChild && - "border-l-2 bg-muted/30 border-l-primary/70 rounded-jotty border-dashed border-t", + !isChild && + "border-l-2 bg-muted/30 border-l-primary/70 rounded-jotty border-dashed border-t", isChild && - "ml-4 rounded-jotty border-dashed border-l border-border border-l-primary/70", + "ml-4 rounded-jotty border-dashed border-l border-border border-l-primary/70", "first:mt-0 transition-colors duration-150", isActive && "bg-muted/20", isDragging && "opacity-50 z-50", @@ -352,7 +352,7 @@ const NestedChecklistItemComponent = ({ isChild ? "px-2.5 py-1.5 lg:py-2" : "p-1.5 lg:p-2", completed && "opacity-80", !permissions?.canEdit && - "opacity-50 cursor-not-allowed pointer-events-none", + "opacity-50 cursor-not-allowed pointer-events-none", )} > {!isPublicView && !isDragDisabled && permissions?.canEdit && ( @@ -434,7 +434,7 @@ const NestedChecklistItemComponent = ({ )}
) : ( -
+
+ {user?.checklistItemClickAction === "edit" && + permissions?.canEdit && + onEdit && + !isPublicView && ( + { + if (e.metaKey || e.ctrlKey) { + onToggle(item.id, !(item.completed || completed)); + return; + } + handleEdit(); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + handleEdit(); + } + }} + className={cn( + "text-md lg:text-sm transition-all duration-200 cursor-text break-words min-w-0 flex items-center select-text", + item.completed || completed + ? "line-through text-muted-foreground" + : "text-foreground", + )} + > + {tagsEnabled ? renderTextWithHashtags(displayText) : displayText} + + )}
{draggedItemId !== item.id && ( {children}
@@ -104,7 +103,7 @@ export const SwipeNavigationWrapper = ({