{/* Header row - parent link on left, Notebooks dropdown on right */}
{/* Parent link - shows first parent with up arrow */}
hasParentNotebooks ? "flex" : "none"
),
alignItems: "center",
gap: "4px",
}}
>
⬆️
{/* Spacer when no parent */}
hasParentNotebooks ? "none" : "block"
),
}}
/>
allNotesCharm ? "flex" : "none"),
}}
>
📁 All Notes
{/* Header - also a drop zone for receiving items from "Other notebooks" */}
{/* Editable Title */}
isEditingTitle.get() ? "none" : "flex"
),
alignItems: "center",
gap: "8px",
cursor: "pointer",
}}
onClick={startEditingTitle({ isEditingTitle })}
>
📓 {title} ({noteCount})
isEditingTitle.get() ? "flex" : "none"
),
flex: 1,
marginRight: "12px",
}}
>
📝
New
📓
New
hasNotes ? "flex" : "none"),
}}
>
{/* Notes List - using ct-table like default-app for consistent spacing */}
{notes.map((note, index) => {
const dragHandle = (
⠿
);
const link = (
);
return (
selectedNoteIndices.get().includes(index)
? "var(--ct-color-bg-secondary, #f5f5f7)"
: "transparent"
),
}}
>
|
{dragHandle}
|
selectedNoteIndices.get().includes(index)
)}
/>
|
{/* Wrap all items in drop zone - handler checks if target is a notebook */}
{link}
|
✕
|
);
})}
{/* Select All footer - only show when more than 1 item */}
notes.length > 1 ? "flex" : "none"
),
alignItems: "center",
padding: "4px 0",
fontSize: "13px",
color: "var(--ct-color-text-secondary, #6e6e73)",
}}
>
{/* Match table column widths: drag (24px + 4px padding) */}
{/* Checkbox column (32px + 4px padding) */}
notes.length > 0 &&
selectedNoteIndices.get().length === notes.length
)}
onct-change={computed(() =>
selectedNoteIndices.get().length === notes.length
? deselectAllNotes({ selectedNoteIndices })
: selectAllNotes({ notes, selectedNoteIndices })
)}
/>
{/* Text aligned with charm pills */}
Select All
{/* Action Bar - Use CSS display to keep DOM alive (preserves handler streams) */}
(hasSelection ? "flex" : "none")),
background: "var(--ct-color-bg-secondary, #f5f5f7)",
borderRadius: "8px",
alignItems: "center",
marginTop: "8px",
}}
>
{selectedCount} selected
Duplicate
Delete
{/* Sibling Notebooks (Other notebooks from space) - vertical list */}
hasSiblingNotebooks ? "flex" : "none"
),
marginTop: "16px",
paddingTop: "16px",
borderTop: "1px solid var(--ct-color-border, #e5e5e7)",
}}
>
Other notebooks:
{siblingNotebooks.map((notebook) => (
{/* Sibling notebooks use "sibling" type - can only drop on main card */}
⠿
{/* Drop zone receives items from notes list above */}
))}
{/* New Notebook Prompt Modal - Use CSS display to keep DOM alive for reactivity */}
showNewNotebookPrompt.get() ? "flex" : "none"
),
position: "fixed",
inset: "0",
background: "rgba(0,0,0,0.5)",
alignItems: "center",
justifyContent: "center",
zIndex: "9999",
}}
>
New Notebook
Cancel
Create
{/* New Note Prompt Modal */}
showNewNotePrompt.get() ? "flex" : "none"
),
position: "fixed",
inset: "0",
background: "rgba(0,0,0,0.5)",
alignItems: "center",
justifyContent: "center",
zIndex: "9999",
}}
>
New Note
Cancel
Create Another
Create
{/* New Nested Notebook Prompt Modal */}
showNewNestedNotebookPrompt.get() ? "flex" : "none"
),
position: "fixed",
inset: "0",
background: "rgba(0,0,0,0.5)",
alignItems: "center",
justifyContent: "center",
zIndex: "9999",
}}
>
New Notebook
Cancel
Create Another
Create
{/* Backlinks footer - show charms that link to this notebook */}
backlinks.get().length > 0 ? "flex" : "none"
),
alignItems: "center",
borderTop: "1px solid var(--ct-color-border, #e5e5e7)",
flexWrap: "wrap",
}}
>
Linked from:
{backlinks.map((charm) => (
{charm?.[NAME]}
))}
),
title,
notes,
noteCount,
backlinks,
// Make notes discoverable via [[ autocomplete system-wide
mentionable: notes,
// LLM-callable streams for omnibot integration
createNote: handleCreateNote({ notes, allCharms }),
createNotes: handleCreateNotes({ notes, allCharms }),
setTitle: handleSetTitle({ title }),
createNotebook: handleCreateNotebook({ allCharms }),
};
},
);
export default Notebook;