feat(workspace): add focus mode and hoisting zoom-in for outliner blocks
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
- Click bullet point (•) or hover zoom icon to zoom into any block branch - Render hoisted block as prominent heading in focus mode with direct editing - Display clickable breadcrumb path leading back to ancestors or root document - Render isolated subtree with visual level offset and bound indentation
This commit is contained in:
1 parent
95977ba15a
commit
177e24fdc8
3 files changed
+346
-78
No files matched your search
@@ -8,6 +8,8 @@ export interface OutlinerTreeProps {
|
||||
docId: string;
|
||||
blocks: DocumentBlock[];
|
||||
canEdit: boolean;
|
||||
levelOffset?: number;
|
||||
onZoomBlock?: (blockId: string) => void;
|
||||
remoteCursors?: Record<string, RemoteCursor>;
|
||||
sendCursorMoved?: (blockId: string | null, selectionStart?: number, selectionEnd?: number) => void;
|
||||
onUpdateBlock: (updated: DocumentBlock, immediate?: boolean) => void;
|
||||
@@ -22,6 +24,8 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
docId,
|
||||
blocks,
|
||||
canEdit,
|
||||
levelOffset = 0,
|
||||
onZoomBlock,
|
||||
remoteCursors,
|
||||
sendCursorMoved,
|
||||
onUpdateBlock,
|
||||
@@ -105,7 +109,7 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
const hasChildren = Boolean(nextBlock && nextLvl > lvl);
|
||||
|
||||
const prevBlock = i > 0 ? blocks[i - 1] : null;
|
||||
const maxAllowedLevel = prevBlock ? Math.max(0, prevBlock.level || 0) + 1 : 0;
|
||||
const maxAllowedLevel = prevBlock ? Math.max(0, prevBlock.level || 0) + 1 : (levelOffset || 0);
|
||||
|
||||
result.push({
|
||||
block: b,
|
||||
@@ -155,8 +159,8 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
const idx = blocks.findIndex((b) => b.id === blockId);
|
||||
if (idx === -1) return;
|
||||
|
||||
// If this is the only block in the document, clear its content/type instead of leaving 0 blocks
|
||||
if (blocks.length <= 1) {
|
||||
// If this is the only block in the document (and not in an isolated zoom subtree), clear instead of deleting
|
||||
if (blocks.length <= 1 && !levelOffset) {
|
||||
const only = blocks[0];
|
||||
onUpdateBlock(
|
||||
{
|
||||
@@ -191,10 +195,11 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
const prevLevel = idx > 0 ? Math.max(0, blocks[idx - 1].level || 0) : 0;
|
||||
const maxLevel = idx === 0 ? 0 : prevLevel + 1;
|
||||
|
||||
const minLevel = levelOffset || 0;
|
||||
const nextLevel =
|
||||
delta === 1
|
||||
? Math.min(maxLevel, currentLevel + 1)
|
||||
: Math.max(0, currentLevel - 1);
|
||||
: Math.max(minLevel, currentLevel - 1);
|
||||
|
||||
if (nextLevel === currentLevel) return;
|
||||
|
||||
@@ -283,6 +288,8 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
hasChildren={hasChildren}
|
||||
isFocused={focusedBlockId === block.id}
|
||||
canEdit={canEdit}
|
||||
levelOffset={levelOffset}
|
||||
onZoomBlock={onZoomBlock}
|
||||
activeRemoteCursors={cursorsByBlock[block.id] || []}
|
||||
sendCursorMoved={sendCursorMoved}
|
||||
onFocusBlock={setFocusedBlockId}
|
||||
@@ -315,7 +322,7 @@ export const OutlinerTree: React.FC<OutlinerTreeProps> = ({
|
||||
documentId: docId,
|
||||
type: 'text',
|
||||
content: '',
|
||||
level: 0,
|
||||
level: levelOffset || 0,
|
||||
position: 1000,
|
||||
};
|
||||
pendingFocusRef.current = { blockId: firstBlock.id, cursor: 'start' };
|
||||
|
||||
Reference in new issue
Block a user