diff --git a/src/app/editor/components/block/block-context-menu.component.ts b/src/app/editor/components/block/block-context-menu.component.ts index ce79136..84d745c 100644 --- a/src/app/editor/components/block/block-context-menu.component.ts +++ b/src/app/editor/components/block/block-context-menu.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, Output, EventEmitter, inject, HostListener, ElementRef, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, Input, Output, EventEmitter, inject, HostListener, ElementRef, OnChanges, SimpleChanges, ViewChild, OnDestroy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Block, BlockType } from '../../core/models/block.model'; import { DocumentService } from '../../services/document.service'; @@ -818,7 +818,7 @@ export interface MenuAction { @keyframes fadeIn { from { opacity:0; transform: scale(.97);} to { opacity:1; transform: scale(1);} } `] }) -export class BlockContextMenuComponent implements OnChanges { +export class BlockContextMenuComponent implements OnChanges, OnDestroy { @Input() block!: Block; @Input() visible = false; @Input() position = { x: 0, y: 0 }; @@ -838,15 +838,28 @@ export class BlockContextMenuComponent implements OnChanges { top = -9999; opacity = 0; + private appendedToBody = false; + constructor() { try { const el = this.elementRef.nativeElement as HTMLElement; if (el && el.parentElement !== document.body) { document.body.appendChild(el); + this.appendedToBody = true; } } catch {} } + ngOnDestroy(): void { + try { + const el = this.elementRef.nativeElement as HTMLElement; + if (el && el.parentElement === document.body && this.appendedToBody) { + document.body.removeChild(el); + } + } catch {} + this.closeSubmenu(); + } + @HostListener('document:click', ['$event']) onDocumentClick(event: MouseEvent): void { const root = this.menuRef?.nativeElement; @@ -1217,6 +1230,11 @@ export class BlockContextMenuComponent implements OnChanges { // Emit action for parent to handle (including ratios/alignment payload) this.action.emit({ type, payload }); } + // Locally hide the menu to ensure it disappears immediately, even if the host is destroyed right after + this.visible = false; + this.opacity = 0; + this.left = -9999; + this.top = -9999; this.close.emit(); } diff --git a/src/app/editor/components/block/block-host.component.ts b/src/app/editor/components/block/block-host.component.ts index 9936c13..71a4784 100644 --- a/src/app/editor/components/block/block-host.component.ts +++ b/src/app/editor/components/block/block-host.component.ts @@ -76,7 +76,7 @@ import { CollapsibleBlockComponent } from './blocks/collapsible-block.component' [attr.data-block-index]="index" [class.active]="isActive()" [class.locked]="block.meta?.locked" - [style.background-color]="(block.type === 'list-item' || block.type === 'file') ? null : block.meta?.bgColor" + [style.background-color]="(block.type === 'list-item' || block.type === 'file' || block.type === 'paragraph' || block.type === 'list' || block.type === 'heading') ? null : block.meta?.bgColor" [ngStyle]="blockStyles()" (click)="onBlockClick($event)" > @@ -101,8 +101,8 @@ import { CollapsibleBlockComponent } from './blocks/collapsible-block.component' } - -
+ +
@switch (block.type) { @case ('paragraph') {
@@ -191,16 +191,38 @@ import { CollapsibleBlockComponent } from './blocks/collapsible-block.component' }
- + - +
@@ -216,7 +238,7 @@ import { CollapsibleBlockComponent } from './blocks/collapsible-block.component' `, styles: [` .block-wrapper { - @apply relative py-1 px-3 rounded-md transition-all; + @apply relative py-0.5 px-3 rounded-md transition-all; /* No fixed min-height; let content define height */ } @@ -691,6 +713,7 @@ export class BlockHostComponent implements OnDestroy { this.documentService.duplicateBlock(this.block.id); break; case 'delete': + this.closeMenu(); this.documentService.deleteBlock(this.block.id); break; case 'lock': diff --git a/src/app/editor/components/block/block-inline-toolbar.component.ts b/src/app/editor/components/block/block-inline-toolbar.component.ts index 0da34fc..f34ed11 100644 --- a/src/app/editor/components/block/block-inline-toolbar.component.ts +++ b/src/app/editor/components/block/block-inline-toolbar.component.ts @@ -10,7 +10,7 @@ export interface InlineToolbarAction { standalone: true, imports: [CommonModule], template: ` -
+
@if (showDragHandle) {
- @switch (block.type) { @@ -134,7 +159,11 @@ import { PaletteItem } from '../../../core/constants/palette-items'; /> } @case ('list-item') { - + } @case ('code') { @@ -218,9 +247,6 @@ import { PaletteItem } from '../../../core/constants/palette-items'; }
- - - ; @Output() update = new EventEmitter(); - @ViewChild('commentsPanel') commentsPanel?: CommentsPanelComponent; @ViewChild('columnsContainer', { static: true }) columnsContainerRef!: ElementRef; // Menu state @@ -283,12 +309,20 @@ export class ColumnsBlockComponent implements AfterViewInit { private resizeState: { active: boolean; index: number; startX: number; containerWidth: number; leftStart: number; rightStart: number } | null = null; resizerPositions = signal([]); + // Comments popover state + private commentRef?: OverlayRef; + private commentSub: { unsubscribe(): void } | null = null; + get props(): ColumnsProps { return this.block.props; } getBlockCommentCount(blockId: string): number { - return this.commentService.getCommentCount(blockId); + try { + return this.commentsStore.count(blockId); + } catch { + return 0; + } } onConvertRequested(item: PaletteItem, blockId: string): void { @@ -315,7 +349,40 @@ export class ColumnsBlockComponent implements AfterViewInit { } openComments(blockId: string): void { - this.commentsPanel?.open(blockId); + this.closeComments(); + const container = this.columnsContainerRef?.nativeElement; + const anchor = (container?.querySelector(`[data-block-id="${blockId}"]`) as HTMLElement) || container; + if (!anchor) return; + + const pos = this.overlay.position().flexibleConnectedTo(anchor).withPositions([ + { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 8 }, + { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -8 } + ]); + + this.commentRef = this.overlay.create({ + hasBackdrop: true, + backdropClass: 'cdk-overlay-transparent-backdrop', + positionStrategy: pos, + panelClass: 'nimbus-menu-panel' + }); + + const portal = new ComponentPortal(BlockCommentComposerComponent); + const ref = this.commentRef.attach(portal); + ref.instance.blockId = blockId; + this.commentSub = ref.instance.close.subscribe(() => this.closeComments()); + this.commentRef.backdropClick().subscribe(() => this.closeComments()); + this.commentRef.keydownEvents().subscribe((e) => { if ((e as KeyboardEvent).key === 'Escape') this.closeComments(); }); + } + + private closeComments(): void { + if (this.commentSub) { + try { this.commentSub.unsubscribe(); } catch {} + this.commentSub = null; + } + if (this.commentRef) { + this.commentRef.dispose(); + this.commentRef = undefined; + } } onBlockMetaChange(metaChanges: any, blockId: string): void { @@ -362,6 +429,54 @@ export class ColumnsBlockComponent implements AfterViewInit { } }, 50); } + + onListItemCreateBelow(blockId: string, columnIndex: number, blockIndex: number): void { + // Insert a new list-item block directly after the given list-item within the same column + const updatedColumns = this.props.columns.map((column, colIdx) => { + if (colIdx !== columnIndex) return column; + + const blocks = [...column.blocks]; + const existing = blocks[blockIndex]; + if (!existing || existing.type !== 'list-item') { + return column; + } + + const props: any = existing.props || {}; + const newProps: any = { + kind: props.kind, + text: '', + checked: props.kind === 'check' ? false : undefined, + indent: props.indent || 0, + align: props.align + }; + + if (props.kind === 'numbered' && typeof props.number === 'number') { + newProps.number = props.number + 1; + } + + const newBlock = this.documentService.createBlock('list-item' as any, newProps); + const newBlocks = [...blocks]; + newBlocks.splice(blockIndex + 1, 0, newBlock); + + return { ...column, blocks: newBlocks }; + }); + + this.update.emit({ columns: updatedColumns }); + + // Focus the new list-item input once it is rendered + const newId = updatedColumns[columnIndex]?.blocks[blockIndex + 1]?.id; + if (!newId) return; + setTimeout(() => { + const input = document.querySelector( + `[data-block-id="${newId}"] input[type="text"]` + ) as HTMLInputElement | null; + if (input) { + input.focus(); + const len = input.value.length; + input.setSelectionRange(len, len); + } + }, 50); + } onBlockDelete(blockId: string): void { // Delete a specific block from columns @@ -648,7 +763,9 @@ export class ColumnsBlockComponent implements AfterViewInit { } getBlockBgColor(block: Block): string | undefined { - if (block.type === 'paragraph') { + // Paragraph, heading and list(-item) blocks in columns should not have a full-width + // background; their inner editable/input pill handles the colored capsule. + if (block.type === 'paragraph' || block.type === 'heading' || block.type === 'list' || block.type === 'list-item') { return undefined; } const bgColor = (block.meta as any)?.bgColor; @@ -680,6 +797,10 @@ export class ColumnsBlockComponent implements AfterViewInit { setTimeout(() => this.computeResizerPositions(), 0); } + ngOnDestroy(): void { + this.closeComments(); + } + @HostListener('window:resize') onWindowResize(): void { this.computeResizerPositions(); diff --git a/src/app/editor/components/block/blocks/heading-block.component.ts b/src/app/editor/components/block/blocks/heading-block.component.ts index 665debf..23d6579 100644 --- a/src/app/editor/components/block/blocks/heading-block.component.ts +++ b/src/app/editor/components/block/blocks/heading-block.component.ts @@ -9,38 +9,46 @@ import { DocumentService } from '../../../services/document.service'; standalone: true, imports: [CommonModule, FormsModule], template: ` - @switch (props.level) { - @case (1) { -

+
+ @switch (props.level) { + @case (1) { +
+

+
+ } + @case (2) { +
+

+
+ } + @case (3) { +
+

+
+ } } - @case (2) { -

- } - @case (3) { -

- } - } +
`, styles: [` [contenteditable]:empty:before { @@ -67,6 +75,12 @@ export class HeadingBlockComponent implements AfterViewInit { return this.block.props; } + getBlockBgColor(): string | undefined { + const meta: any = this.block?.meta || {}; + const bgColor = meta.bgColor; + return bgColor && bgColor !== 'transparent' ? bgColor : undefined; + } + ngAfterViewInit(): void { if (this.editable?.nativeElement) { this.editable.nativeElement.textContent = this.props.text || ''; diff --git a/src/app/editor/components/block/blocks/list-block.component.ts b/src/app/editor/components/block/blocks/list-block.component.ts index c5958fb..9829bef 100644 --- a/src/app/editor/components/block/blocks/list-block.component.ts +++ b/src/app/editor/components/block/blocks/list-block.component.ts @@ -13,18 +13,18 @@ import { SelectionService } from '../../../services/selection.service'; template: `
@for (it of items(); track it.id; let i = $index) { -
+
-
+
@if (kind() === 'bullet') { -
+
} @else if (kind() === 'check') { } @else { - {{ i + 1 }}. + {{ i + 1 }}. }
+
-
+
@if (props.kind === 'bullet') { - {{ getBulletSymbol() }} + {{ getBulletSymbol() }} } @else if (props.kind === 'check') {
} @else { - {{ props.number || 1 }}. + {{ props.number || 1 }}. }
; @Output() update = new EventEmitter(); + // Optional: when provided (e.g. in ColumnsBlockComponent), delegate creation of the next item + // to the parent container instead of inserting a top-level block via DocumentService. + @Output() createBelow = new EventEmitter(); @ViewChild('inp') input!: ElementRef; @@ -162,12 +165,16 @@ export class ListItemBlockComponent implements OnInit, AfterViewInit { if (ev.key === 'Enter' && !ev.shiftKey) { ev.preventDefault(); - // Get the current block index + // If parent container listens to createBelow (e.g. columns), delegate creation to it + if (this.createBelow.observers.length > 0) { + this.createBelow.emit(); + return; + } + + // Fallback: legacy behavior for top-level list-item blocks const blocks = this.documentService.blocks(); const currentIndex = blocks.findIndex(b => b.id === this.block.id); - if (currentIndex !== -1) { - // Create new list item with same kind and indent let newProps: ListItemProps = { kind: this.props.kind, text: '', @@ -175,12 +182,11 @@ export class ListItemBlockComponent implements OnInit, AfterViewInit { indent: this.props.indent || 0, align: this.props.align }; - - // For numbered lists, increment the number + if (this.props.kind === 'numbered' && this.props.number) { newProps.number = this.props.number + 1; } - + const newBlock = this.documentService.createBlock('list-item' as any, newProps); this.documentService.insertBlock(this.block.id, newBlock); this.selection.setActive(newBlock.id); diff --git a/src/app/editor/components/comment/block-comment-composer.component.ts b/src/app/editor/components/comment/block-comment-composer.component.ts index e25dd2e..5270164 100644 --- a/src/app/editor/components/comment/block-comment-composer.component.ts +++ b/src/app/editor/components/comment/block-comment-composer.component.ts @@ -11,53 +11,57 @@ import { CommentActionMenuComponent } from './comment-action-menu.component'; standalone: true, imports: [CommonModule, FormsModule, OverlayModule, PortalModule], template: ` -
-
-
Comments
-
-
+
-
+
+ {{ (c.author || 'U').slice(0, 2) }} +
-
-
{{ c.author || 'User' }}
+
+
{{ c.author || 'User' }}
-
{{ c.createdAt | date:'shortTime' }}
-
-
{{ c.text }}
+
{{ c.text }}
-
+
- - + +
-
+
-
No comments yet.
+
No comments yet.
-
+
-
+
+ {{ 'You' | slice:0:2 }} +
-
diff --git a/src/app/editor/components/comment/comment-action-menu.component.ts b/src/app/editor/components/comment/comment-action-menu.component.ts index ddde7c2..b4c7968 100644 --- a/src/app/editor/components/comment/comment-action-menu.component.ts +++ b/src/app/editor/components/comment/comment-action-menu.component.ts @@ -12,16 +12,16 @@ export interface CommentMenuItem { standalone: true, imports: [CommonModule], template: ` -
- - - diff --git a/src/app/features/editor/blocks/table/table-editor.component.html b/src/app/features/editor/blocks/table/table-editor.component.html index a447b5c..93ed76a 100644 --- a/src/app/features/editor/blocks/table/table-editor.component.html +++ b/src/app/features/editor/blocks/table/table-editor.component.html @@ -148,15 +148,37 @@
- + diff --git a/vault/.obsidian/bookmarks.json b/vault/.obsidian/bookmarks.json index 491add1..e5dd774 100644 --- a/vault/.obsidian/bookmarks.json +++ b/vault/.obsidian/bookmarks.json @@ -10,7 +10,7 @@ "type": "file", "path": "tata/Les Compléments Alimentaires Un Guide Général.md", "title": "Les Compléments Alimentaires Un Guide Général.md", - "ctime": 1763156633684 + "ctime": 1763267370357 } ] } \ No newline at end of file diff --git a/vault/Allo-3/Stargate Atlantis.md b/vault/Allo-3/Stargate Atlantis.md index 07a1559..6827ab3 100644 --- a/vault/Allo-3/Stargate Atlantis.md +++ b/vault/Allo-3/Stargate Atlantis.md @@ -3,7 +3,6 @@ titre: "Nouvelle note 1" auteur: "Bruno Charest" creation_date: "2025-10-24T03:30:58.977Z" modification_date: "2025-11-03T22:06:07-04:00" -tags: [""] status: "en-cours" publish: false favoris: true @@ -13,6 +12,11 @@ archive: true draft: false private: false description: "Une expédition militaire et scientifique découvre la cité mythique d'Atlantis dans la galaxie de Pégase et affronte les Wraiths." +tags: + - titi + - test + - tag4 + - configuration --- *Stargate Atlantis* est une série de science-fiction dérivée de la populaire *Stargate SG-1*. Elle suit les aventures d'une expédition internationale, composée de scientifiques et de militaires, qui voyage à travers la porte des étoiles vers la lointaine galaxie de Pégase. Leur destination est la cité mythique d'Atlantis, une métropole volante abandonnée construite par une race ancienne et technologiquement supérieure connue sous le nom d'Anciens. diff --git a/vault/attachments/nimbus/2025/1115/img-7086e8a9-2d58-4a7d-9002-dfb6512c71ec-png-20251115-232622-ir44dv.png b/vault/attachments/nimbus/2025/1115/img-7086e8a9-2d58-4a7d-9002-dfb6512c71ec-png-20251115-232622-ir44dv.png new file mode 100644 index 0000000..87c8951 Binary files /dev/null and b/vault/attachments/nimbus/2025/1115/img-7086e8a9-2d58-4a7d-9002-dfb6512c71ec-png-20251115-232622-ir44dv.png differ diff --git a/vault/tests/nimbus-editor-snapshot.md b/vault/tests/nimbus-editor-snapshot.md index cf420e8..ef0f5df 100644 --- a/vault/tests/nimbus-editor-snapshot.md +++ b/vault/tests/nimbus-editor-snapshot.md @@ -10,338 +10,59 @@ documentModelFormat: "block-model-v1" "title": "Page Tests", "blocks": [ { - "id": "block_1763234865120_um14zlycy", + "id": "block_1763307699824_r3elleo33", "type": "heading", "props": { "level": 1, - "text": "H1" + "text": "asdassda" }, "meta": { - "createdAt": "2025-11-15T19:27:45.120Z", - "updatedAt": "2025-11-15T20:06:49.723Z", - "align": "center", - "bgColor": "#dc2626" + "createdAt": "2025-11-16T15:41:39.824Z", + "updatedAt": "2025-11-16T15:41:42.459Z" } }, { - "id": "block_1763240860187_hdklpobdf", - "type": "line", - "props": { - "style": "solid" - }, - "meta": { - "createdAt": "2025-11-15T21:07:40.187Z", - "updatedAt": "2025-11-15T21:36:29.164Z" - } - }, - { - "id": "block_1763234657433_5malb56fe", - "type": "list-item", - "props": { - "kind": "check", - "text": "checkbox", - "checked": false, - "indent": 0, - "align": "left" - }, - "meta": { - "createdAt": "2025-11-15T19:24:17.433Z", - "updatedAt": "2025-11-15T20:13:56.404Z", - "bgColor": "#dc2626" - } - }, - { - "id": "block_1763237180130_m82opx9yx", - "type": "paragraph", - "props": { - "text": "paragraphe" - }, - "meta": { - "createdAt": "2025-11-15T20:06:20.130Z", - "updatedAt": "2025-11-15T20:13:50.464Z", - "bgColor": "#dc2626" - } - }, - { - "id": "block_1763234665227_e09ql6sb4", - "type": "list-item", - "props": { - "kind": "bullet", - "text": "bullet", - "indent": 0, - "align": "left" - }, - "meta": { - "createdAt": "2025-11-15T19:24:25.227Z", - "updatedAt": "2025-11-15T20:07:27.797Z", - "bgColor": "#dc2626" - } - }, - { - "id": "block_1763237840896_fuw5hvm9t", - "type": "columns", + "id": "block_1763308160356_nfhdtf1p1", + "type": "kanban", "props": { "columns": [ { - "id": "or66s9hqb", - "blocks": [ + "id": "block_1763308177122_doyf2zh37", + "title": "To Do", + "cards": [ { - "id": "block_1763237836743_a06ez4lux", - "type": "paragraph", - "props": { - "text": "paragraphe" - }, - "meta": { - "createdAt": "2025-11-15T20:17:16.743Z", - "updatedAt": "2025-11-15T20:17:16.743Z", - "bgColor": "#dc2626" - } + "id": "item_1763308197023_pbeezlint", + "title": "New Card 2", + "description": "" }, { - "id": "n90fsx72s", - "type": "paragraph", - "props": { - "text": "paragraphe" - }, - "children": [], - "meta": { - "bgColor": "#dc2626" - } + "id": "item_1763308195207_1skel85f7", + "title": "New Card 1", + "description": "" }, { - "id": "tkk3ir62w", - "type": "paragraph", - "props": { - "text": "" - }, - "children": [] - }, - { - "id": "ljyumvc5w", - "type": "paragraph", - "props": { - "text": "" - }, - "children": [] - }, - { - "id": "rae60bsx3", - "type": "paragraph", - "props": { - "text": "" - }, - "children": [] - }, - { - "id": "xa3p8sybb", - "type": "paragraph", - "props": { - "text": "" - }, - "children": [] - }, - { - "id": "qln2xcscy", - "type": "paragraph", - "props": { - "text": "" - }, - "children": [] + "id": "item_1763308197933_aj34wtfd9", + "title": "New Card 3", + "description": "" } - ], - "width": 50 + ] }, { - "id": "s16nsirzh", - "blocks": [ - { - "id": "block_1763237223906_8fazui2p9", - "type": "paragraph", - "props": { - "text": "paragraphe" - }, - "meta": { - "createdAt": "2025-11-15T20:07:03.906Z", - "updatedAt": "2025-11-15T20:07:24.349Z", - "bgColor": "#dc2626" - } - } - ], - "width": 50 + "id": "item_1763308190239_dmw2vomdm", + "title": "done", + "cards": [] } ] }, "meta": { - "createdAt": "2025-11-15T20:17:20.896Z", - "updatedAt": "2025-11-15T22:13:34.707Z" - } - }, - { - "id": "block_1763237641170_alnjnsj8c", - "type": "list-item", - "props": { - "kind": "numbered", - "text": "number list 1", - "indent": 0, - "align": "left", - "number": 1 - }, - "meta": { - "createdAt": "2025-11-15T20:14:01.171Z", - "updatedAt": "2025-11-15T20:14:14.163Z", - "bgColor": "#dc2626" - } - }, - { - "id": "block_1763238667046_3xvnp49yo", - "type": "heading", - "props": { - "level": 1, - "text": "H2" - }, - "meta": { - "createdAt": "2025-11-15T20:31:07.046Z", - "updatedAt": "2025-11-15T21:07:39.675Z", - "bgColor": "#525252" - } - }, - { - "id": "block_1763238706236_v3kaqyax7", - "type": "heading", - "props": { - "level": 1, - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:46.236Z", - "updatedAt": "2025-11-15T21:55:37.233Z" - } - }, - { - "id": "block_1763238706552_9zpoiafug", - "type": "table", - "props": { - "rows": [ - { - "id": "block_1763248205146_pnw8cifrx", - "cells": [ - { - "id": "block_1763248205146_p9wc7se4g", - "text": "" - } - ] - } - ], - "header": false - }, - "meta": { - "createdAt": "2025-11-15T20:31:46.552Z", - "updatedAt": "2025-11-15T23:10:05.176Z" - } - }, - { - "id": "block_1763238706720_x5fdjzcm1", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:46.720Z", - "updatedAt": "2025-11-15T20:32:24.875Z" - } - }, - { - "id": "block_1763238706867_cbgv0yetb", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:46.867Z", - "updatedAt": "2025-11-15T20:31:46.867Z" - } - }, - { - "id": "block_1763238707162_bh255zvaf", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:47.162Z", - "updatedAt": "2025-11-15T20:31:47.162Z" - } - }, - { - "id": "block_1763248197366_ynjr796za", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T23:09:57.366Z", - "updatedAt": "2025-11-15T23:09:57.366Z" - } - }, - { - "id": "block_1763248197834_sb477zyix", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T23:09:57.834Z", - "updatedAt": "2025-11-15T23:09:57.834Z" - } - }, - { - "id": "block_1763248198129_38lauudq6", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T23:09:58.130Z", - "updatedAt": "2025-11-15T23:09:58.130Z" - } - }, - { - "id": "block_1763248199064_xs3run960", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T23:09:59.064Z", - "updatedAt": "2025-11-15T23:09:59.064Z" - } - }, - { - "id": "block_1763238707334_uupqjbbia", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:47.334Z", - "updatedAt": "2025-11-15T20:31:47.334Z" - } - }, - { - "id": "block_1763238707506_lj73550jb", - "type": "paragraph", - "props": { - "text": "" - }, - "meta": { - "createdAt": "2025-11-15T20:31:47.506Z", - "updatedAt": "2025-11-15T20:31:47.506Z" + "createdAt": "2025-11-16T15:49:20.356Z", + "updatedAt": "2025-11-16T15:50:10.618Z" } } ], "meta": { "createdAt": "2025-11-14T19:38:33.471Z", - "updatedAt": "2025-11-15T23:10:05.176Z" + "updatedAt": "2025-11-16T15:50:10.618Z" } } ```