From 9912b21b3db838d4c06e5f17a7826caef62cb709 Mon Sep 17 00:00:00 2001 From: thephez Date: Sun, 24 May 2026 20:26:07 -0400 Subject: [PATCH 1/4] feat(dashnote): optimize workspace UI for mobile Convert modals to bottom sheets, add mobile action sheet and info drawer to the note editor, surface activity drawer from the mobile nav, and enlarge tap targets across login and note list. Co-Authored-By: Claude Opus 4.7 (1M context) --- example-apps/dashnote/src/App.tsx | 1 + .../dashnote/src/components/AppShell.tsx | 15 + .../dashnote/src/components/LoginModal.tsx | 7 +- .../dashnote/src/components/Modal.tsx | 4 +- .../dashnote/src/components/NoteEditor.tsx | 235 ++++++-- .../dashnote/src/components/NoteList.tsx | 529 +++++++++++++++--- .../src/components/NotesWorkspace.tsx | 42 +- example-apps/dashnote/src/styles/globals.css | 8 + example-apps/dashnote/test/App.test.tsx | 19 + .../dashnote/test/LoginModal.test.tsx | 10 + .../dashnote/test/NoteEditor.test.tsx | 172 ++++-- example-apps/dashnote/test/NoteList.test.tsx | 228 +++++++- .../dashnote/test/NotesWorkspace.test.tsx | 45 +- 13 files changed, 1088 insertions(+), 227 deletions(-) diff --git a/example-apps/dashnote/src/App.tsx b/example-apps/dashnote/src/App.tsx index 68b27cd7..04c4e56e 100644 --- a/example-apps/dashnote/src/App.tsx +++ b/example-apps/dashnote/src/App.tsx @@ -68,6 +68,7 @@ function App() { dpnsName={session.dpnsName} contractId={session.contractId} onLoginOpen={() => setLoginOpen(true)} + onOpenActivity={() => setActivityOpen(true)} mobileFullBleed={mobileFullBleed} > {tab === "notes" ? ( diff --git a/example-apps/dashnote/src/components/AppShell.tsx b/example-apps/dashnote/src/components/AppShell.tsx index 5ad9111b..d438d547 100644 --- a/example-apps/dashnote/src/components/AppShell.tsx +++ b/example-apps/dashnote/src/components/AppShell.tsx @@ -14,6 +14,7 @@ interface AppShellProps { dpnsName: string | null; contractId: string | null; onLoginOpen: () => void; + onOpenActivity?: () => void; children: ReactNode; mobileFullBleed?: boolean; } @@ -97,6 +98,7 @@ export function AppShell({ dpnsName, contractId, onLoginOpen, + onOpenActivity, children, mobileFullBleed = false, }: AppShellProps) { @@ -132,6 +134,19 @@ export function AppShell({ closeDrawer(); }} /> + {onOpenActivity && ( +
+ { + onOpenActivity(); + closeDrawer(); + }} + /> +
+ )} {status !== "authenticated" && status !== "browsing" && ( @@ -279,14 +280,14 @@ export function LoginModal({ open, onClose }: LoginModalProps) { @@ -312,7 +313,7 @@ export function LoginModal({ open, onClose }: LoginModalProps) { href="https://bridge.thepasta.org/" target="_blank" rel="noreferrer" - className="font-semibold text-accent underline-offset-2 hover:underline" + className="font-semibold text-accent underline-offset-2 hover:underline max-md:mt-2 max-md:inline-flex max-md:min-h-10 max-md:items-center max-md:rounded-full max-md:border max-md:border-line-2 max-md:px-3 max-md:no-underline" > Create one on Dash Bridge → diff --git a/example-apps/dashnote/src/components/Modal.tsx b/example-apps/dashnote/src/components/Modal.tsx index 14c31427..31e8bb49 100644 --- a/example-apps/dashnote/src/components/Modal.tsx +++ b/example-apps/dashnote/src/components/Modal.tsx @@ -42,7 +42,7 @@ export function Modal({ return (
e.stopPropagation()} >
diff --git a/example-apps/dashnote/src/components/NoteEditor.tsx b/example-apps/dashnote/src/components/NoteEditor.tsx index 42b2305c..091bd63e 100644 --- a/example-apps/dashnote/src/components/NoteEditor.tsx +++ b/example-apps/dashnote/src/components/NoteEditor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, type ReactNode } from "react"; import type { NoteRecord } from "../dash/queries"; import { FIELD_BYTE_LIMIT } from "../lib/fieldLimits"; @@ -65,6 +65,10 @@ export function NoteEditor({ const isNew = selectedId === "new"; const oversize = messageOversize; const [jsonOpen, setJsonOpen] = useState(false); + const [mobileActionsOpen, setMobileActionsOpen] = useState(false); + const [mobileInfoOpen, setMobileInfoOpen] = useState(false); + const showMobileSave = + !isDesktop && !isReadOnly && hasSelection && (dirty || saving); // Cmd/Ctrl-S triggers Save (matches the keyboard hint chip). useEffect(() => { @@ -208,7 +212,7 @@ export function NoteEditor({ )} - {!isReadOnly && hasSelection && ( + {!isReadOnly && hasSelection && (isDesktop || showMobileSave) && ( + )}
@@ -314,7 +342,7 @@ export function NoteEditor({ onChange={(event) => onTitleChange(event.target.value)} placeholder={isNew ? "New note title" : "Title"} disabled={!canEdit} - className="w-full border-0 bg-transparent px-0 pt-0 pb-1 text-[28px] font-semibold leading-tight tracking-tight text-ink outline-none placeholder:text-ink-4 disabled:cursor-not-allowed disabled:text-ink-4" + className="mobile-note-editor-field w-full border-0 bg-transparent px-0 pt-0 pb-1 text-[28px] font-semibold leading-tight tracking-tight text-ink outline-none placeholder:text-ink-4 disabled:cursor-not-allowed disabled:text-ink-4" />