feat(workspace): journal with one note per day and a calendar
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m6s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m6s
- journal_days (migration 8) maps a person's day to an ordinary note;
GET /journal lists the days, POST /journal/{date} opens or creates one
- the journal sits on top of the notes sidebar: days below each other
(today first, older ones load while scrolling), a calendar with month
and year picker follows the day in view and jumps to any day
- the header shares the day in view; journal notes stay out of the folder
tree; can be switched off per device under Darstellung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
261579f0f2
commit
9008d7c915
19 files changed
+864
-19
No files matched your search
@@ -61,6 +61,8 @@ export interface DocumentEditorProps {
|
||||
/** A note was created from this one (extract) or removed again (undo); keeps the sidebar current */
|
||||
onDocCreated?: (doc: Document) => void;
|
||||
onDocRemoved?: (docId: string) => void;
|
||||
/** 'journal': one day inside the journal list – no own scrolling, header or backlinks, fixed title */
|
||||
variant?: 'page' | 'journal';
|
||||
}
|
||||
|
||||
import { EmojiPickerModal } from './EmojiPickerModal';
|
||||
@@ -211,7 +213,9 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
onRequestMerge,
|
||||
onDocCreated,
|
||||
onDocRemoved,
|
||||
variant = 'page',
|
||||
}) => {
|
||||
const inJournal = variant === 'journal';
|
||||
const [doc, setDoc] = useState<Document | null>(initialDoc || null);
|
||||
const [title, setTitle] = useState<string>(initialDoc?.title || '');
|
||||
const [blocks, setBlocks] = useState<DocumentBlock[]>(() =>
|
||||
@@ -245,7 +249,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
useEffect(() => {
|
||||
const root = scrollRef.current;
|
||||
const el = titleInputRef.current;
|
||||
if (!root || !el || typeof IntersectionObserver === 'undefined') {
|
||||
if (inJournal || !root || !el || typeof IntersectionObserver === 'undefined') {
|
||||
setTitleScrolledAway(false);
|
||||
return;
|
||||
}
|
||||
@@ -256,7 +260,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
);
|
||||
observer.observe(el);
|
||||
return () => observer.disconnect();
|
||||
}, [zoomedBlockId, doc?.id]);
|
||||
}, [zoomedBlockId, doc?.id, inJournal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && (autoFocusTitle || (doc?.title === 'Neue Notiz' && (!doc?.blocks || doc?.blocks.length <= 1)))) {
|
||||
@@ -782,8 +786,13 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={scrollRef} className="flex-1 flex flex-col h-full min-h-0 bg-editor overflow-y-auto" data-sync-channel={engineState?.channel}>
|
||||
{/* Top Document Action Bar */}
|
||||
<div
|
||||
ref={scrollRef}
|
||||
className={inJournal ? 'flex flex-col bg-editor' : 'flex-1 flex flex-col h-full min-h-0 bg-editor overflow-y-auto'}
|
||||
data-sync-channel={engineState?.channel}
|
||||
>
|
||||
{/* Top Document Action Bar (the journal has one for all days) */}
|
||||
{!inJournal && (
|
||||
<div className="sticky top-0 z-20 flex items-center justify-between gap-2 px-4 sm:px-6 py-2 bg-editor border-b border-border">
|
||||
<div className="flex flex-1 items-center gap-2 min-w-0">
|
||||
{onBackMobile && (
|
||||
@@ -1017,6 +1026,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* In the trash: read-only with a way back */}
|
||||
{inTrash && (
|
||||
@@ -1047,7 +1057,11 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
{/* Main Content Area */}
|
||||
{/* Phones: almost no side margin; very deep outlines scroll sideways (rows keep a minimum width) */}
|
||||
{/* shrink-0: the sideways scroll area must keep its full height, so only the editor scrolls vertically */}
|
||||
<div className="w-full pl-2 pr-1 sm:px-8 py-5 flex-auto shrink-0 overflow-x-auto sm:overflow-x-visible [-webkit-overflow-scrolling:touch]">
|
||||
<div
|
||||
className={`w-full pl-2 pr-1 sm:px-8 flex-auto shrink-0 overflow-x-auto sm:overflow-x-visible [-webkit-overflow-scrolling:touch] ${
|
||||
inJournal ? 'py-3' : 'py-5'
|
||||
}`}
|
||||
>
|
||||
{zoomedBlock ? (
|
||||
/* =========================================================================
|
||||
FOKUS-MODUS & ZOOM-IN (HOISTED SUBTREE VIEW)
|
||||
@@ -1127,6 +1141,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
</div>
|
||||
) : (
|
||||
<OutlinerTree
|
||||
compact={inJournal}
|
||||
docId={docId}
|
||||
blocks={childBlocks}
|
||||
canEdit={canEdit}
|
||||
@@ -1162,7 +1177,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => canEdit && setShowEmojiPicker((prev) => !prev)}
|
||||
onClick={() => canEdit && !inJournal && setShowEmojiPicker((prev) => !prev)}
|
||||
className="text-2xl sm:text-3xl hover:opacity-80 transition-opacity cursor-pointer select-none leading-none bg-transparent border-0 outline-none p-0 focus:outline-none focus:ring-0 shadow-none"
|
||||
title="Icon ändern"
|
||||
>
|
||||
@@ -1187,7 +1202,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
}}
|
||||
rows={1}
|
||||
value={title}
|
||||
readOnly={!canEdit}
|
||||
readOnly={!canEdit || inJournal}
|
||||
autoFocus={autoFocusTitle}
|
||||
onChange={(e) => {
|
||||
handleTitleChange(e);
|
||||
@@ -1214,6 +1229,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
|
||||
{/* Logseq Outliner Tree */}
|
||||
<OutlinerTree
|
||||
compact={inJournal}
|
||||
ref={outlinerTreeRef}
|
||||
docId={docId}
|
||||
blocks={blocks}
|
||||
@@ -1239,7 +1255,7 @@ export const DocumentEditor: React.FC<DocumentEditorProps> = ({
|
||||
onRedo={handleRedo}
|
||||
/>
|
||||
|
||||
{onOpenDocBlock && !inTrash && <BacklinksSection docId={docId} title={title} onOpen={onOpenDocBlock} />}
|
||||
{onOpenDocBlock && !inTrash && !inJournal && <BacklinksSection docId={docId} title={title} onOpen={onOpenDocBlock} />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user