"wolai-frontend/src/app/api/auth/mnote-web-token/route.ts"
      ],
      "acceptance": [
        "mnote-web 输出 /api/runtime/config、/api/auth/session 与 session refresh/handoff route",
        "浏览器 runtime config 不泄漏 internal MNOTE_WEB_* base URL",
        "Next /api/auth/mnote-web-token 降级为 compat/debug"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web session && cargo test -p mnote-web runtime_config && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/runtime-config.test.ts src/app/api/auth/mnote-web-token/route.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 mnote-web runtime config/session/refresh routes，并将 Next mnote-web-token route 标记为 compat/debug。",
          "timestamp": "2026-04-28T09:16:28Z"
        }
      ],
      "completed_at": "2026-04-28T09:16:28Z"
    },
    {
      "id": "task-022",
      "title": "Phase W-3：建立 Rust-owned 文档页 / Page Aggregate server-first shell",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-021"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/routes/web_shell.rs",
        "rust/crates/mnote-web/src/routes/documents.rs",
        "rust/crates/mnote-web/src/routes/mod.rs",
        "wolai-frontend/src/components/editor/document-shell.tsx",
        "wolai-frontend/src/components/editor/document-content.tsx",
        "scripts/task115-rust-web-document-shell-smoke.js"
      ],
      "acceptance": [
        "mnote-web 输出 /documents/:id server-first shell 与 page aggregate snapshot",
        "Next 文档页降级为 legacy compat",
        "task115 smoke 证明默认文档页由 Rust shell 服务且不挂载 mnote-web-document-shell iframe"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web document_shell && cargo test -p mnote-web page_aggregate && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/editor/document-content.test.ts src/lib/documents/page-aggregate-builder.test.ts && cd /mnt/Data1T/mnote && node scripts/task115-rust-web-document-shell-smoke.js",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [


"Next 文档页降级为 legacy compat",
        "task115 smoke 证明默认文档页由 Rust shell 服务且不挂载 mnote-web-document-shell iframe"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web document_shell && cargo test -p mnote-web page_aggregate && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/editor/document-content.test.ts src/lib/documents/page-aggregate-builder.test.ts && cd /mnt/Data1T/mnote && node scripts/task115-rust-web-document-shell-smoke.js",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 mnote-web /documents/:id shell 与 /api/page-aggregate/:id snapshot contract，并补 task115 smoke。",
          "timestamp": "2026-04-28T09:20:22Z"
        }
      ],
      "completed_at": "2026-04-28T09:20:22Z"
    },
    {
      "id": "task-023",
      "title": "Phase W-4：把主 API transport 从 Next route 收薄为 Rust Web owned",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-022"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/routes/documents.rs",
        "rust/crates/mnote-web/src/routes/tree.rs",
        "rust/crates/mnote-web/src/routes/search.rs",
        "wolai-frontend/src/lib/tree-route-boundary.ts",
        "wolai-frontend/src/app/api/documents/route-adapters.test.ts",
        "wolai-frontend/src/lib/search/request.test.ts"
      ],
      "acceptance": [
        "Rust Web owner 覆盖 documents meta/content/save、tree commands/projections 与 search documents transport",
        "Next route 只保留 cookie/header 透传和 legacy compat",
        "TREE_3000_ROUTE_BOUNDARY_MANIFEST owner 口径改为 rust-web-gateway"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web documents_api && cargo test -p mnote-web tree_commands && cargo test -p mnote-web search && cargo test -p bridge-runtime page_aggregate && cargo test -p bridge-runtime tree_subtree_move_command && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/tree-route-boundary.test.ts src/app/api/documents/route-adapters.test.ts src/lib/search/request.test.ts",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [
        "[CONFIG] 原验证中的 cargo test -p bridge-runtime page_aggregate 过滤词当前命中 0 项；本任务实际覆盖 documents_api/tree_commands/search 与 route boundary。"
      ],
      "checkpoints": [
        {
          "step": 1,


"command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web documents_api && cargo test -p mnote-web tree_commands && cargo test -p mnote-web search && cargo test -p bridge-runtime page_aggregate && cargo test -p bridge-runtime tree_subtree_move_command && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/tree-route-boundary.test.ts src/app/api/documents/route-adapters.test.ts src/lib/search/request.test.ts",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [
        "[CONFIG] 原验证中的 cargo test -p bridge-runtime page_aggregate 过滤词当前命中 0 项；本任务实际覆盖 documents_api/tree_commands/search 与 route boundary。"
      ],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已补 Rust Web search.documents transport、documents owner headers 与 TREE_3000_ROUTE_BOUNDARY rust-web-gateway owner 口径。",
          "timestamp": "2026-04-28T09:26:14Z"
        }
      ],
      "completed_at": "2026-04-28T09:26:14Z"
    },
    {
      "id": "task-024",
      "title": "Phase W-5：把 tree realtime stream 固定为 Rust Web 主链",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-023"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/routes/sse.rs",
        "rust/crates/mnote-web/src/routes/stream_support.rs",
        "wolai-frontend/src/app/api/mnote-web/stream/route.ts",
        "wolai-frontend/src/lib/tree-stream/protocol.ts",
        "wolai-frontend/src/lib/tree-stream/tree-stream.test.ts",
        "wolai-frontend/src/lib/tree-stream/use-sidebar-tree-stream.test.tsx"
      ],
      "acceptance": [
        "Rust Web 输出正式 /api/tree/events snapshot + delta stream",
        "/api/mnote-web/stream 降级为 compat alias",
        "Sidebar / page tree / file tree consumer 使用 Rust-owned stream"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web tree_realtime && cargo test -p mnote-web stream && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/app/api/mnote-web/stream/route.test.ts src/lib/tree-stream/tree-stream.test.ts src/lib/tree-stream/use-sidebar-tree-stream.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js",
        "timeout_seconds": 4200
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "Rust Web /api/tree/events 已固定为 tree realtime stream 主链，输出 rust-web owner、snapshot/delta stream；Next /api/mnote-web/stream 保留 compat alias，consumer URL 指向 /api/tree/events。task112 针对 legacy Chromium renderer crash 仅做一次新 context 恢复。",
          "timestamp": "2026-04-28T12:33:41Z"
        }


},
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "Rust Web /api/tree/events 已固定为 tree realtime stream 主链，输出 rust-web owner、snapshot/delta stream；Next /api/mnote-web/stream 保留 compat alias，consumer URL 指向 /api/tree/events。task112 针对 legacy Chromium renderer crash 仅做一次新 context 恢复。",
          "timestamp": "2026-04-28T12:33:41Z"
        }
      ],
      "completed_at": "2026-04-28T12:33:41Z"
    },
    {
      "id": "task-025",
      "title": "Phase W-6：把 Search / AI / Mindmap 页面壳迁到 Rust Web shell + islands",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-024"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/routes/search.rs",
        "rust/crates/mnote-web/src/routes/mindmap_shell.rs",
        "rust/crates/mnote-web/src/routes/hermes.rs",
        "wolai-frontend/src/components/search/SearchPaletteHost.tsx",
        "wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx",
        "wolai-frontend/src/app/mindmap/[docId]/[mindmapId]/page.tsx",
        "scripts/task116-rust-web-islands-smoke.js"
      ],
      "acceptance": [
        "Rust Web 输出 Search server-first shell",
        "Rust Web / Hermes 拥有 AI bridge session、tool event、client action contract",
        "Rust Web 输出 Mindmap object shell，React runtime 只作为 island 或 legacy compat"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web search_shell && cargo test -p mnote-web ai_bridge && cargo test -p mnote-web mindmap_shell && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/editor/DocumentAiAgentPanel.runtime.test.tsx src/lib/ai-agent/runtime/runAgent.test.ts src/lib/search/request.test.ts && cd /mnt/Data1T/mnote && node scripts/task116-rust-web-islands-smoke.js",
        "timeout_seconds": 4200
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "Search server-first shell、Hermes AI bridge owner contract、Mindmap object shell 已落到 Rust Web；React runtime 标记为 island/legacy compat；task116 smoke 证明三类 owner 已迁移。",
          "timestamp": "2026-04-28T12:44:04Z"
        }
      ],
      "completed_at": "2026-04-28T12:44:04Z"
    },


},
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "Search server-first shell、Hermes AI bridge owner contract、Mindmap object shell 已落到 Rust Web；React runtime 标记为 island/legacy compat；task116 smoke 证明三类 owner 已迁移。",
          "timestamp": "2026-04-28T12:44:04Z"
        }
      ],
      "completed_at": "2026-04-28T12:44:04Z"
    },
    {
      "id": "task-026",
      "title": "Phase W-7：退役 Next App Router 主入口并完成 03-rust-web 文档收口",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-025"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "ARCHITECTURE.md",
        "design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md",
        "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
        "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
        "scripts/task117-next-retirement-guard.js"
      ],
      "acceptance": [
        "task117 证明 SKIP_NEXT_LEGACY=1 时核心 smoke 仍可通过",
        "默认启动链不再把 pnpm dev 作为 3000 owner，3104 不恢复为公开入口",
        "ARCHITECTURE.md 与 3-1 同步 Next App Router legacy compat 口径，本 plan/checklist 移入 done"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote && node scripts/task097-homepage-entry-smoke.js && node scripts/task114-rust-web-gateway-entry-smoke.js && node scripts/task115-rust-web-document-shell-smoke.js && node scripts/task116-rust-web-islands-smoke.js && node scripts/task117-next-retirement-guard.js && git diff --check -- ARCHITECTURE.md design/03-rust-web scripts wolai-frontend rust/crates/mnote-web",
        "timeout_seconds": 4800
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "task117 已证明 SKIP_NEXT_LEGACY=1 时核心 shell 不依赖 Next legacy；desktop-hot 支持跳过 Next legacy；ARCHITECTURE/3-1 已同步 Next App Router legacy compat 口径，3-5/3-6 已移入 done。",
          "timestamp": "2026-04-28T12:50:07Z"
        }
      ],
      "completed_at": "2026-04-28T12:50:07Z"
    }
  ],
  "session_count": 10,
  "last_session": "2026-04-28T12:50:07Z",
  "final_renderer_gate": {
    "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",


"total": 1,
          "description": "task117 已证明 SKIP_NEXT_LEGACY=1 时核心 shell 不依赖 Next legacy；desktop-hot 支持跳过 Next legacy；ARCHITECTURE/3-1 已同步 Next App Router legacy compat 口径，3-5/3-6 已移入 done。",
          "timestamp": "2026-04-28T12:50:07Z"
        }
      ],
      "completed_at": "2026-04-28T12:50:07Z"
    }
  ],
  "session_count": 10,
  "last_session": "2026-04-28T12:50:07Z",
  "final_renderer_gate": {
    "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
    "status": "completed",
    "note": "task-001 至 task-009 只代表 Rust/WASM reducer runtime 阶段完成；不得再把 inline iframe srcdoc host 视为 final Rust DOM renderer。"
  },
  "final_runtime_remaining": {
    "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
    "status": "completed",
    "tasks": [
      "task-014",
      "task-015",
      "task-016",
      "task-017",
      "task-018"
    ]
  },
  "main_web_execution_plane": {
    "source_doc": "design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md",
    "plan_doc": "design/03-rust-web/process/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
    "status": "planned",
    "tasks": [
      "task-019",
      "task-020",
      "task-021",
      "task-022",
      "task-023",
      "task-024",
      "task-025",
      "task-026"
    ],
    "note": "树域 Rust family 默认执行面已完成；主 Web 执行面仍需通过 task-019 至 task-026 迁到 mnote-web / Rust family。"
  },
  "rust_web_3_4_undo": {
    "source_doc": "design/03-rust-web/process/3-4-undo.md",
    "status": "in_progress",
    "dependency_chain": "A -> B/C -> D/E/F -> G -> H",
    "tasks": [
      {
        "id": "task-3-4-A",
        "title": "Page Aggregate 升级为 core-protocol projection contract",
        "status": "in_progress",
        "depends_on": [],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol page_aggregate && cargo test -p bridge-runtime page_aggregate && cargo test -p mnote-web page_aggregate"
      },
      {
        "id": "task-3-4-B",
        "title": "page.* 成为页面默认写命令族",
        "status": "in_progress",
        "depends_on": ["task-3-4-A"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime page_body_save && cargo test -p bridge-runtime page_head_update_title && cargo test -p bridge-runtime page_layout_update_options && cargo test -p mnote-web documents_save"


"id": "task-3-4-A",
        "title": "Page Aggregate 升级为 core-protocol projection contract",
        "status": "in_progress",
        "depends_on": [],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol page_aggregate && cargo test -p bridge-runtime page_aggregate && cargo test -p mnote-web page_aggregate"
      },
      {
        "id": "task-3-4-B",
        "title": "page.* 成为页面默认写命令族",
        "status": "in_progress",
        "depends_on": ["task-3-4-A"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime page_body_save && cargo test -p bridge-runtime page_head_update_title && cargo test -p bridge-runtime page_layout_update_options && cargo test -p mnote-web documents_save"
      },
      {
        "id": "task-3-4-C",
        "title": "3000 Rust shell 消费 tree realtime event stream",
        "status": "in_progress",
        "depends_on": ["task-3-4-A"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web tree_events && cargo test -p mnote-web tree_command && cd /mnt/Data1T/mnote && MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js"
      },
      {
        "id": "task-3-4-D",
        "title": "Search 收口为 server-first / kernel-aware 主链",
        "status": "in_progress",
        "depends_on": ["task-3-4-B", "task-3-4-C"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol search && cargo test -p bridge-runtime search_documents_query && cargo test -p mnote-web search && cd /mnt/Data1T/mnote && MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task125-rust-web-search-server-first-smoke.js"
      },
      {
        "id": "task-3-4-E",
        "title": "AI bridge 与结构化写链收口",
        "status": "in_progress",
        "depends_on": ["task-3-4-B", "task-3-4-C"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol ai && cargo test -p mnote-web hermes && cd /mnt/Data1T/mnote && MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task126-rust-web-ai-bridge-structured-write-smoke.js"
      },
      {
        "id": "task-3-4-F",
        "title": "Mindmap 成为 kernel projection / command truth",
        "status": "in_progress",
        "depends_on": ["task-3-4-B", "task-3-4-C"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p core-protocol mindmap && cargo test -p bridge-runtime mindmap && cargo test -p mnote-web mindmap && cd /mnt/Data1T/mnote && MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task127-rust-web-mindmap-kernel-projection-smoke.js"
      },
      {
        "id": "task-3-4-G",
        "title": "Legacy Next / React 兼容层退役 gate",
        "status": "in_progress",
        "depends_on": ["task-3-4-D", "task-3-4-E", "task-3-4-F"],
        "validation": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web gateway && cargo test -p mnote-web legacy_next && cd /mnt/Data1T/mnote && MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task117-next-retirement-guard.js"
      },
      {
        "id": "task-3-4-H",
        "title": "设计文档状态治理与执行节奏",
        "status": "in_progress",
        "depends_on": ["task-3-4-G"],
        "validation": "cd /mnt/Data1T/mnote && rg -n \"\\\\[ \\\\]\" design/03-rust-web/process/3-4-undo.md && rg -n \"\\\\[done\\\\]|\\\\[recycle\\\\]\" design/03-rust-web design/old && git diff --check && git status --short --untracked-files=all"
      }
    ]
  }
}


{
  "name": "mnote-desktop",
  "version": "0.1.0",
  "private": true,
  "main": "desktop-electron/main.js",
  "scripts": {
    "desktop": "node scripts/desktop-prod.js",
    "desktop:hot": "node scripts/desktop-hot.js",
    "desktop:local": "node scripts/desktop-local.js",
    "desktop:electron": "node desktop-electron/dev-runner.js",
    "build:desktop:next": "node scripts/prepare-desktop-next.js",
    "dist:win": "pnpm run build:desktop:next && electron-builder --win nsis"
  },
  "dependencies": {},
  "devDependencies": {
    "@playwright/test": "^1.56.1",
    "electron": "^34.0.0",
    "electron-builder": "^26.0.12",
    "playwright": "^1.56.1"
  },
  "build": {
    "appId": "org.aichem.mnote",
    "productName": "MNOTE",
    "asar": true,
    "directories": {
      "output": "dist",
      "buildResources": "desktop-electron/build"
    },
    "files": [
      "package.json",
      "desktop-electron/**",
      "!desktop-electron/desktop-next{,/**}"
    ],
    "extraResources": [
      {
        "from": "desktop-electron/desktop-next",
        "to": "desktop-next",
        "filter": [
          "**/*",
          "!node_modules{,/**}"
        ]
      },
      {
        "from": "desktop-electron/desktop-next/node_modules",
        "to": "desktop-next/node_modules"
      }
    ],
    "win": {
      "target": [
        "nsis"
      ]
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true,
      "include": "desktop-electron/build/installer.nsh"
    }
  },
  "pnpm": {
    "onlyBuiltDependencies": [


"target": [
        "nsis"
      ]
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true,
      "include": "desktop-electron/build/installer.nsh"
    }
  },
  "pnpm": {
    "onlyBuiltDependencies": [
      "electron",
      "electron-winstaller"
    ]
  }
}


# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

#test
**/test/
pw-tests/**
pw-tests/
wolai-frontend/public/documents/**


F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.


F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.


Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis


    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed


at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\node_modules__pnpm_ecee5139._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: Route "/documents/[id]" used `params.id`. `params` is a Promise and must be unwrapped with `await` or `React.use()` before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis
    at DocumentPage (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__d0423852._.js:120:150)


Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\2536e_@blocknote_react_dist_blocknote-react_65a5c21f.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\node_modules\.pnpm\next@16.0.3_@babel+core@7.2_a6e7fe7b2107bfd5c9e45031e322c5c3\node_modules\next\dist\compiled\next-server\app-page-turbo.runtime.dev.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__c5e4d87b._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
 ⨯ ReferenceError: window is not defined
    at <unknown> (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\2536e_@blocknote_react_dist_blocknote-react_65a5c21f.js:421:9)
    at Se (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\2536e_@blocknote_react_dist_blocknote-react_65a5c21f.js:419:354)
    at BlockNoteEditor (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__c5e4d87b._.js:87:315) {
  digest: '2629247923'
}


> wolai-frontend@0.1.0 dev F:\SOFT\MNOTE\wolai-frontend
> next dev "--port" "3001"

   ▲ Next.js 16.0.3 (Turbopack)
   - Local:         http://localhost:3001
   - Network:       http://192.168.121.1:3001
   - Environments: .env.local

 ✓ Starting...
 ✓ Ready in 729ms
 GET /login 200 in 2.3s (compile: 2.1s, render: 265ms)
 GET / 200 in 613ms (compile: 365ms, render: 248ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 3.4s (compile: 2.9s, render: 473ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 442ms (compile: 26ms, render: 415ms)
 GET /documents/1f256e20-0ef4-4090-9bec-954fcd799a4c 200 in 245ms (compile: 25ms, render: 220ms)
 POST /api/documents/create 200 in 424ms (compile: 311ms, render: 113ms)
 GET /documents/880f0a7a-e894-465b-a64d-bea1afa3da48 200 in 62ms (compile: 27ms, render: 34ms)
 GET /documents/1f256e20-0ef4-4090-9bec-954fcd799a4c 200 in 68ms (compile: 22ms, render: 47ms)
 GET /documents/e8b15472-3665-425e-b945-0de6bc9a15fb 200 in 56ms (compile: 22ms, render: 35ms)
 GET /documents/880f0a7a-e894-465b-a64d-bea1afa3da48 200 in 56ms (compile: 22ms, render: 34ms)
 GET / 307 in 297ms (compile: 2ms, render: 295ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 222ms (compile: 23ms, render: 199ms)
 GET / 307 in 102ms (compile: 3ms, render: 99ms)
 GET /login 200 in 53ms (compile: 3ms, render: 49ms)
 GET / 200 in 20ms (compile: 3ms, render: 17ms)
 GET /login 200 in 35ms (compile: 4ms, render: 31ms)
 GET /login 200 in 18ms (compile: 2ms, render: 15ms)
 GET / 200 in 20ms (compile: 3ms, render: 17ms)
 GET /login 200 in 34ms (compile: 3ms, render: 31ms)
 GET /login 200 in 18ms (compile: 2ms, render: 16ms)
 GET / 307 in 108ms (compile: 3ms, render: 105ms)
 GET /login 200 in 57ms (compile: 5ms, render: 52ms)
 GET / 307 in 193ms (compile: 3ms, render: 190ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 125ms (compile: 23ms, render: 102ms)
 GET / 200 in 27ms (compile: 4ms, render: 23ms)
 GET /login 200 in 34ms (compile: 3ms, render: 31ms)
 GET /login 200 in 19ms (compile: 3ms, render: 16ms)
 GET / 200 in 19ms (compile: 3ms, render: 16ms)
 GET /login 200 in 39ms (compile: 3ms, render: 36ms)
 GET /login 200 in 19ms (compile: 3ms, render: 16ms)
 GET / 200 in 20ms (compile: 3ms, render: 18ms)
 GET /login 200 in 40ms (compile: 9ms, render: 31ms)
 GET /login 200 in 19ms (compile: 2ms, render: 17ms)
 GET / 200 in 27ms (compile: 5ms, render: 22ms)
 GET /login 200 in 41ms (compile: 3ms, render: 37ms)
 GET /login 200 in 21ms (compile: 2ms, render: 18ms)
 GET / 200 in 23ms (compile: 3ms, render: 20ms)
 GET /login 200 in 40ms (compile: 3ms, render: 37ms)
 GET /login 200 in 22ms (compile: 3ms, render: 19ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 307 in 181ms (compile: 29ms, render: 152ms)
 GET /login 200 in 60ms (compile: 3ms, render: 57ms)
 ✓ Compiled in 90ms
 GET /login 200 in 235ms (compile: 93ms, render: 142ms)
 GET /login 200 in 42ms (compile: 3ms, render: 39ms)
 GET / 200 in 175ms (compile: 93ms, render: 82ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 313ms (compile: 125ms, render: 188ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 67ms (compile: 4ms, render: 64ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 235ms (compile: 118ms, render: 117ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 611ms (compile: 24ms, render: 587ms)


GET /login 200 in 40ms (compile: 3ms, render: 37ms)
 GET /login 200 in 22ms (compile: 3ms, render: 19ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 307 in 181ms (compile: 29ms, render: 152ms)
 GET /login 200 in 60ms (compile: 3ms, render: 57ms)
 ✓ Compiled in 90ms
 GET /login 200 in 235ms (compile: 93ms, render: 142ms)
 GET /login 200 in 42ms (compile: 3ms, render: 39ms)
 GET / 200 in 175ms (compile: 93ms, render: 82ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 313ms (compile: 125ms, render: 188ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 67ms (compile: 4ms, render: 64ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 235ms (compile: 118ms, render: 117ms)
 GET /documents/2b01d6b7-dc39-49b9-97bb-0be59956e6fb 200 in 611ms (compile: 24ms, render: 587ms)


import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
  ...nextVitals,
  ...nextTs,
  {
    // 说明：当前仓库存在大量历史代码与第三方/生成代码。
    // 为避免 ESLint 在生产构建阶段被“风格型规则”阻塞，这里将部分规则从 error 降级为 warning。
    name: "mnote-lint-overrides",
    rules: {
      "@typescript-eslint/no-this-alias": "warn",
      "@typescript-eslint/no-explicit-any": "warn",
      "@typescript-eslint/no-require-imports": "warn",
      "react-hooks/set-state-in-effect": "warn",
    },
  },
  // Override default ignores of eslint-config-next.
  globalIgnores([
    // Default ignores of eslint-config-next:
    ".next/**",
    "out/**",
    "build/**",
    "next-env.d.ts",
    // 说明：静态资源/第三方构建产物不参与 lint（否则会产生大量无意义报错）。
    "public/luckysheet/**",
    // 说明：Convex 生成代码不参与 lint。
    "convex/_generated/**",
  ]),
]);

export default eslintConfig;


> wolai-frontend@0.1.0 dev F:\SOFT\MNOTE\wolai-frontend
> next dev "--port" "3001"

   ▲ Next.js 16.0.3 (Turbopack)
   - Local:         http://localhost:3001
   - Network:       http://192.168.121.1:3001
   - Environments: .env.local

 ✓ Starting...
 ✓ Ready in 876ms


⨯ Failed to start server
F:\SOFT\MNOTE\wolai-frontend\node_modules\.pnpm\next@16.0.3_@babel+core@7.2_a6e7fe7b2107bfd5c9e45031e322c5c3\node_modules\next\dist\server\lib\start-server.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\node_modules\.pnpm\next@16.0.3_@babel+core@7.2_a6e7fe7b2107bfd5c9e45031e322c5c3\node_modules\next\dist\trace\trace.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
Error: listen EADDRINUSE: address already in use :::3000
    at <unknown> (Error: listen EADDRINUSE: address already in use :::3000)
    at new Promise (<anonymous>) {
  code: 'EADDRINUSE',
  errno: -4091,
  syscall: 'listen',
  address: '::',
  port: 3000
}


> wolai-frontend@0.1.0 dev F:\SOFT\MNOTE\wolai-frontend
> next dev "--port" "3000"

 ELIFECYCLE  Command failed with exit code 1.


    at async (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\012f5__pnpm_791fff69._.js:12357:24)
    at async (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\012f5__pnpm_791fff69._.js:8251:36) {
  __isAuthError: true,
  status: 400,
  code: 'refresh_token_not_found'
}
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
 ⨯ Error: 创建默认工作空间失败：new row violates row-level security policy for table "workspaces"
    at ensureDefaultWorkspace (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js:172:15)
    at async Home (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js:126:5) {
  digest: '2664484505'
}
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.


status: 400,
  code: 'refresh_token_not_found'
}
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
 ⨯ Error: 创建默认工作空间失败：new row violates row-level security policy for table "workspaces"
    at ensureDefaultWorkspace (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js:172:15)
    at async Home (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js:126:5) {
  digest: '2664484505'
}
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js: Invalid source map. Only conformant source maps can be used to find the original code. Cause: Error: sourceMapURL could not be parsed
 ⨯ Error: 创建默认工作空间失败：new row violates row-level security policy for table "workspaces"
    at ensureDefaultWorkspace (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\wolai-frontend_9f844c81._.js:172:15)
    at async Home (F:\SOFT\MNOTE\wolai-frontend\.next\dev\server\chunks\ssr\[root-of-the-server]__97b7743c._.js:126:5) {
  digest: '2664484505'
}
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.


Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.
Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.

<--- Last few GCs --->

[30704:000001C2AAECC000]  3154411 ms: Mark-Compact 31843.8 (32807.5) -> 31742.3 (32803.9) MB, pooled: 0 MB, 10826.74 / 19.88 ms  (average mu = 0.281, current mu = 0.289) task; scavenge might not succeed
[30704:000001C2AAECC000]  3167016 ms: Mark-Compact 31812.8 (32808.6) -> 31722.8 (32794.1) MB, pooled: 8 MB, 11662.27 / 4.09 ms  (average mu = 0.187, current mu = 0.075) task; scavenge might not succeed

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
----- Native stack trace -----

 1: 00007FF6DC3D7F8F node::OnFatalError+1343
 2: 00007FF6DD01B1E7 v8::Function::NewInstance+423
 3: 00007FF6DCE1BA97 v8::base::AddressSpaceReservation::AddressSpaceReservation+322071
 4: 00007FF6DCE1F7A4 v8::base::AddressSpaceReservation::AddressSpaceReservation+337700
 5: 00007FF6DCE2E73C v8::internal::StrongRootAllocatorBase::deallocate_impl+16604
 6: 00007FF6DCE2DF7B v8::internal::StrongRootAllocatorBase::deallocate_impl+14619
 7: 00007FF6DE295D2D v8::base::UnsignedDivisionByConstant<unsigned __int64>+2791341
 8: 00007FF6DCE19560 v8::base::AddressSpaceReservation::AddressSpaceReservation+312544
 9: 00007FF6DCDC9F12 EVP_PKEY_asn1_set_get_priv_key+86258
10: 00007FF6DC32A48A node::GetNodeReport+98346
11: 00007FF6DC328C70 node::GetNodeReport+92176
12: 00007FF6DD082BCB uv_run+1867
13: 00007FF6DD08273F uv_run+703
14: 00007FF6DC4E4E3F node::DecodeWrite+367
15: 00007FF6DC3659B2 node::MultiIsolatePlatform::DisposeIsolate+240642
16: 00007FF6DC430F4C node::Start+1052
17: 00007FF6DD449A82 AES_cbc_encrypt+2546
18: 00007FF6DE29F434 v8::base::UnsignedDivisionByConstant<unsigned __int64>+2830004
19: 00007FFEDDDAE8D7 BaseThreadInitThunk+23
20: 00007FFEDFAAC53C RtlUserThreadStart+44


> wolai-frontend@0.1.0 dev F:\SOFT\MNOTE\wolai-frontend
> next dev

   ▲ Next.js 16.0.3 (Turbopack)
   - Local:         http://localhost:3000
   - Network:       http://192.168.121.1:3000
   - Environments: .env.local

 ✓ Starting...
 ✓ Ready in 983ms
 ○ Compiling /documents/[id] ...
 GET /login 200 in 2.4s (compile: 1432ms, render: 975ms)
 POST /api/auth/callback 200 in 460ms (compile: 439ms, render: 21ms)
 GET /login 200 in 632ms (compile: 31ms, render: 601ms)
 GET /documents/fe527e35-08c8-45db-8cc5-4e040b40973e 307 in 6.2s (compile: 5.1s, render: 1160ms)
 POST /api/auth/callback 200 in 42ms (compile: 29ms, render: 13ms)
 GET /login 200 in 639ms (compile: 35ms, render: 603ms)
 POST /api/auth/callback 200 in 48ms (compile: 22ms, render: 26ms)
 POST /api/auth/callback 200 in 146ms (compile: 45ms, render: 101ms)
 POST /api/auth/callback 200 in 200ms (compile: 49ms, render: 151ms)
 GET / 200 in 1154ms (compile: 432ms, render: 723ms)
 GET /login 200 in 699ms (compile: 29ms, render: 670ms)
 POST /api/auth/callback 200 in 55ms (compile: 44ms, render: 12ms)
 POST /api/auth/callback 200 in 98ms (compile: 47ms, render: 50ms)
 POST /api/auth/callback 200 in 132ms (compile: 36ms, render: 96ms)
 POST /api/auth/callback 200 in 133ms (compile: 40ms, render: 93ms)
 POST /api/auth/callback 200 in 141ms (compile: 39ms, render: 102ms)
 POST /api/auth/callback 200 in 188ms (compile: 44ms, render: 144ms)
 GET / 200 in 788ms (compile: 52ms, render: 736ms)
 GET /login 200 in 711ms (compile: 20ms, render: 691ms)
 POST /api/auth/callback 200 in 57ms (compile: 37ms, render: 21ms)
 POST /api/auth/callback 200 in 59ms (compile: 41ms, render: 18ms)
 POST /api/auth/callback 200 in 130ms (compile: 22ms, render: 108ms)
 POST /api/auth/callback 200 in 132ms (compile: 33ms, render: 99ms)
 POST /api/auth/callback 200 in 136ms (compile: 34ms, render: 102ms)
 POST /api/auth/callback 200 in 171ms (compile: 37ms, render: 134ms)
 GET / 200 in 900ms (compile: 44ms, render: 856ms)
 GET /documents/6fd575a8-1ca5-41f8-9366-2d8c400efd02 200 in 1754ms (compile: 65ms, render: 1689ms)
 GET /documents/6fd575a8-1ca5-41f8-9366-2d8c400efd02 200 in 1323ms (compile: 61ms, render: 1262ms)
 GET /api/references/backlinks?workspaceId=637d70cb-fbe1-4dc3-9f2c-ff8e099b1225&pageId=6fd575a8-1ca5-41f8-9366-2d8c400efd02 200 in 252ms (compile: 156ms, render: 96ms)
 POST /api/documents/save 200 in 174ms (compile: 134ms, render: 40ms)
 POST /api/documents/stats 200 in 192ms (compile: 156ms, render: 37ms)

 ELIFECYCLE  Command failed with exit code 134.


import type { NextConfig } from "next";
import fs from "fs";
import path from "path";

function loadEnvAll() {
  // 约定：全局仅使用仓库根目录的 .env.all 作为配置来源（生产优先）。
  // 说明：这里仅“补齐缺失项”，避免覆盖系统环境变量（方便部署/容器注入）。
  const envAllPath = path.resolve(__dirname, "..", ".env.all");
  if (!fs.existsSync(envAllPath)) return;
  const raw = fs.readFileSync(envAllPath, { encoding: "utf8" });
  for (const line of raw.split(/\r?\n/)) {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith("#")) continue;
    const idx = trimmed.indexOf("=");
    if (idx === -1) continue;
    const key = trimmed.slice(0, idx).trim();
    const value = trimmed.slice(idx + 1).trim();
    if (!key) continue;
    if (process.env[key] === undefined) {
      process.env[key] = value;
    }
  }
}

loadEnvAll();

const nextConfig: NextConfig = {
  // 说明：3000 Rust 网关会把 /_next/* dev 资源代理到 3100，浏览器 Origin 仍是 127.0.0.1:3000。
  // Next dev 默认会拦截这类跨 origin HMR 请求，导致客户端 hydration 不执行。
  allowedDevOrigins: ["127.0.0.1", "localhost"],
  // 供桌面端打包使用（Electron 内置 Next server.js + 最小依赖）。
  // 说明：`pnpm run build:desktop:next` 会依赖该产物。
  output: "standalone",
  turbopack: {
    // 避免 monorepo/多 lockfile 场景下 root 误判，减少构建与热更新的不确定性
    root: __dirname,
  },
  async headers() {
    // 说明：ONLYOFFICE 文档服务器会在其 iframe 内跨域拉取插件 manifest/js/html。
    // 这里对插件资源放开 CORS，避免 pluginsData 加载失败。
    return [
      // 说明：远程通过 Cloudflare Tunnel 访问时，Next.js（Turbopack dev）默认对
      // `/_next/static/chunks/*` 返回 `no-store`，导致 Cloudflare 无法缓存大体积
      // bundle，网络稍慢时会出现页面长期停留在“正在载入编辑器...”的现象。
      // 这些资源带 hash 文件名，设置 immutable 不会导致更新不一致。
      {
        source: "/_next/static/chunks/:path*",
        headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }],
      },
      {
        source: "/_next/static/media/:path*",
        headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }],
      },
      // Luckysheet 静态资源：文件位于 public/luckysheet，路径稳定（并且我们在 URL 上追加了版本参数），可长期缓存。
      {
        source: "/luckysheet/:path*",
        headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }],
      },
      {
        source: "/onlyoffice/plugins/:path*",


},
      {
        source: "/_next/static/media/:path*",
        headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }],
      },
      // Luckysheet 静态资源：文件位于 public/luckysheet，路径稳定（并且我们在 URL 上追加了版本参数），可长期缓存。
      {
        source: "/luckysheet/:path*",
        headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }],
      },
      {
        source: "/onlyoffice/plugins/:path*",
        headers: [
          { key: "Access-Control-Allow-Origin", value: "*" },
          { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS" },
          { key: "Access-Control-Allow-Headers", value: "*" },
        ],
      },
    ];
  },
};

export default nextConfig;


{
  "name": "wolai-frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "node scripts/dev-server.js",
    "build": "next build",
    "start": "node scripts/prod-server.js",
    "start:next": "next start",
    "lint": "eslint",
    "test": "vitest"
  },
  "dependencies": {
    "@auth/core": "0.37.0",
    "@blocknote/core": "^0.42.0",
    "@blocknote/mantine": "0.42.0",
    "@blocknote/react": "^0.42.0",
    "@convex-dev/auth": "^0.0.91",
    "@dnd-kit/core": "^6.3.1",
    "@dnd-kit/sortable": "^10.0.0",
    "@dnd-kit/utilities": "^3.2.2",
    "@hello-pangea/dnd": "^18.0.1",
    "@hocuspocus/provider": "^2.15.3",
    "@radix-ui/react-avatar": "^1.1.11",
    "@radix-ui/react-dialog": "^1.1.15",
    "@radix-ui/react-dropdown-menu": "^2.1.16",
    "@radix-ui/react-hover-card": "^1.1.15",
    "@radix-ui/react-label": "^2.1.8",
    "@radix-ui/react-popover": "^1.1.15",
    "@radix-ui/react-scroll-area": "^1.2.10",
    "@radix-ui/react-separator": "^1.1.8",
    "@radix-ui/react-slot": "^1.2.4",
    "@radix-ui/react-tabs": "^1.1.13",
    "@radix-ui/react-toggle": "^1.1.10",
    "@supabase/auth-helpers-nextjs": "^0.10.0",
    "@supabase/auth-helpers-react": "^0.5.0",
    "@supabase/supabase-js": "^2.81.1",
    "@svgdotjs/svg.js": "^3.2.5",
    "@tanstack/react-query": "^5.90.10",
    "@tanstack/react-query-devtools": "^5.90.2",
    "@tanstack/react-virtual": "^3.11.0",
    "@xyflow/react": "^12.9.3",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "cmdk": "^1.1.1",
    "convex": "^1.35.1",
    "jszip": "3.10.1",
    "lucide-react": "^0.554.0",
    "next": "16.2.4",
    "pdfjs-dist": "^4.10.38",
    "react": "19.2.0",
    "react-dom": "19.2.0",
    "react-dropzone": "^14.3.8",
    "react-icons": "5.5.0",
    "simple-mind-map": "0.14.0-fix.1",
    "simple-mind-map-plugin-themes": "^1.0.1",
    "sonner": "^2.0.7",
    "tailwind-merge": "^3.4.0",
    "uuid": "^13.0.0",
    "vaul": "^1.1.2",


const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;


> wolai-frontend@0.1.0 start F:\SOFT\MNOTE\wolai-frontend
> next start "--port" "4000"

   ▲ Next.js 16.0.3
   - Local:         http://localhost:4000
   - Network:       http://192.168.121.1:4000

 ✓ Starting...
 ✓ Ready in 785ms
 ELIFECYCLE  Command failed with exit code 4294967295.


import { defineConfig } from "vitest/config";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";

export default defineConfig({
  resolve: {
    alias: {
      "@": resolve(dirname(fileURLToPath(import.meta.url)), "./src"),
      "server-only": resolve(dirname(fileURLToPath(import.meta.url)), "./test/server-only.ts"),
    },
  },
  test: {
    environment: "jsdom",
    globals: true,
    include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
  },
});


import { convexAuthNextjsMiddleware, createRouteMatcher, nextjsMiddlewareRedirect } from "@convex-dev/auth/nextjs/server";
import { isDevAuthEnabled } from "@/lib/auth/devUser";

// 说明：
// - 这里启用 Convex Auth 的 Next.js 中间件，负责：
//   1) 代理 /api/auth 到 Convex（用于 signIn / signOut 等动作）
//   2) 刷新/同步 auth cookies
//   3) 对需要登录的页面做统一拦截
// - 默认将未登录用户重定向到 /auth（Convex Auth 登录页）。

const isPublicRoute = createRouteMatcher([
  "/auth",
  "/login",
  "/api/auth(.*)",
  // 说明：ONLYOFFICE 文档服务器（容器/远端）拉取文件与回调保存不携带用户态，必须放行。
  "/api/onlyoffice/proxy(.*)",
  "/api/onlyoffice/callback(.*)",
  // 说明：媒体附件 API 作为 Rust 3000 的 TS transport 壳保留；
  // 先放过 Next middleware，真实鉴权由各 route 内部 requireAuthContext 执行。
  "/api/media(.*)",
  // 说明：/onlyoffice-server 与 /cache 主要承载 ONLYOFFICE 静态资源与二进制缓存。
  // 这些资源不依赖用户态，且需要浏览器强缓存；若经过 Auth middleware 可能被追加 no-store，导致每次都重下几十 MB。
  "/onlyoffice-server(.*)",
  // 说明：本地自定义 ONLYOFFICE 插件页面运行在同源 /onlyoffice/plugins/* 下，由文档编辑器 iframe 直接加载。
  // 若被鉴权重定向到 /auth，会导致插件桥永远收不到 ready。
  "/onlyoffice/plugins(.*)",
  "/cache(.*)",
  "/api/health(.*)",
  "/_next(.*)",
  "/favicon.ico",
]);

export default convexAuthNextjsMiddleware(async (request, ctx) => {
  // 公共路由不做拦截（但 proxy 仍会处理 token 刷新/代理等）。
  if (isPublicRoute(request)) return;

  // 只在启用 Convex 模式时做鉴权拦截；否则走 Supabase 逻辑（页面内部自行处理）。
  // 注意：proxy 运行在 Edge/Node 环境中，读取到的是运行期环境变量。
  if (process.env.NEXT_PUBLIC_USE_CONVEX !== "1") return;

  // 开发用户模式下允许“免登录”访问（主要用于迁移/联调与 E2E 回归）。
  // 注意：生产环境请勿开启 MNOTE_DEV_AUTH。
  if (isDevAuthEnabled()) return;

  const authed = await ctx.convexAuth.isAuthenticated();
  if (!authed) {
    return nextjsMiddlewareRedirect(request, "/auth");
  }
});

export const config = {
  // 说明：排除静态资源，避免无意义的 proxy 开销。
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};


import type { DocumentStats } from "@/types/page-options";
import type { Json } from "@/types/supabase";

export interface DocumentSnapshot {
  id: string;
  timestamp: number;
  blocks: Json;
  stats: DocumentStats;
}


declare module "jsdom" {
  export class JSDOM {
    constructor(html?: string, options?: unknown);
    // 说明：仅用于服务端导入时的最小类型声明（用于 window/document/DOMParser 等 DOM shim）。
    // 若需更完整的类型能力，可改为使用 jsdom 自带的类型定义。
    window: Window & typeof globalThis;
  }
}


export interface MediaAsset {
  id: string;
  workspace_id: string;
  document_id: string;
  asset_type: string;
  file_url: string | null;
  thumbnail_url: string | null;
  storage_id?: string | null;
  bucket?: string | null;
  storage_path?: string | null;
  file_name: string | null;
  file_size: number | null;
  mime_type: string | null;
  ocr_payload?: unknown;
  ocr_strategy?: string | null;
  ocr_text: string | null;
  ocr_status: string | null;
  deleted_at?: string | null;
  deleted_by?: string | null;
  purged_at?: string | null;
  signed_url?: string | null;
  created_at: string;
  updated_at: string;
}

export interface MediaSelection {
  assetId: string;
  fileUrl: string;
  thumbnailUrl?: string | null;
  assetType?: string;
  fileName?: string | null;
  fileSize?: number | null;
  mimeType?: string | null;
}

export type MediaKind = "image" | "video" | "audio" | "file";


export type ColumnType =
  | "text"
  | "number"
  | "currency"
  | "date"
  | "select"
  | "checkbox"
  | "formula"
  | "reference";

export interface TableColumn {
  id: string; // Unique column ID
  name: string; // Column display name
  type: ColumnType;
  width: number; // Column width for display
  options?: { value: string; color: string }[]; // For 'select' type
}

export interface TableSchema {
  columns: TableColumn[];
  frozenRowCount: number; // 冻结行数
  frozenColCount: number; // 冻结列数
  // 其他视图偏好设置...
}

export type TableRowData = Record<string, string | number | boolean | null | undefined>;

export interface DocumentTableSnapshot {
  rows?: TableRowData[];
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  luckysheet?: any;
}

// 对应 Supabase document_tables 表的元数据
export interface DocumentTable {
  id: string;
  document_id: string;
  grid_key: string;
  title: string;
  schema: TableSchema;
  snapshot?: DocumentTableSnapshot | null;
  created_at: string;
  updated_at: string;
}

// Blocknote 块的数据结构
export interface OnlineTableBlockProps {
  tableId: string;
  title: string; // 允许 Blocknote 渲染时显示表格名称
}

// Global declarations for Luckysheet (minimal definition)
declare global {
  interface Window {
    luckysheet?: {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      create?: (options: any) => void;
      destroy?: (id: string) => void;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      getluckysheetfile?: () => any;


title: string; // 允许 Blocknote 渲染时显示表格名称
}

// Global declarations for Luckysheet (minimal definition)
declare global {
  interface Window {
    luckysheet?: {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      create?: (options: any) => void;
      destroy?: (id: string) => void;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      getluckysheetfile?: () => any;
      enterEditMode?: () => void;
      getluckysheet_select_save?: () => unknown;
      // 实际功能会通过动态加载的脚本注入
      // 最小化声明以通过 TypeScript 检查（必须允许 undefined）
    };
  }
}


export type PageFont = "default" | "song" | "kai";

export type PageLayoutDensity = "compact" | "normal" | "spacious";

export type BooleanPageOptionKey =
  | "wideLayout"
  | "smallText"
  | "showHeadingNumbers"
  | "showToc"
  | "protectEditing"
  | "showWordCount"
  | "collapseBacklinks"
  | "hideChildPages"
  | "showBlockRefCount";

export interface PageOptionsState {
  wideLayout: boolean;
  smallText: boolean;
  showHeadingNumbers: boolean;
  showToc: boolean;
  showStructure: boolean;
  protectEditing: boolean;
  showWordCount: boolean;
  collapseBacklinks: boolean;
  pageFont: PageFont;
  layoutDensity: PageLayoutDensity;
  hideChildPages: boolean;
  showBlockRefCount: boolean;
  embedDefaultBlockId: string | null;
}

export interface DocumentStats {
  wordCount: number;
  characterCount: number;
  blockCount: number;
  todoTotal: number;
  todoDone: number;
}


import type { ReferenceInsertMode } from "@/store/search-palette";

export interface BacklinkRecord {
  id: string;
  sourcePageId: string;
  sourceBlockId: string | null;
  alias: string | null;
  displayMode: ReferenceInsertMode | string;
  isPreviewable: boolean;
  createdAt: string;
  updatedAt: string;
  sourceTitle: string | null;
}


export type DocumentSearchMatchField = "title" | "content" | "recent";

export type DocumentSearchTimeRange = "any" | "7d" | "30d";
export type DocumentSearchTimeField = "updated" | "created";

export interface DocumentSearchFilters {
  titleOnly: boolean;
  exact: boolean;
  onlyCurrentPage: boolean;
  includeOcr: boolean;
  timeRange: DocumentSearchTimeRange;
  timeField: DocumentSearchTimeField;
  customRange?: {
    from?: string;
    to?: string;
  };
}

export interface DocumentSearchRequest {
  workspaceId: string;
  documentId?: string;
  query?: string;
  filters: DocumentSearchFilters;
  limit?: number;
}

export interface DocumentSearchResult {
  id: string;
  title: string | null;
  snippet: string;
  updatedAt: string | null;
  createdAt: string | null;
  matchField: DocumentSearchMatchField;
  hasOcr: boolean;
  publicPath: string;
  score: number;
  nodeId?: string | null;
  subtreeRootId?: string | null;
  projectionOwner?: "rust-kernel" | "compat-index";
  evidence?: Array<{
    kind: string;
    nodeId?: string | null;
    snippet: string;
  }>;
}

export interface DocumentSearchResponse {
  projectionOwner?: "rust-kernel" | "compat-index";
  results: DocumentSearchResult[];
  recent: DocumentSearchResult[];
}

export interface ReferenceTarget {
  id: string;
  title: string | null;
}


// 说明：
// `simple-mind-map` 的 `src/*` 属于内部实现路径，npm 包通常不提供 TypeScript 声明文件。
// 但在项目中我们需要按官方示例从 `src/plugins/*` 等路径引入插件，因此在此补充最小声明，
// 仅用于消除 TS7016（找不到声明文件）报错。
//
// 注意：这里的类型刻意保持宽松（unknown），避免把内部 API 当作稳定公共接口依赖。

declare module "simple-mind-map/src/svg/icons.js" {
  export const nodeIconList: unknown[];
}

declare module "simple-mind-map/src/utils/index.js" {
  export const mergerIconList: (icons: unknown[]) => unknown[];
  export const createUid: () => string;
}

declare module "simple-mind-map/src/plugins/Painter.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/AssociativeLine.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Search.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/OuterFrame.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Export.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Formula.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/RichText.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/MiniMap.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Select.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Drag.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/KeyboardNavigation.js" {
  const plugin: unknown;
  export default plugin;
}


declare module "simple-mind-map/src/plugins/Select.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Drag.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/KeyboardNavigation.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/NodeImgAdjust.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Scrollbar.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/RainbowLines.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Watermark.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/TouchEvent.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Cooperate.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/Demonstrate.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/MindMapLayoutPro.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/NodeBase64ImageStorage.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/ExportPDF.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/ExportXMind.js" {
  const plugin: unknown;
  export default plugin;
}

declare module "simple-mind-map/src/core/render/node/MindMapNode.js" {
  const MindMapNode: unknown;
  export default MindMapNode;


declare module "simple-mind-map/src/plugins/ExportPDF.js" {
  const plugin: unknown;
  export default plugin;
}
declare module "simple-mind-map/src/plugins/ExportXMind.js" {
  const plugin: unknown;
  export default plugin;
}

declare module "simple-mind-map/src/core/render/node/MindMapNode.js" {
  const MindMapNode: unknown;
  export default MindMapNode;
}

declare module "simple-mind-map/src/parse/xmind.js" {
  const xmind: {
    parseXmindFile: (
      file: Blob | ArrayBuffer | Uint8Array,
      handleMultiCanvas?: (content: unknown[]) => unknown,
    ) => Promise<unknown>;
  };
  export default xmind;
}

declare module "simple-mind-map/src/parse/markdownTo.js" {
  export type MarkdownToMindmapResult = {
    data?: Record<string, unknown>;
    children?: unknown[];
    [key: string]: unknown;
  };
  export const transformMarkdownTo: (md: string) => MarkdownToMindmapResult;
}


export type Json =
  | string
  | number
  | boolean
  | null
  | { [key: string]: Json | undefined }
  | Json[];

export type Database = {
  public: {
    Tables: {
      documents: {
        Row: {
          access_scope: "private" | "shared" | "public";
          content: Json | null;
          created_at: string | null;
          deleted_at: string | null;
          deleted_by: string | null;
          id: string;
          index_status: string | null;
          is_public: boolean | null;
          is_starred: boolean | null;
          is_template: boolean;
          mindmap_data: Json | null;
          parent_id: string | null;
          raw_text: string | null;
          sort_order: number;
          title: string | null;
          updated_at: string | null;
          user_id: string;
          workspace_id: string;
          wide_layout: boolean;
          use_small_text: boolean;
          show_heading_numbers: boolean;
          show_toc: boolean;
          show_structure: boolean;
          protect_editing: boolean;
          show_word_count: boolean;
          word_count: number;
          character_count: number;
          block_count: number;
        };
        Insert: {
          access_scope?: "private" | "shared" | "public";
          content?: Json | null;
          created_at?: string | null;
          deleted_at?: string | null;
          deleted_by?: string | null;
          id?: string;
          index_status?: string | null;
          is_public?: boolean | null;
          is_starred?: boolean | null;
          is_template?: boolean;
          mindmap_data?: Json | null;
          parent_id?: string | null;
          raw_text?: string | null;
          sort_order?: number;
          title?: string | null;
          updated_at?: string | null;
          user_id: string;


id?: string;
          index_status?: string | null;
          is_public?: boolean | null;
          is_starred?: boolean | null;
          is_template?: boolean;
          mindmap_data?: Json | null;
          parent_id?: string | null;
          raw_text?: string | null;
          sort_order?: number;
          title?: string | null;
          updated_at?: string | null;
          user_id: string;
          workspace_id: string;
          wide_layout?: boolean;
          use_small_text?: boolean;
          show_heading_numbers?: boolean;
          show_toc?: boolean;
          show_structure?: boolean;
          protect_editing?: boolean;
          show_word_count?: boolean;
          word_count?: number;
          character_count?: number;
          block_count?: number;
        };
        Update: {
          access_scope?: "private" | "shared" | "public";
          content?: Json | null;
          created_at?: string | null;
          deleted_at?: string | null;
          deleted_by?: string | null;
          id?: string;
          index_status?: string | null;
          is_public?: boolean | null;
          is_starred?: boolean | null;
          is_template?: boolean;
          mindmap_data?: Json | null;
          parent_id?: string | null;
          raw_text?: string | null;
          sort_order?: number;
          title?: string | null;
          updated_at?: string | null;
          user_id?: string;
          workspace_id?: string;
          wide_layout?: boolean;
          use_small_text?: boolean;
          show_heading_numbers?: boolean;
          show_toc?: boolean;
          show_structure?: boolean;
          protect_editing?: boolean;
          show_word_count?: boolean;
          word_count?: number;
          character_count?: number;
          block_count?: number;
        };
        Relationships: [
          {
            foreignKeyName: "documents_parent_id_fkey";
            columns: ["parent_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];


protect_editing?: boolean;
          show_word_count?: boolean;
          word_count?: number;
          character_count?: number;
          block_count?: number;
        };
        Relationships: [
          {
            foreignKeyName: "documents_parent_id_fkey";
            columns: ["parent_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "documents_workspace_id_fkey";
            columns: ["workspace_id"];
            referencedRelation: "workspaces";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "documents_deleted_by_fkey";
            columns: ["deleted_by"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
        ];
      };
      document_embeddings: {
        Row: {
          content_text: string;
          document_id: string | null;
          embedding: string | null;
          id: string;
        };
        Insert: {
          content_text: string;
          document_id?: string | null;
          embedding?: string | null;
          id?: string;
        };
        Update: {
          content_text?: string;
          document_id?: string | null;
          embedding?: string | null;
          id?: string;
        };
        Relationships: [
          {
            foreignKeyName: "document_embeddings_document_id_fkey";
            columns: ["document_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
        ];
      };
      entities: {
        Row: {
          document_ids: string[] | null;
          id: string;
          name: string;


foreignKeyName: "document_embeddings_document_id_fkey";
            columns: ["document_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
        ];
      };
      entities: {
        Row: {
          document_ids: string[] | null;
          id: string;
          name: string;
          properties: Json | null;
          type: string | null;
          user_id: string;
        };
        Insert: {
          document_ids?: string[] | null;
          id?: string;
          name: string;
          properties?: Json | null;
          type?: string | null;
          user_id: string;
        };
        Update: {
          document_ids?: string[] | null;
          id?: string;
          name?: string;
          properties?: Json | null;
          type?: string | null;
          user_id?: string;
        };
        Relationships: [];
      };
      relationships: {
        Row: {
          document_ids: string[] | null;
          from_entity: string | null;
          id: string;
          to_entity: string | null;
          type: string | null;
        };
        Insert: {
          document_ids?: string[] | null;
          from_entity?: string | null;
          id?: string;
          to_entity?: string | null;
          type?: string | null;
        };
        Update: {
          document_ids?: string[] | null;
          from_entity?: string | null;
          id?: string;
          to_entity?: string | null;
          type?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "relationships_from_entity_fkey";
            columns: ["from_entity"];


};
        Update: {
          document_ids?: string[] | null;
          from_entity?: string | null;
          id?: string;
          to_entity?: string | null;
          type?: string | null;
        };
        Relationships: [
          {
            foreignKeyName: "relationships_from_entity_fkey";
            columns: ["from_entity"];
            referencedRelation: "entities";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "relationships_to_entity_fkey";
            columns: ["to_entity"];
            referencedRelation: "entities";
            referencedColumns: ["id"];
          },
        ];
      };
      page_refs: {
        Row: {
          id: string;
          workspace_id: string;
          source_page_id: string;
          source_block_id: string | null;
          target_page_id: string;
          alias: string | null;
          display_mode: string;
          is_previewable: boolean;
          created_by: string;
          created_at: string;
          updated_at: string;
        };
        Insert: {
          id?: string;
          workspace_id: string;
          source_page_id: string;
          source_block_id?: string | null;
          target_page_id: string;
          alias?: string | null;
          display_mode?: string;
          is_previewable?: boolean;
          created_by: string;
          created_at?: string;
          updated_at?: string;
        };
        Update: {
          id?: string;
          workspace_id?: string;
          source_page_id?: string;
          source_block_id?: string | null;
          target_page_id?: string;
          alias?: string | null;
          display_mode?: string;
          is_previewable?: boolean;
          created_by?: string;


updated_at?: string;
        };
        Update: {
          id?: string;
          workspace_id?: string;
          source_page_id?: string;
          source_block_id?: string | null;
          target_page_id?: string;
          alias?: string | null;
          display_mode?: string;
          is_previewable?: boolean;
          created_by?: string;
          created_at?: string;
          updated_at?: string;
        };
        Relationships: [
          {
            foreignKeyName: "page_refs_workspace_id_fkey";
            columns: ["workspace_id"];
            referencedRelation: "workspaces";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "page_refs_source_page_id_fkey";
            columns: ["source_page_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "page_refs_target_page_id_fkey";
            columns: ["target_page_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "page_refs_created_by_fkey";
            columns: ["created_by"];
          referencedRelation: "profiles";
          referencedColumns: ["id"];
        },
      ];
      };
      media_assets: {
        Row: {
          id: string;
          workspace_id: string;
          document_id: string;
          asset_type: string;
          file_url: string | null;
          thumbnail_url: string | null;
          bucket: string | null;
          storage_path: string | null;
          file_name: string | null;
          file_size: number | null;
          mime_type: string | null;
          ocr_payload: Json | null;
          ocr_strategy: string | null;
          ocr_text: string | null;
          ocr_status: string | null;
          deleted_at: string | null;


file_url: string | null;
          thumbnail_url: string | null;
          bucket: string | null;
          storage_path: string | null;
          file_name: string | null;
          file_size: number | null;
          mime_type: string | null;
          ocr_payload: Json | null;
          ocr_strategy: string | null;
          ocr_text: string | null;
          ocr_status: string | null;
          deleted_at: string | null;
          deleted_by: string | null;
          purged_at: string | null;
          created_by: string | null;
          created_at: string;
          updated_at: string;
        };
        Insert: {
          id?: string;
          workspace_id: string;
          document_id: string;
          asset_type?: string;
          file_url?: string | null;
          thumbnail_url?: string | null;
          bucket?: string | null;
          storage_path?: string | null;
          file_name?: string | null;
          file_size?: number | null;
          mime_type?: string | null;
          ocr_payload?: Json | null;
          ocr_strategy?: string | null;
          ocr_text?: string | null;
          ocr_status?: string | null;
          deleted_at?: string | null;
          deleted_by?: string | null;
          purged_at?: string | null;
          created_by?: string | null;
          created_at?: string;
          updated_at?: string;
        };
        Update: {
          id?: string;
          workspace_id?: string;
          document_id?: string;
          asset_type?: string;
          file_url?: string | null;
          thumbnail_url?: string | null;
          bucket?: string | null;
          storage_path?: string | null;
          file_name?: string | null;
          file_size?: number | null;
          mime_type?: string | null;
          ocr_payload?: Json | null;
          ocr_strategy?: string | null;
          ocr_text?: string | null;
          ocr_status?: string | null;
          deleted_at?: string | null;
          deleted_by?: string | null;
          purged_at?: string | null;


bucket?: string | null;
          storage_path?: string | null;
          file_name?: string | null;
          file_size?: number | null;
          mime_type?: string | null;
          ocr_payload?: Json | null;
          ocr_strategy?: string | null;
          ocr_text?: string | null;
          ocr_status?: string | null;
          deleted_at?: string | null;
          deleted_by?: string | null;
          purged_at?: string | null;
          created_by?: string | null;
          created_at?: string;
          updated_at?: string;
        };
        Relationships: [
          {
            foreignKeyName: "media_assets_workspace_id_fkey";
            columns: ["workspace_id"];
            referencedRelation: "workspaces";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "media_assets_document_id_fkey";
            columns: ["document_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "media_assets_created_by_fkey";
            columns: ["created_by"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "media_assets_deleted_by_fkey";
            columns: ["deleted_by"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
        ];
      };
      user_recent_pages: {
        Row: {
          id: string;
          user_id: string;
          workspace_id: string;
          document_id: string;
          last_accessed_at: string;
        };
        Insert: {
          id?: string;
          user_id: string;
          workspace_id: string;
          document_id: string;
          last_accessed_at?: string;
        };
        Update: {
          id?: string;


document_id: string;
          last_accessed_at: string;
        };
        Insert: {
          id?: string;
          user_id: string;
          workspace_id: string;
          document_id: string;
          last_accessed_at?: string;
        };
        Update: {
          id?: string;
          user_id?: string;
          workspace_id?: string;
          document_id?: string;
          last_accessed_at?: string;
        };
        Relationships: [
          {
            foreignKeyName: "user_recent_pages_document_id_fkey";
            columns: ["document_id"];
            referencedRelation: "documents";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "user_recent_pages_user_id_fkey";
            columns: ["user_id"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
          {
            foreignKeyName: "user_recent_pages_workspace_id_fkey";
            columns: ["workspace_id"];
            referencedRelation: "workspaces";
            referencedColumns: ["id"];
          },
        ];
      };
      [key: string]: any;
    };
    Views: {
      [_ in never]: never;
    };
    Functions: {
      get_documents_tree: {
        Args: {
          request_user_id: string;
        };
        Returns: {
          depth: number;
          id: string;
          parent_id: string | null;
          path: string[];
          sort_order: number;
          title: string | null;
        }[];
      };
      workspaces: {
        Row: {
          created_at: string;


Returns: {
          depth: number;
          id: string;
          parent_id: string | null;
          path: string[];
          sort_order: number;
          title: string | null;
        }[];
      };
      workspaces: {
        Row: {
          created_at: string;
          created_by: string;
          icon_url: string | null;
          id: string;
          name: string;
          type: "personal" | "team";
          updated_at: string;
        };
        Insert: {
          created_at?: string;
          created_by: string;
          icon_url?: string | null;
          id?: string;
          name: string;
          type?: "personal" | "team";
          updated_at?: string;
        };
        Update: {
          created_at?: string;
          created_by?: string;
          icon_url?: string | null;
          id?: string;
          name?: string;
          type?: "personal" | "team";
          updated_at?: string;
        };
        Relationships: [
          {
            foreignKeyName: "workspaces_created_by_fkey";
            columns: ["created_by"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
        ];
      };
      workspace_members: {
        Row: {
          created_at: string;
          id: string;
          is_default: boolean;
          role: string;
          user_id: string;
          workspace_id: string;
        };
        Insert: {
          created_at?: string;
          id?: string;
          is_default?: boolean;
          role?: string;


created_at: string;
          id: string;
          is_default: boolean;
          role: string;
          user_id: string;
          workspace_id: string;
        };
        Insert: {
          created_at?: string;
          id?: string;
          is_default?: boolean;
          role?: string;
          user_id: string;
          workspace_id: string;
        };
        Update: {
          created_at?: string;
          id?: string;
          is_default?: boolean;
          role?: string;
          user_id?: string;
          workspace_id?: string;
        };
        Relationships: [
          {
            foreignKeyName: "workspace_members_user_id_fkey";
            columns: ["user_id"];
            referencedRelation: "profiles";
            referencedColumns: ["id"];
          },
          {
          foreignKeyName: "workspace_members_workspace_id_fkey";
          columns: ["workspace_id"];
          referencedRelation: "workspaces";
          referencedColumns: ["id"];
        },
      ];
    };
      record_page_ref: {
        Args: {
          p_workspace_id: string;
          p_source_page_id: string;
          p_source_block_id?: string | null;
          p_target_page_id: string;
          p_alias?: string | null;
          p_display_mode?: string;
          p_is_previewable?: boolean;
          p_created_by?: string;
        };
        Returns: Database["public"]["Tables"]["page_refs"]["Row"];
      };
      list_backlinks: {
        Args: {
          p_workspace_id: string;
          p_target_page_id: string;
          p_limit?: number;
          p_offset?: number;
        };
        Returns: Array<{
          id: string;


};
        Returns: Database["public"]["Tables"]["page_refs"]["Row"];
      };
      list_backlinks: {
        Args: {
          p_workspace_id: string;
          p_target_page_id: string;
          p_limit?: number;
          p_offset?: number;
        };
        Returns: Array<{
          id: string;
          source_page_id: string;
          source_block_id: string | null;
          alias: string | null;
          display_mode: string;
          is_previewable: boolean;
          created_at: string;
          updated_at: string;
          source_title: string | null;
          workspace_id: string;
        }>;
      };
    };
    Enums: {
      [_ in never]: never;
    };
    CompositeTypes: {
      [_ in never]: never;
    };
  };
};