diff --git a/apps/mobile/src/MobileApp.tsx b/apps/mobile/src/MobileApp.tsx index 5fe623f1..0215a1ce 100644 --- a/apps/mobile/src/MobileApp.tsx +++ b/apps/mobile/src/MobileApp.tsx @@ -44,7 +44,7 @@ import { NamedIcon, type IconName } from './NamedIcon' import { SwipeSurface } from './SwipeSurface' import { styles } from './styles' import { colors } from './theme' -import { MobileNoteCreatePrompt } from './MobileNoteCreatePrompt' +import { MobileEditorBreadcrumb } from './MobileEditorBreadcrumb' import { MobilePropertiesPanel } from './MobilePropertiesPanel' import { MobileVaultManagementCard } from './MobileVaultManagementCard' import { MobileVaultRemotePrompt } from './MobileVaultRemotePrompt' @@ -170,16 +170,11 @@ export function MobileApp() { notes={availableNotes} selectedNoteId={compactNavigation.selectedNoteId} createNoteFailed={createFlow.failed} - createNoteTitle={createFlow.title} - isCreatePromptOpen={createFlow.isPromptOpen} isCreatingNote={createFlow.isCreating} runtimeLoadFailed={runtimeLoader.failed} - onCancelCreateNote={createFlow.cancel} - onChangeCreateNoteTitle={createFlow.setTitle} - onOpenCreateNote={createFlow.open} + onCreateNote={createFlow.create} onGitSyncAction={gitSyncFlow.runPrimaryAction} onRetryRuntimeLoad={runtimeLoader.retry} - onSubmitCreateNote={createFlow.submit} onSelectNote={selectNote} /> void onDeleteNote?: () => void onDraftChange: (draft: MobileEditorDraft) => void - onCancelCreateNote: () => void - onChangeCreateNoteTitle: (title: string) => void - onOpenCreateNote: () => void + onCreateNote: () => void onGitSyncAction: () => void onChangeProperties: (patch: MobileNotePropertyPatch) => void onOpenRemoteSetup: () => void onRetryRuntimeLoad: () => void - onSubmitCreateNote: () => void onSelectNote: (note: MobileNote) => void propertiesFailed: boolean isSavingProperties: boolean @@ -346,18 +326,13 @@ function CompactShell({ notes={notes} selectedNoteId={selectedNoteId} createNoteFailed={createNoteFailed} - createNoteTitle={createNoteTitle} - isCreatePromptOpen={isCreatePromptOpen} isCreatingNote={isCreatingNote} runtimeLoadFailed={runtimeLoadFailed} - onCancelCreateNote={onCancelCreateNote} - onChangeCreateNoteTitle={onChangeCreateNoteTitle} onGitSyncAction={onGitSyncAction} - onOpenCreateNote={onOpenCreateNote} + onCreateNote={onCreateNote} onOpenSidebar={() => onNavigate({ type: 'openSidebar' })} onRetryRuntimeLoad={onRetryRuntimeLoad} onSelectNote={onSelectNote} - onSubmitCreateNote={onSubmitCreateNote} /> ) @@ -408,35 +383,25 @@ function NoteListPanel({ gitSyncPlan, notes, createNoteFailed, - createNoteTitle, - isCreatePromptOpen, isCreatingNote, runtimeLoadFailed, - onCancelCreateNote, - onChangeCreateNoteTitle, onGitSyncAction, - onOpenCreateNote, + onCreateNote, onOpenSidebar, onRetryRuntimeLoad, onSelectNote, - onSubmitCreateNote, selectedNoteId, }: { gitSyncPlan: MobileGitSyncPlan notes: MobileNote[] createNoteFailed: boolean - createNoteTitle: string - isCreatePromptOpen: boolean isCreatingNote: boolean runtimeLoadFailed: boolean - onCancelCreateNote: () => void - onChangeCreateNoteTitle: (title: string) => void onGitSyncAction: () => void - onOpenCreateNote: () => void + onCreateNote: () => void onOpenSidebar?: () => void onRetryRuntimeLoad: () => void onSelectNote: (note: MobileNote) => void - onSubmitCreateNote: () => void selectedNoteId: string }) { return ( @@ -477,20 +442,11 @@ function NoteListPanel({ )} /> - {isCreatePromptOpen ? ( - - ) : null} + {createNoteFailed ? Could not create note. : null} [ styles.composeButton, isCreatingNote ? styles.composeButtonDisabled : null, @@ -547,12 +503,12 @@ function EditorPanel({ {onBack ? } onPress={onBack} /> : null} - + {onOpenProperties ? } onPress={onOpenProperties} /> : null} {onDeleteNote ? } onPress={onDeleteNote} /> : null} } /> - + ) } diff --git a/apps/mobile/src/MobileEditablePropertyPickers.tsx b/apps/mobile/src/MobileEditablePropertyPickers.tsx index ac6beb63..e4cc7e98 100644 --- a/apps/mobile/src/MobileEditablePropertyPickers.tsx +++ b/apps/mobile/src/MobileEditablePropertyPickers.tsx @@ -1,14 +1,16 @@ import { CaretDown, CaretRight } from 'phosphor-react-native' -import type { ReactNode } from 'react' -import { Pressable, Text, View, type StyleProp, type ViewStyle } from 'react-native' +import { useState, type ReactNode } from 'react' +import { Pressable, Text, TextInput, View, type StyleProp, type ViewStyle } from 'react-native' import type { MobileNote } from './demoData' import { NamedIcon, type IconName } from './NamedIcon' import { + formatMobileNoteTags, isMobileNotePropertySelected, mobileNoteIconOptions, mobileNoteStatusOptions, mobileNoteTagOptions, mobileNoteTypeOptions, + parseMobileNoteTags, toggleMobileNoteTag, type MobileNotePropertyPatch, } from './mobileNoteProperties' @@ -29,72 +31,199 @@ export function MobileEditablePropertyPickers({ onSelectPicker: (selected: MobilePropertyPickerKey) => void openPicker: MobilePropertyPickerKey | null }) { + const today = formatMobilePropertyDate(new Date()) + return ( <> - onChangeProperties?.({ type })} onOpen={() => onSelectPicker('type')} - > - - {mobileNoteTypeOptions.map((option) => ( - onChangeProperties?.({ type })} - /> - ))} - - - + onChangeProperties?.({ status })} onOpen={() => onSelectPicker('status')} - > + /> + onChangeProperties?.({ date })} + onOpen={() => onSelectPicker('date')} + /> + onSelectPicker('icon')} + /> + onSelectPicker('tags')} + /> + + ) +} + +function EditableTextProperty({ + disabled, + isOpen, + label, + onCommit, + onOpen, + placeholder, + suggestions, + value, +}: { + disabled: boolean + isOpen: boolean + label: string + onCommit: (value: string) => void + onOpen: () => void + placeholder: string + suggestions: readonly string[] + value: string +}) { + const [draft, setDraft] = useState(value) + const commitDraft = () => commitTextValue({ current: value, next: draft, onCommit }) + + return ( + + + - {mobileNoteStatusOptions.map((option) => ( + {suggestions.map((option) => ( onChangeProperties?.({ status })} + value={value} + onSelect={(selected) => { + setDraft(selected) + onCommit(selected) + }} /> ))} - - onSelectPicker('icon')} - > - - {mobileNoteIconOptions.map((option) => ( - onChangeProperties?.({ icon })} - /> - ))} - - - 0 ? `${note.tags.length} selected` : 'None'} - onOpen={() => onSelectPicker('tags')} - > + + + ) +} + +function IconProperty({ + disabled, + isOpen, + note, + onChangeProperties, + onOpen, +}: { + disabled: boolean + isOpen: boolean + note: MobileNote + onChangeProperties?: (patch: MobileNotePropertyPatch) => void + onOpen: () => void +}) { + return ( + + + {mobileNoteIconOptions.map((option) => ( + onChangeProperties?.({ icon })} + /> + ))} + + + ) +} + +function TagsProperty({ + disabled, + isOpen, + note, + onChangeProperties, + onOpen, +}: { + disabled: boolean + isOpen: boolean + note: MobileNote + onChangeProperties?: (patch: MobileNotePropertyPatch) => void + onOpen: () => void +}) { + const [draft, setDraft] = useState(formatMobileNoteTags(note.tags)) + const commitDraft = () => onChangeProperties?.({ tags: parseMobileNoteTags(draft) }) + + return ( + 0 ? formatMobileNoteTags(note.tags) : 'None'} + onOpen={onOpen} + > + + {mobileNoteTagOptions.map((option) => ( onChangeProperties?.({ tags: toggleMobileNoteTag(note.tags, tag) })} + onSelect={(tag) => { + const tags = toggleMobileNoteTag(note.tags, tag) + setDraft(formatMobileNoteTags(tags)) + onChangeProperties?.({ tags }) + }} /> ))} - - + + ) } @@ -156,7 +289,7 @@ function PropertyPickerRow({ style={({ pressed }) => [styles.propertyRow, disabled ? styles.propertyDisabled : null, pressed ? styles.pressed : null]} > {label} - {value} + {value} ) @@ -189,10 +322,8 @@ function PropertyIconChip({ function PropertyChipOptions({ children }: { children: ReactNode }) { return ( - - - {children} - + + {children} ) } @@ -254,3 +385,26 @@ function SelectablePropertyChip({ ) } + +function commitTextValue({ + current, + next, + onCommit, +}: { + current: string + next: string + onCommit: (value: string) => void +}) { + const normalized = next.trim() + if (normalized !== current) { + onCommit(normalized) + } +} + +function formatMobilePropertyDate(date: Date) { + return new Intl.DateTimeFormat(undefined, { + day: 'numeric', + month: 'short', + year: 'numeric', + }).format(date) +} diff --git a/apps/mobile/src/MobileEditorAdapter.tsx b/apps/mobile/src/MobileEditorAdapter.tsx index 408d105c..751285ef 100644 --- a/apps/mobile/src/MobileEditorAdapter.tsx +++ b/apps/mobile/src/MobileEditorAdapter.tsx @@ -1,9 +1,8 @@ import { useEffect, useMemo, useRef } from 'react' -import { KeyboardAvoidingView, Platform, Text, View } from 'react-native' +import { KeyboardAvoidingView, Platform, View } from 'react-native' import { RichText, Toolbar, useEditorBridge } from '@10play/tentap-editor' import type { MobileNote } from './mobileNoteProjection' import { createMobileEditorDraft, type MobileEditorDraft } from './mobileEditorDraft' -import { idleMobileEditorSaveState, type MobileEditorSaveState } from './mobileEditorSaveState' import { createMobileEditorDocument, createMobileEditorHtml, @@ -13,11 +12,9 @@ import { styles } from './styles' export function MobileEditorAdapter({ note, onDraftChange, - saveState = idleMobileEditorSaveState, }: { note: MobileNote onDraftChange?: (draft: MobileEditorDraft) => void - saveState?: MobileEditorSaveState }) { const document = useMemo(() => createMobileEditorDocument(note), [note]) const initialContent = useMemo(() => createMobileEditorHtml(document), [document]) @@ -35,15 +32,17 @@ export function MobileEditorAdapter({ }) }, }) + useEffect(() => { + const timer = setTimeout(() => { + editor.injectCSS(mobileEditorCss, 'tolaria-mobile-editor') + editor.injectJS('document.documentElement.lang = navigator.language || "en"; true;') + }, 100) + + return () => clearTimeout(timer) + }, [editor, note.id]) return ( - - {note.type} - / - {note.id} - {saveState.label} - @@ -57,19 +56,33 @@ export function MobileEditorAdapter({ ) } -function saveStateStyle(saveState: MobileEditorSaveState) { - switch (saveState.state) { - case 'blocked': - return styles.editorSaveState_blocked - case 'failed': - return styles.editorSaveState_failed - case 'queued': - return styles.editorSaveState_queued - case 'saved': - return styles.editorSaveState_saved - case 'saving': - return styles.editorSaveState_saving - default: - return styles.editorSaveState_idle +const mobileEditorCss = ` + html, + body, + #root, + .ProseMirror { + color: #292825; + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif; + font-size: 18px; + line-height: 1.55; } -} + + .ProseMirror { + padding: 0; + } + + .ProseMirror h1 { + font-family: inherit; + font-size: 42px; + font-weight: 760; + letter-spacing: 0; + line-height: 1.08; + margin: 18px 0 28px; + } + + .ProseMirror p, + .ProseMirror li, + .ProseMirror blockquote { + font-family: inherit; + } +` diff --git a/apps/mobile/src/MobileEditorBreadcrumb.tsx b/apps/mobile/src/MobileEditorBreadcrumb.tsx new file mode 100644 index 00000000..44ab4385 --- /dev/null +++ b/apps/mobile/src/MobileEditorBreadcrumb.tsx @@ -0,0 +1,38 @@ +import { Text, View } from 'react-native' +import type { MobileNote } from './demoData' +import type { MobileEditorSaveState } from './mobileEditorSaveState' +import { styles } from './styles' + +export function MobileEditorBreadcrumb({ + note, + saveState, +}: { + note: MobileNote + saveState: MobileEditorSaveState +}) { + return ( + + {note.type} + / + {note.id} + {saveState.label} + + ) +} + +function saveStateStyle(saveState: MobileEditorSaveState) { + switch (saveState.state) { + case 'blocked': + return styles.editorSaveState_blocked + case 'failed': + return styles.editorSaveState_failed + case 'queued': + return styles.editorSaveState_queued + case 'saved': + return styles.editorSaveState_saved + case 'saving': + return styles.editorSaveState_saving + default: + return styles.editorSaveState_idle + } +} diff --git a/apps/mobile/src/MobileNoteCreatePrompt.tsx b/apps/mobile/src/MobileNoteCreatePrompt.tsx deleted file mode 100644 index f4b88a23..00000000 --- a/apps/mobile/src/MobileNoteCreatePrompt.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Pressable, Text, TextInput, View } from 'react-native' -import { styles } from './styles' - -export function MobileNoteCreatePrompt({ - failed, - isCreating, - onCancel, - onChangeTitle, - onSubmit, - title, -}: { - failed: boolean - isCreating: boolean - onCancel: () => void - onChangeTitle: (title: string) => void - onSubmit: () => void - title: string -}) { - return ( - - - {failed ? Could not create note : null} - - - - - - ) -} - -function PromptButton({ - disabled, - label, - onPress, - primary, -}: { - disabled?: boolean - label: string - onPress: () => void - primary?: boolean -}) { - return ( - [ - styles.createNoteAction, - primary ? styles.createNoteActionPrimary : null, - disabled ? styles.createNoteActionDisabled : null, - pressed ? styles.pressed : null, - ]} - > - {label} - - ) -} diff --git a/apps/mobile/src/MobilePropertiesPanel.tsx b/apps/mobile/src/MobilePropertiesPanel.tsx index a509edac..ef7180d5 100644 --- a/apps/mobile/src/MobilePropertiesPanel.tsx +++ b/apps/mobile/src/MobilePropertiesPanel.tsx @@ -21,7 +21,6 @@ export function MobilePropertiesPanel({ onChangeProperties?: (patch: MobileNotePropertyPatch) => void onClose?: () => void }) { - const today = formatMobilePropertyDate(new Date()) const [openPicker, setOpenPicker] = useState(null) const selectPicker = (selected: MobilePropertyPickerKey) => { setOpenPicker((current) => nextMobilePropertyPicker({ current, selected })) @@ -39,13 +38,6 @@ export function MobilePropertiesPanel({ onChangeProperties={onChangeProperties} onSelectPicker={selectPicker} /> - onChangeProperties?.({ date: today })} - /> History @@ -70,44 +62,17 @@ function PanelToolbar({ onClose }: { onClose?: () => void }) { ) } -function formatMobilePropertyDate(date: Date) { - return new Intl.DateTimeFormat('en-US', { - day: 'numeric', - month: 'short', - year: 'numeric', - }).format(date) -} - function PropertyRow({ - actionLabel, - disabled = false, label, - onPress, value, }: { - actionLabel?: string - disabled?: boolean label: string - onPress?: () => void value: string }) { - const content = ( - <> + return ( + {label} {value} - {actionLabel ? {actionLabel} : null} - - ) - - return onPress ? ( - [styles.propertyRow, disabled ? styles.propertyDisabled : null, pressed ? styles.pressed : null]} - > - {content} - - ) : ( - {content} + ) } diff --git a/apps/mobile/src/mobileNoteProperties.test.ts b/apps/mobile/src/mobileNoteProperties.test.ts index a9aed44e..024dce51 100644 --- a/apps/mobile/src/mobileNoteProperties.test.ts +++ b/apps/mobile/src/mobileNoteProperties.test.ts @@ -2,7 +2,9 @@ import { describe, expect, it } from 'vitest' import { notes } from './demoData' import { createMobileNoteFrontmatterPatch, + formatMobileNoteTags, isMobileNotePropertySelected, + parseMobileNoteTags, toggleMobileNoteTag, } from './mobileNoteProperties' @@ -41,3 +43,10 @@ describe('toggleMobileNoteTag', () => { expect(toggleMobileNoteTag(['Tolaria MVP', 'mobile'], 'mobile')).toEqual(['Tolaria MVP']) }) }) + +describe('mobile note tag text helpers', () => { + it('formats and parses keyboard-entered tag lists', () => { + expect(formatMobileNoteTags(['Tolaria MVP', 'mobile'])).toBe('Tolaria MVP, mobile') + expect(parseMobileNoteTags(' Tolaria MVP, mobile, mobile, ')).toEqual(['Tolaria MVP', 'mobile']) + }) +}) diff --git a/apps/mobile/src/mobileNoteProperties.ts b/apps/mobile/src/mobileNoteProperties.ts index 15a6c54a..2aa6ac69 100644 --- a/apps/mobile/src/mobileNoteProperties.ts +++ b/apps/mobile/src/mobileNoteProperties.ts @@ -37,3 +37,14 @@ export function isMobileNotePropertySelected({ export function toggleMobileNoteTag(tags: string[], tag: string) { return tags.includes(tag) ? tags.filter((item) => item !== tag) : [...tags, tag] } + +export function formatMobileNoteTags(tags: readonly string[]) { + return tags.join(', ') +} + +export function parseMobileNoteTags(value: string) { + return value + .split(',') + .map((tag) => tag.trim()) + .filter((tag, index, tags) => tag.length > 0 && tags.indexOf(tag) === index) +} diff --git a/apps/mobile/src/mobilePropertyPicker.ts b/apps/mobile/src/mobilePropertyPicker.ts index 5a047457..9978f346 100644 --- a/apps/mobile/src/mobilePropertyPicker.ts +++ b/apps/mobile/src/mobilePropertyPicker.ts @@ -1,4 +1,4 @@ -export type MobilePropertyPickerKey = 'icon' | 'status' | 'tags' | 'type' +export type MobilePropertyPickerKey = 'date' | 'icon' | 'status' | 'tags' | 'type' export function nextMobilePropertyPicker({ current, diff --git a/apps/mobile/src/styles.ts b/apps/mobile/src/styles.ts index a638fe6b..086de3db 100644 --- a/apps/mobile/src/styles.ts +++ b/apps/mobile/src/styles.ts @@ -1,8 +1,8 @@ +import { breadcrumbStyles } from './styles/breadcrumbStyles' import { commonStyles } from './styles/commonStyles' import { editorSaveStateStyles } from './styles/editorSaveStateStyles' import { editorStyles } from './styles/editorStyles' import { gitSyncStyles } from './styles/gitSyncStyles' -import { noteCreateStyles } from './styles/noteCreateStyles' import { noteListStyles } from './styles/noteListStyles' import { propertiesStyles } from './styles/propertiesStyles' import { propertyChipStyles } from './styles/propertyChipStyles' @@ -12,11 +12,11 @@ import { vaultManagementStyles } from './styles/vaultManagementStyles' import { vaultLoadStyles } from './styles/vaultLoadStyles' export const styles = { + ...breadcrumbStyles, ...commonStyles, ...editorStyles, ...editorSaveStateStyles, ...gitSyncStyles, - ...noteCreateStyles, ...noteListStyles, ...propertiesStyles, ...propertyChipStyles, diff --git a/apps/mobile/src/styles/breadcrumbStyles.ts b/apps/mobile/src/styles/breadcrumbStyles.ts new file mode 100644 index 00000000..a03e0e11 --- /dev/null +++ b/apps/mobile/src/styles/breadcrumbStyles.ts @@ -0,0 +1,28 @@ +import { StyleSheet } from 'react-native' +import { colors, spacing } from '../theme' + +export const breadcrumbStyles = StyleSheet.create({ + editorBreadcrumb: { + minWidth: 0, + flex: 1, + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + }, + editorBreadcrumbText: { + color: colors.mutedText, + fontSize: 14, + fontWeight: '600', + }, + editorBreadcrumbDivider: { + color: colors.border, + fontSize: 14, + }, + editorBreadcrumbTitle: { + minWidth: 0, + flexShrink: 1, + color: colors.textSoft, + fontSize: 14, + fontWeight: '700', + }, +}) diff --git a/apps/mobile/src/styles/editorStyles.ts b/apps/mobile/src/styles/editorStyles.ts index 78f5cb63..b87e4975 100644 --- a/apps/mobile/src/styles/editorStyles.ts +++ b/apps/mobile/src/styles/editorStyles.ts @@ -12,6 +12,7 @@ export const editorStyles = StyleSheet.create({ maxWidth: 760, alignSelf: 'center', paddingHorizontal: spacing.xl, + paddingTop: spacing.xl, paddingBottom: 96, }, editorAdapterContent: { @@ -22,21 +23,6 @@ export const editorStyles = StyleSheet.create({ paddingHorizontal: spacing.xl, paddingBottom: 96, }, - breadcrumbRow: { - marginBottom: spacing.xl, - flexDirection: 'row', - alignItems: 'center', - gap: spacing.sm, - }, - breadcrumbText: { - color: colors.mutedText, - fontSize: 14, - fontWeight: '600', - }, - breadcrumbDivider: { - color: colors.border, - fontSize: 14, - }, editorTitle: { marginBottom: spacing.xl, color: colors.text, diff --git a/apps/mobile/src/styles/noteCreateStyles.ts b/apps/mobile/src/styles/noteCreateStyles.ts deleted file mode 100644 index 7e90e62b..00000000 --- a/apps/mobile/src/styles/noteCreateStyles.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { StyleSheet } from 'react-native' -import { colors, spacing } from '../theme' - -export const noteCreateStyles = StyleSheet.create({ - composeButtonDisabled: { - opacity: 0.55, - }, - createNoteAction: { - minWidth: 82, - alignItems: 'center', - justifyContent: 'center', - borderRadius: 16, - paddingHorizontal: spacing.md, - paddingVertical: spacing.sm, - backgroundColor: colors.primarySoft, - }, - createNoteActionDisabled: { - opacity: 0.55, - }, - createNoteActionPrimary: { - backgroundColor: colors.primary, - }, - createNoteActionText: { - color: colors.primary, - fontSize: 14, - fontWeight: '700', - }, - createNoteActionTextPrimary: { - color: colors.canvas, - }, - createNoteActions: { - flexDirection: 'row', - justifyContent: 'flex-end', - gap: spacing.sm, - }, - createNoteError: { - color: colors.textSoft, - fontSize: 13, - fontWeight: '600', - }, - createNoteInput: { - minHeight: 44, - borderColor: colors.border, - borderWidth: StyleSheet.hairlineWidth, - borderRadius: 14, - paddingHorizontal: spacing.md, - color: colors.text, - backgroundColor: colors.canvas, - fontSize: 16, - fontWeight: '600', - }, - createNotePrompt: { - position: 'absolute', - right: spacing.xl, - bottom: spacing.xl + 76, - width: 280, - gap: spacing.sm, - borderColor: colors.border, - borderWidth: StyleSheet.hairlineWidth, - borderRadius: 18, - padding: spacing.md, - backgroundColor: colors.canvas, - }, -}) diff --git a/apps/mobile/src/styles/noteListStyles.ts b/apps/mobile/src/styles/noteListStyles.ts index 29e7b93b..71408648 100644 --- a/apps/mobile/src/styles/noteListStyles.ts +++ b/apps/mobile/src/styles/noteListStyles.ts @@ -66,4 +66,7 @@ export const noteListStyles = StyleSheet.create({ borderRadius: 32, backgroundColor: colors.primary, }, + composeButtonDisabled: { + opacity: 0.55, + }, }) diff --git a/apps/mobile/src/styles/propertyChipStyles.ts b/apps/mobile/src/styles/propertyChipStyles.ts index a3da1cea..6b0eba1b 100644 --- a/apps/mobile/src/styles/propertyChipStyles.ts +++ b/apps/mobile/src/styles/propertyChipStyles.ts @@ -2,12 +2,6 @@ import { StyleSheet } from 'react-native' import { colors, spacing } from '../theme' export const propertyChipStyles = StyleSheet.create({ - propertyAction: { - marginLeft: spacing.sm, - color: colors.primary, - fontSize: 13, - fontWeight: '700', - }, propertyDisabled: { opacity: 0.55, }, @@ -17,12 +11,6 @@ export const propertyChipStyles = StyleSheet.create({ fontSize: 13, fontWeight: '700', }, - propertyOptionGroup: { - minHeight: 58, - paddingVertical: spacing.sm, - borderBottomColor: colors.border, - borderBottomWidth: StyleSheet.hairlineWidth, - }, propertyChipRow: { flexDirection: 'row', flexWrap: 'wrap', @@ -34,6 +22,17 @@ export const propertyChipStyles = StyleSheet.create({ borderBottomColor: colors.border, borderBottomWidth: StyleSheet.hairlineWidth, }, + propertyTextInput: { + minHeight: 38, + marginTop: spacing.sm, + borderColor: colors.border, + borderRadius: 10, + borderWidth: StyleSheet.hairlineWidth, + color: colors.text, + fontSize: 15, + fontWeight: '600', + paddingHorizontal: spacing.md, + }, propertyChip: { minHeight: 30, justifyContent: 'center', diff --git a/apps/mobile/src/useMobileNoteCreateFlow.ts b/apps/mobile/src/useMobileNoteCreateFlow.ts index 99af1715..c47b056b 100644 --- a/apps/mobile/src/useMobileNoteCreateFlow.ts +++ b/apps/mobile/src/useMobileNoteCreateFlow.ts @@ -10,36 +10,17 @@ export function useMobileNoteCreateFlow({ }) { const [failed, setFailed] = useState(false) const [isCreating, setIsCreating] = useState(false) - const [isPromptOpen, setIsPromptOpen] = useState(false) - const [title, setTitle] = useState('') - const cancel = useCallback(() => { - if (isCreating) { - return - } - - setFailed(false) - setIsPromptOpen(false) - setTitle('') - }, [isCreating]) - - const open = useCallback(() => { - setFailed(false) - setIsPromptOpen(true) - }, []) - - const submit = useCallback(() => { + const create = useCallback(() => { if (isCreating) { return } setFailed(false) setIsCreating(true) - void createNote(title) + void createNote('') .then((note) => { if (note) { - setIsPromptOpen(false) - setTitle('') onCreated(note) } }) @@ -49,16 +30,11 @@ export function useMobileNoteCreateFlow({ .finally(() => { setIsCreating(false) }) - }, [createNote, isCreating, onCreated, title]) + }, [createNote, isCreating, onCreated]) return { - cancel, + create, failed, isCreating, - isPromptOpen, - open, - setTitle, - submit, - title, } } diff --git a/docs/MOBILE_PROGRESS.md b/docs/MOBILE_PROGRESS.md index a5ea097d..35f8b639 100644 --- a/docs/MOBILE_PROGRESS.md +++ b/docs/MOBILE_PROGRESS.md @@ -106,6 +106,12 @@ This file is the resumable working log for Tolaria mobile. The strategy and road - Added the optional Expo native-module resolver for `TolariaGit`, so the JavaScript Git transport automatically binds to future development builds that include the native module and keeps the current unavailable-module failure everywhere else. - Created [ADR-0115](./adr/0115-expo-native-module-boundary-for-mobile-git.md) for the mobile Git native module discovery boundary. - Added remote host and auth strategy to the native Git transport request so native Git can select credential callbacks without reparsing remote URLs. +- Reworked the prototype compose flow so tapping the pencil immediately creates and opens an app-local `Untitled` note instead of blocking on a title modal. +- Moved the editor breadcrumb and save state into the top editor toolbar, matching the desktop/Bear-style bar placement instead of rendering it inside the editor body. +- Injected system-font CSS into TenTap so the editor uses iOS-native typography instead of the browser/WebView serif default, while leaving keyboard layout to the OS/simulator input source. +- Upgraded the mobile properties prototype so type, status, date, and tags can be entered from the keyboard with quick chips as shortcuts, while icon editing stays chip-based. +- Removed the now-unused title prompt component and kept the compose-button disabled state with the note-list styles. +- Split new breadcrumb styles out of the shared toolbar styles and removed stale editor/property style entries so the CodeScene pre-commit safeguard stays green. ## Next Action @@ -408,6 +414,11 @@ Continue Phase 4 with editor durability: - `pnpm --filter @tolaria/mobile typecheck` passed after adding remote host/auth strategy to native Git requests. - CodeScene after adding remote host/auth strategy to native Git requests: `apps/mobile/src/mobileNativeGitTransport.ts` and `apps/mobile/src/mobileNativeGitTransport.test.ts` scored `10`. - `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after adding remote host/auth strategy to native Git requests; Metro recovered from a cache deserialize warning by doing a full crawl. +- `pnpm --filter @tolaria/mobile test` passed after the direct-create/properties editability update: 46 files / 153 tests. +- `pnpm --filter @tolaria/mobile typecheck` passed after the direct-create/properties editability update. +- CodeScene after the direct-create/properties editability update: `apps/mobile/src/MobileApp.tsx`, `apps/mobile/src/MobileEditablePropertyPickers.tsx`, `apps/mobile/src/MobileEditorAdapter.tsx`, `apps/mobile/src/MobileEditorBreadcrumb.tsx`, `apps/mobile/src/MobilePropertiesPanel.tsx`, `apps/mobile/src/mobileNoteProperties.ts`, `apps/mobile/src/mobilePropertyPicker.ts`, `apps/mobile/src/styles/breadcrumbStyles.ts`, `apps/mobile/src/styles/commonStyles.ts`, `apps/mobile/src/styles/editorStyles.ts`, `apps/mobile/src/styles/noteListStyles.ts`, `apps/mobile/src/styles/propertyChipStyles.ts`, and `apps/mobile/src/useMobileNoteCreateFlow.ts` scored `10`; `apps/mobile/src/styles.ts` reported no scorable score. +- CodeScene pre-commit safeguard passed after splitting the new styles. +- `pnpm --filter @tolaria/mobile exec expo export --platform ios --output-dir /tmp/tolaria-mobile-export` passed after the direct-create/properties editability update. ## Risks / Watch Items