diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 921b18e..38bb35f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -6,6 +6,10 @@ - Keep `src/app.ts` as a tiny entrypoint and preserve single-file build output (`ink-app.html`). - Local git hooks are tracked under `.githooks/`; keep lint-plus-repomix pre-commit automation there and point setup docs at `git config core.hooksPath .githooks` instead of ad hoc hook creation snippets. - Regression coverage for refactor-sensitive flows lives in `cypress/e2e/workspace-actions.cy.js` and `tests/qunit/fs-api.test.js`. +- Workspace opening must call `showDirectoryPicker()` directly from the user action, guard overlapping picker requests, coalesce duplicate rescans, and hydrate Markdown metadata/tags through the bounded worker pool in `src/app/workspace-io.ts` rather than reading every file serially or more than once. +- Foreground note opens must cancel obsolete scan generations, and auto-refresh must remain idle-gated and low-frequency so background vault hydration cannot starve interactive file reads. +- Unit coverage for auto-refresh I/O gating lives in `tests/qunit/auto-refresh.test.js`; preserve the invariant that recent interaction and dirty state return before permission queries or rescans. Cypress coverage in `workspace-actions.cy.js` verifies foreground note opens cancel slow scan generations. +- Progressive large-vault loading is proposed in `openspec/changes/add-progressive-workspace-loading/`. Do not implement it before approval; its invariants are separate bounded directory/file queues, batched initial-open publication, atomic refreshes, and generation-token cancellation. - The app controller is in `src/app/app-controller.ts`, with UI wiring in `src/app/ui-events.ts` and workspace flows in `src/app/workspace-io.ts`. - Top menu visuals are authored directly in `ink.template.html`; when adding icons to dropdown rows, keep the text in a nested `.menu-label-text` span so dynamic updates like the Sort label do not remove the icon. - Keyboard shortcut precedence lives in `src/app/ui-events.ts`; editor-scoped handlers must not swallow longer chords like Cmd/Ctrl+Shift+S that are reserved for export. @@ -23,13 +27,16 @@ - Cogito Mode entrypoint must be a top-right menu bar button using a thinking-man glyphicon with accessible Cogito labeling. - Cogito runtime integration lives in `src/app/cogito.ts`; keep the prompt contract and JSON parsing helpers (`extractLastSentence`, `parseCogitoQuestionPayload`, `formatCogitoQuestionBlock`) stable and covered by QUnit tests. - Cypress end-to-end coverage for Cogito uses a test-only `globalThis.__INK_TEST_WEBLLM__` override; preserve that seam so the full panel/generate/insert flow remains deterministic under test. +- Cogito configures WebLLM with the official IndexedDB cache backend to avoid brittle Cache API `Cache.add()` failures. Recoverable cache/network errors get one model-specific cleanup/retry for both Lite and Deep, and Deep additionally falls back to Lite; preserve both explicit Cypress scenarios. - The WebMCP `create_note` tool is agent-facing only. Keep its form out of the always-visible sidebar UI and surface it as a modal only when the tool is being used. - The canonical implementation for the WebMCP note popup lives in `ink.template.html`, `src/styles.scss`, and `src/app/ui-events.ts`; rebuild `ink-app.html` after source edits. - For local debugging, the WebMCP popup can be opened manually with `Cmd/Ctrl+Alt+N` or automatically via `?debugWebMcpNote=1`; keep those hooks available without making the tool permanently visible. -- Document Linter runtime integration lives in `src/app/document-linter/document-linter.ts`. When extending or maintaining the Document Linter, make sure all associated DOM elements (e.g. `documentLinterPanel`, `documentLinterAnalyzeBtn`, etc.) are mapped in `src/app/dom.ts` and defined in `src/app/types.ts` to avoid bootstrapping reference errors. +- Document Linter runtime integration lives in `src/app/document-linter/document-linter.ts`. Its controls and results render inside `#cogitoPanel`; keep associated DOM elements (e.g. `documentLinterAnalyzeBtn`) mapped in `src/app/dom.ts` and defined in `src/app/types.ts` to avoid bootstrapping reference errors. - The Document Linter now emits a markdown-aware review summary with prioritized fixes and section notes. Preserve the `analyzeDocumentText()` and `buildDocumentLinterReport()` contract when adjusting report output so the UI panel and export stay aligned. - Document Linter copy now lives in `src/app/document-linter/messages.ts`; keep suggestion/overview strings centralized there, preserve the `overallScore` field, and keep section-analysis `needsAttention` flags aligned between the panel and markdown export. - Document Linter panel behavior now includes a `Rerun on change` toggle plus clickable section line links back into the editor. Preserve `handleEditorChanged()` in `src/app/document-linter/document-linter.ts` and keep the panel's line-jump buttons wired to the textarea selection logic when changing the panel UI. -- Cogito and the Document Linter are mutually exclusive panels. Opening one should close the other so the editor split layout stays stable and never tries to render both sidebars together. +- Cogito is the single writing-assistance panel and contains both Document strength and Improve with Cogito. Do not restore a standalone Linter button or panel. +- The unified writing-assistance flow is tracked in `openspec/changes/integrate-document-linter-into-cogito/`. Keep deterministic analysis independent from WebLLM, pass only compact linter findings into coaching, and preserve Cogito's strict three-question JSON contract. +- Keep Document strength rerun-on-change debounced so analysis happens after typing pauses rather than on every keystroke, and keep Cogito prompt context bounded before invoking WebLLM. - Editor view mode lives in `src/app/editor-preview.ts` and is wired through the main editor header in `ink.template.html`. Keep the Source/Split/Preview toggle in sync with `editorSplit`, `editorPane`, and `previewPane`, and preserve the `ink-editor-view-mode` localStorage key when changing that flow. - Demo capture is recorded by `build/record-demo.js` using headless Chrome DevTools so Cypress UI is never present; use `npm run demo:record` for a slow, full-viewport app-only `assets/demo/ink-demo.webm` plus MP4 when conversion is available, and `npm run demo:gif` for the optional ffmpeg GIF. diff --git a/README.md b/README.md index 00ac6db..adaf0ea 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,15 @@ The webapp is released as a single HTML file (`ink-app.html`). Canonical build entrypoint: `build/compile-and-assemble.js` (used by `npm run build`). +## Cogito Writing Assistance + +Cogito is the single writing-assistance entrypoint in the top-right menu bar. Its side panel combines: + +- **Document strength**: deterministic analysis of clarity, structure, readability, engagement, and section-level priorities. Analysis can be run manually, rerun after edits, navigated by line, and exported as Markdown. +- **Improve with Cogito**: exactly three question-only coaching prompts grounded in the latest sentence. When document analysis is available, Cogito uses a compact summary of its priorities and strengths to focus those questions. + +Document analysis does not require the language model. Cogito's local WebLLM model is loaded only when coaching questions are generated, and generated questions are never inserted without an explicit user action. + ## Branding Assets - Canonical logo source: `assets/branding/logo.svg` diff --git a/build/build-test.js b/build/build-test.js index ee04495..6372ab1 100644 --- a/build/build-test.js +++ b/build/build-test.js @@ -14,6 +14,10 @@ const bundles = [ entryPoints: ["src/app/fs-api.ts"], outfile: "dist/test/fs-api.js", }, + { + entryPoints: ["src/app/auto-refresh.ts"], + outfile: "dist/test/auto-refresh.js", + }, { entryPoints: ["src/app/utils.ts"], outfile: "dist/test/utils.js", diff --git a/cypress/e2e/cogito-mode.cy.js b/cypress/e2e/cogito-mode.cy.js index 655c43c..3968423 100644 --- a/cypress/e2e/cogito-mode.cy.js +++ b/cypress/e2e/cogito-mode.cy.js @@ -97,7 +97,12 @@ describe("cogito mode", () => { win.__fakeWorkspace = root; win.__cogitoCreateEngineCalls = []; win.__cogitoCompletions = []; + win.__cogitoDeletedModels = []; win.__INK_TEST_WEBLLM__ = { + prebuiltAppConfig: { model_list: [] }, + async deleteModelAllInfoInCache(modelId) { + win.__cogitoDeletedModels.push(modelId); + }, async CreateMLCEngine(modelId, options = {}) { win.__cogitoCreateEngineCalls.push(modelId); options.initProgressCallback?.({ text: `Stub model ready: ${modelId}` }); @@ -132,7 +137,7 @@ describe("cogito mode", () => { }); }); - it("opens the panel, switches model, generates questions, and inserts one into the editor", () => { + it("assesses the document, uses findings for coaching, and inserts one question", () => { cy.get("[data-action=\"open-workspace\"]").click({ force: true }); cy.get("[data-action=\"new-note\"]").click({ force: true }); cy.get("#editor").clear().type(markdown); @@ -143,6 +148,20 @@ describe("cogito mode", () => { .should("have.attr", "aria-expanded", "true"); cy.get("#cogitoPanel").should("be.visible"); cy.get(".split").should("have.class", "with-cogito"); + cy.get("#documentLinterToggleBtn").should("not.exist"); + cy.get("#documentLinterPanel").should("not.exist"); + + cy.get("#editorViewSourceBtn").click(); + cy.get("#editorSplit").should("have.class", "view-source").and("have.class", "with-cogito"); + cy.get("#cogitoPanel").should("be.visible"); + cy.get("#editorViewPreviewBtn").click(); + cy.get("#editorSplit").should("have.class", "view-preview").and("have.class", "with-cogito"); + cy.get("#cogitoPanel").should("be.visible"); + cy.get("#editorViewSplitBtn").click(); + + cy.get("#documentLinterAnalyzeBtn").click(); + cy.get("#documentLinterStatus").should("contain", "Analysis complete"); + cy.get("#documentLinterResults").should("contain", "Overall"); cy.get("#cogitoDeepBtn").click().should("have.class", "active"); cy.get("#cogitoLiteBtn").should("not.have.class", "active"); @@ -160,10 +179,131 @@ describe("cogito mode", () => { expect(win.__cogitoCreateEngineCalls).to.deep.equal(["Qwen3-8B-q4f16_1-MLC"]); expect(win.__cogitoCompletions).to.have.length(1); expect(win.__cogitoCompletions[0].messages[1].content).to.contain(markdown); + expect(win.__cogitoCompletions[0].messages[1].content).to.contain("Document strength:"); + expect(win.__cogitoCompletions[0].messages[1].content).to.contain("Highest-priority improvements:"); }); cy.contains("#cogitoQuestionList .cogitoInsertBtn", "Insert").first().click(); cy.get("#statusBadge").should("contain", "Inserted AI question"); cy.get("#editor").should("have.value", `${markdown}> ### AI\nWhat problem does local-first editing solve here?\n`); }); + + it("marks existing questions stale when document analysis changes", () => { + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("[data-action=\"new-note\"]").click({ force: true }); + cy.get("#editor").clear().type(markdown); + cy.get("#cogitoToggleBtn").click(); + cy.get("#documentLinterAnalyzeBtn").click(); + cy.get("#documentLinterStatus").should("contain", "Analysis complete"); + cy.get("#cogitoGenerateBtn").click(); + cy.get("#cogitoQuestionList .cogitoQuestionItem").should("have.length", 3); + + cy.get("#editor").type(" More evidence is needed."); + cy.get("#documentLinterAnalyzeBtn").click(); + cy.get("#cogitoStatus").should("contain", "Regenerate"); + cy.get("#cogitoQuestionList .cogitoQuestionItem").should("have.length", 3); + }); + + it("repairs a failed cache and falls back from Deep to Lite", () => { + cy.window().then((win) => { + win.__INK_TEST_WEBLLM__.CreateMLCEngine = async (modelId, options = {}) => { + win.__cogitoCreateEngineCalls.push(modelId); + if (modelId === "Qwen3-8B-q4f16_1-MLC") { + throw new Error("Failed to execute 'add' on 'Cache': Cache.add() encountered a network error"); + } + options.initProgressCallback?.({ text: `Stub model ready: ${modelId}` }); + return { + chat: { + completions: { + async create(payload) { + win.__cogitoCompletions.push(payload); + return { + choices: [{ + message: { + content: JSON.stringify({ + questions: ["Fallback one?", "Fallback two?", "Fallback three?"], + }), + }, + }], + }; + }, + }, + }, + }; + }; + }); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("[data-action=\"new-note\"]").click({ force: true }); + cy.get("#editor").clear().type(markdown); + cy.get("#cogitoToggleBtn").click(); + cy.get("#cogitoDeepBtn").click(); + cy.get("#cogitoGenerateBtn").click(); + + cy.get("#statusBadge", { timeout: 10000 }).should("contain", "Cogito questions ready"); + cy.get("#cogitoLiteBtn").should("have.class", "active"); + cy.get("#cogitoQuestionList .cogitoQuestionItem").should("have.length", 3); + cy.window().then((win) => { + expect(win.__cogitoDeletedModels).to.deep.equal(["Qwen3-8B-q4f16_1-MLC"]); + expect(win.__cogitoCreateEngineCalls).to.deep.equal([ + "Qwen3-8B-q4f16_1-MLC", + "Qwen3-8B-q4f16_1-MLC", + "Llama-3.2-1B-Instruct-q4f32_1-MLC", + ]); + }); + }); + + it("repairs the Lite model cache and retries Lite successfully", () => { + cy.window().then((win) => { + let liteAttempts = 0; + win.__INK_TEST_WEBLLM__.CreateMLCEngine = async (modelId, options = {}) => { + win.__cogitoCreateEngineCalls.push(modelId); + if (modelId === "Llama-3.2-1B-Instruct-q4f32_1-MLC") { + liteAttempts += 1; + if (liteAttempts === 1) { + throw new Error("Failed to execute 'add' on 'Cache': Cache.add() encountered a network error"); + } + } + options.initProgressCallback?.({ text: `Stub model ready: ${modelId}` }); + return { + chat: { + completions: { + async create(payload) { + win.__cogitoCompletions.push(payload); + return { + choices: [{ + message: { + content: JSON.stringify({ + questions: ["Lite retry one?", "Lite retry two?", "Lite retry three?"], + }), + }, + }], + }; + }, + }, + }, + }; + }; + }); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("[data-action=\"new-note\"]").click({ force: true }); + cy.get("#editor").clear().type(markdown); + cy.get("#cogitoToggleBtn").click(); + cy.get("#cogitoLiteBtn").should("have.class", "active"); + cy.get("#cogitoGenerateBtn").click(); + + cy.get("#statusBadge", { timeout: 10000 }).should("contain", "Cogito questions ready"); + cy.get("#cogitoLiteBtn").should("have.class", "active"); + cy.get("#cogitoQuestionList .cogitoQuestionItem").should("have.length", 3); + cy.window().then((win) => { + expect(win.__cogitoDeletedModels).to.deep.equal([ + "Llama-3.2-1B-Instruct-q4f32_1-MLC", + ]); + expect(win.__cogitoCreateEngineCalls).to.deep.equal([ + "Llama-3.2-1B-Instruct-q4f32_1-MLC", + "Llama-3.2-1B-Instruct-q4f32_1-MLC", + ]); + }); + }); }); diff --git a/cypress/e2e/document-linter.cy.js b/cypress/e2e/document-linter.cy.js index bbe23ff..5c97759 100644 --- a/cypress/e2e/document-linter.cy.js +++ b/cypress/e2e/document-linter.cy.js @@ -109,7 +109,9 @@ describe("document linter export", () => { cy.get("[data-action=\"new-note\"]").click({ force: true }); cy.get("#editor").clear().type(noteContent); - cy.get("#documentLinterToggleBtn").click(); + cy.get("#documentLinterToggleBtn").should("not.exist"); + cy.get("#cogitoToggleBtn").click(); + cy.get("#cogitoPanel").should("be.visible"); cy.get("#documentLinterAnalyzeBtn").click(); cy.get("#statusBadge").should("contain", "Analysis complete"); diff --git a/cypress/e2e/workspace-actions.cy.js b/cypress/e2e/workspace-actions.cy.js index 5972f68..288cfb3 100644 --- a/cypress/e2e/workspace-actions.cy.js +++ b/cypress/e2e/workspace-actions.cy.js @@ -146,10 +146,111 @@ describe("workspace actions regression", () => { cy.get("#workspaceName").should("contain", workspaceName); cy.get("body").click("topLeft"); - cy.get("#refreshBtn").click(); + cy.get("body").click("bottomRight"); + cy.get("#refreshBtn").click({ force: true }); cy.get("#statusBadge").should("contain", "Refreshed"); }); + it("opens the folder picker immediately and ignores overlapping open requests", () => { + cy.window().then((win) => { + win.__pickerCalls = 0; + win.showDirectoryPicker = () => { + win.__pickerCalls += 1; + return new Promise((resolve) => { + win.__resolveWorkspacePicker = () => resolve(win.__fakeWorkspace); + }); + }; + }); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("#statusBadge").should("contain", "Choose a workspace folder"); + cy.window().its("__pickerCalls").should("eq", 1); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.window().its("__pickerCalls").should("eq", 1); + cy.get("#statusBadge").should("contain", "Folder picker already open"); + + cy.window().then((win) => { + win.__resolveWorkspacePicker(); + }); + cy.get("#statusBadge").should("contain", "Workspace ready"); + }); + + it("loads workspace files concurrently and reads each file once", () => { + cy.window().then((win) => { + const tracker = { active: 0, maximum: 0, calls: 0 }; + win.__workspaceReadTracker = tracker; + + for (let index = 0; index < 32; index += 1) { + const name = `note-${index}.md`; + win.__fakeWorkspace.__entries.set(name, { + kind: "file", + name, + async getFile() { + tracker.calls += 1; + tracker.active += 1; + tracker.maximum = Math.max(tracker.maximum, tracker.active); + await new Promise((resolve) => win.setTimeout(resolve, 20)); + tracker.active -= 1; + return new File([`---\ntags: [batch]\n---\n# Note ${index}`], name, { + type: "text/markdown", + lastModified: index, + }); + }, + }); + } + }); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("#statusBadge").should("contain", "Workspace ready"); + cy.get("#countsPill").should("contain", "32 notes"); + cy.window().then((win) => { + expect(win.__workspaceReadTracker.calls).to.eq(32); + expect(win.__workspaceReadTracker.maximum).to.be.greaterThan(1); + expect(win.__workspaceReadTracker.maximum).to.be.at.most(4); + }); + }); + + it("prioritizes opening a note over an in-progress background scan", () => { + cy.window().then((win) => { + win.__fakeWorkspace.__entries.set( + "priority.md", + createFakeFileHandle("priority.md", "# Priority\n\nOpen me immediately."), + ); + }); + + cy.get("[data-action=\"open-workspace\"]").click({ force: true }); + cy.get("#workspaceName").should("contain", workspaceName); + cy.get("#tree .node").contains("priority.md").should("be.visible"); + + cy.window().then((win) => { + const tracker = { calls: 0 }; + win.__slowRefreshTracker = tracker; + for (let index = 0; index < 40; index += 1) { + const name = `slow-${index}.md`; + win.__fakeWorkspace.__entries.set(name, { + kind: "file", + name, + async getFile() { + tracker.calls += 1; + await new Promise((resolve) => win.setTimeout(resolve, 250)); + return new File([`# Slow ${index}`], name, { type: "text/markdown" }); + }, + }); + } + }); + + cy.get("#refreshBtn").click({ force: true }); + cy.get("#tree .node").contains("priority.md").click(); + cy.get("#currentFilename").should("contain", "priority.md"); + cy.get("#editor").should("contain.value", "Open me immediately"); + + cy.wait(350); + cy.window().then((win) => { + expect(win.__slowRefreshTracker.calls).to.be.at.most(4); + }); + }); + it("close workspace resets UI state", () => { cy.get("[data-action=\"open-workspace\"]").click({ force: true }); cy.get("[data-action=\"new-note\"]").click({ force: true }); diff --git a/dist/app.min.js b/dist/app.min.js index cf85571..a0bb177 100644 --- a/dist/app.min.js +++ b/dist/app.min.js @@ -1,45 +1,45 @@ -(()=>{var Ln=Object.create;var Ee=Object.defineProperty;var Tn=Object.getOwnPropertyDescriptor;var Mn=Object.getOwnPropertyNames;var En=Object.getPrototypeOf,Rn=Object.prototype.hasOwnProperty;var Cn=(t,e,n)=>e in t?Ee(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var An=(t=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(t,{get:(e,n)=>(typeof require!="undefined"?require:e)[n]}):t)(function(t){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var $n=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Mn(e))!Rn.call(t,i)&&i!==n&&Ee(t,i,{get:()=>e[i],enumerable:!(r=Tn(e,i))||r.enumerable});return t};var Nn=(t,e,n)=>(n=t!=null?Ln(En(t)):{},$n(e||!t||!t.__esModule?Ee(n,"default",{value:t,enumerable:!0}):n,t));var $=(t,e,n)=>Cn(t,typeof e!="symbol"?e+"":e,n);function $e(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var ae=$e();function dt(t){ae=t}var oe={exec:()=>null};function C(t,e=""){let n=typeof t=="string"?t:t.source,r={replace:(i,o)=>{let a=typeof o=="string"?o:o.source;return a=a.replace(z.caret,"$1"),n=n.replace(i,a),r},getRegex:()=>new RegExp(n,e)};return r}var Pn=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:t=>new RegExp(`^( {0,3}${t})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}#`),htmlBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}<(?:[a-z].*>|!--)`,"i"),blockquoteBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}>`)},Dn=/^(?:[ \t]*(?:\n|$))+/,Bn=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,In=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,he=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Hn=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,Ne=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,pt=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,ht=C(pt).replace(/bull/g,Ne).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Fn=C(pt).replace(/bull/g,Ne).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Pe=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,zn=/^[^\n]+/,De=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,_n=C(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",De).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Wn=C(/^(bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,Ne).getRegex(),xe="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Be=/|$))/,On=C("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",Be).replace("tag",xe).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),gt=C(Pe).replace("hr",he).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",xe).getRegex(),qn=C(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",gt).getRegex(),Ie={blockquote:qn,code:Bn,def:_n,fences:In,heading:Hn,hr:he,html:On,lheading:ht,list:Wn,newline:Dn,paragraph:gt,table:oe,text:zn},ot=C("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",he).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",xe).getRegex(),jn={...Ie,lheading:Fn,table:ot,paragraph:C(Pe).replace("hr",he).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",ot).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",xe).getRegex()},Qn={...Ie,html:C(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",Be).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:oe,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:C(Pe).replace("hr",he).replace("heading",` *#{1,6} *[^ -]`).replace("lheading",ht).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Vn=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,Un=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,mt=/^( {2,}|\\)\n(?!\s*$)/,Gn=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",Pn?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),bt=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,tr=C(bt,"u").replace(/punct/g,ve).getRegex(),nr=C(bt,"u").replace(/punct/g,kt).getRegex(),yt="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",rr=C(yt,"gu").replace(/notPunctSpace/g,ft).replace(/punctSpace/g,He).replace(/punct/g,ve).getRegex(),ir=C(yt,"gu").replace(/notPunctSpace/g,Jn).replace(/punctSpace/g,Zn).replace(/punct/g,kt).getRegex(),or=C("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,ft).replace(/punctSpace/g,He).replace(/punct/g,ve).getRegex(),sr=C(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,wt).getRegex(),ar="^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)",lr=C(ar,"gu").replace(/notPunctSpace/g,Xn).replace(/punctSpace/g,Yn).replace(/punct/g,wt).getRegex(),cr=C(/\\(punct)/,"gu").replace(/punct/g,ve).getRegex(),ur=C(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),dr=C(Be).replace("(?:-->|$)","-->").getRegex(),pr=C("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",dr).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),we=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,hr=C(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label",we).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),xt=C(/^!?\[(label)\]\[(ref)\]/).replace("label",we).replace("ref",De).getRegex(),vt=C(/^!?\[(ref)\](?:\[\])?/).replace("ref",De).getRegex(),gr=C("reflink|nolink(?!\\()","g").replace("reflink",xt).replace("nolink",vt).getRegex(),st=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,Fe={_backpedal:oe,anyPunctuation:cr,autolink:ur,blockSkip:er,br:mt,code:Un,del:oe,delLDelim:oe,delRDelim:oe,emStrongLDelim:tr,emStrongRDelimAst:rr,emStrongRDelimUnd:or,escape:Vn,link:hr,nolink:vt,punctuation:Kn,reflink:xt,reflinkSearch:gr,tag:pr,text:Gn,url:oe},mr={...Fe,link:C(/^!?\[(label)\]\((.*?)\)/).replace("label",we).getRegex(),reflink:C(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",we).getRegex()},Re={...Fe,emStrongRDelimAst:ir,emStrongLDelim:nr,delLDelim:sr,delRDelim:lr,url:C(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",st).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:C(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},at=t=>kr[t];function U(t,e){if(e){if(z.escapeTest.test(t))return t.replace(z.escapeReplace,at)}else if(z.escapeTestNoEncode.test(t))return t.replace(z.escapeReplaceNoEncode,at);return t}function lt(t){try{t=encodeURI(t).replace(z.percentDecode,"%")}catch(e){return null}return t}function ct(t,e){var o;let n=t.replace(z.findPipe,(a,s,c)=>{let l=!1,d=s;for(;--d>=0&&c[d]==="\\";)l=!l;return l?"|":" |"}),r=n.split(z.splitPipe),i=0;if(r[0].trim()||r.shift(),r.length>0&&!((o=r.at(-1))!=null&&o.trim())&&r.pop(),e)if(r.length>e)r.splice(e);else for(;r.length0?-2:-1}function br(t,e=0){let n=e,r="";for(let i of t)if(i===" "){let o=4-n%4;r+=" ".repeat(o),n+=o}else r+=i,n++;return r}function ut(t,e,n,r,i){let o=e.href,a=e.title||null,s=t[1].replace(i.other.outputLinkReplace,"$1");r.state.inLink=!0;let c={type:t[0].charAt(0)==="!"?"image":"link",raw:n,href:o,title:a,text:s,tokens:r.inlineTokens(s)};return r.state.inLink=!1,c}function yr(t,e,n){let r=t.match(n.other.indentCodeCompensation);if(r===null)return e;let i=r[1];return e.split(` +(()=>{var Bn=Object.create;var $e=Object.defineProperty;var In=Object.getOwnPropertyDescriptor;var Hn=Object.getOwnPropertyNames;var _n=Object.getPrototypeOf,Fn=Object.prototype.hasOwnProperty;var zn=(t,e,n)=>e in t?$e(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var On=(t=>typeof require!="undefined"?require:typeof Proxy!="undefined"?new Proxy(t,{get:(e,n)=>(typeof require!="undefined"?require:e)[n]}):t)(function(t){if(typeof require!="undefined")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var Wn=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Hn(e))!Fn.call(t,i)&&i!==n&&$e(t,i,{get:()=>e[i],enumerable:!(r=In(e,i))||r.enumerable});return t};var qn=(t,e,n)=>(n=t!=null?Bn(_n(t)):{},Wn(e||!t||!t.__esModule?$e(n,"default",{value:t,enumerable:!0}):n,t));var B=(t,e,n)=>zn(t,typeof e!="symbol"?e+"":e,n);function Ie(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var ce=Ie();function kt(t){ce=t}var ae={exec:()=>null};function N(t,e=""){let n=typeof t=="string"?t:t.source,r={replace:(i,o)=>{let a=typeof o=="string"?o:o.source;return a=a.replace(Q.caret,"$1"),n=n.replace(i,a),r},getRegex:()=>new RegExp(n,e)};return r}var jn=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:t=>new RegExp(`^( {0,3}${t})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`),hrRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}#`),htmlBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}<(?:[a-z].*>|!--)`,"i"),blockquoteBeginRegex:t=>new RegExp(`^ {0,${Math.min(3,t-1)}}>`)},Qn=/^(?:[ \t]*(?:\n|$))+/,Vn=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Un=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,ke=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,Gn=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,He=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,wt=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,bt=N(wt).replace(/bull/g,He).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),Kn=N(wt).replace(/bull/g,He).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),_e=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,Zn=/^[^\n]+/,Fe=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Yn=N(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",Fe).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),Xn=N(/^(bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,He).getRegex(),Ee="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",ze=/|$))/,Jn=N("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",ze).replace("tag",Ee).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),yt=N(_e).replace("hr",ke).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ee).getRegex(),er=N(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",yt).getRegex(),Oe={blockquote:er,code:Vn,def:Yn,fences:Un,heading:Gn,hr:ke,html:Jn,lheading:bt,list:Xn,newline:Qn,paragraph:yt,table:ae,text:Zn},dt=N("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",ke).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ee).getRegex(),tr={...Oe,lheading:Kn,table:dt,paragraph:N(_e).replace("hr",ke).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",dt).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",Ee).getRegex()},nr={...Oe,html:N(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",ze).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:ae,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:N(_e).replace("hr",ke).replace("heading",` *#{1,6} *[^ +]`).replace("lheading",bt).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},rr=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,ir=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,xt=/^( {2,}|\\)\n(?!\s*$)/,or=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",jn?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),Tt=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,pr=N(Tt,"u").replace(/punct/g,Me).getRegex(),hr=N(Tt,"u").replace(/punct/g,St).getRegex(),Et="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",gr=N(Et,"gu").replace(/notPunctSpace/g,vt).replace(/punctSpace/g,We).replace(/punct/g,Me).getRegex(),mr=N(Et,"gu").replace(/notPunctSpace/g,lr).replace(/punctSpace/g,ar).replace(/punct/g,St).getRegex(),fr=N("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,vt).replace(/punctSpace/g,We).replace(/punct/g,Me).getRegex(),kr=N(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,Lt).getRegex(),wr="^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)",br=N(wr,"gu").replace(/notPunctSpace/g,ur).replace(/punctSpace/g,cr).replace(/punct/g,Lt).getRegex(),yr=N(/\\(punct)/,"gu").replace(/punct/g,Me).getRegex(),xr=N(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),vr=N(ze).replace("(?:-->|$)","-->").getRegex(),Sr=N("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",vr).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),Se=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,Lr=N(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label",Se).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),Mt=N(/^!?\[(label)\]\[(ref)\]/).replace("label",Se).replace("ref",Fe).getRegex(),At=N(/^!?\[(ref)\](?:\[\])?/).replace("ref",Fe).getRegex(),Tr=N("reflink|nolink(?!\\()","g").replace("reflink",Mt).replace("nolink",At).getRegex(),pt=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,qe={_backpedal:ae,anyPunctuation:yr,autolink:xr,blockSkip:dr,br:xt,code:ir,del:ae,delLDelim:ae,delRDelim:ae,emStrongLDelim:pr,emStrongRDelimAst:gr,emStrongRDelimUnd:fr,escape:rr,link:Lr,nolink:At,punctuation:sr,reflink:Mt,reflinkSearch:Tr,tag:Sr,text:or,url:ae},Er={...qe,link:N(/^!?\[(label)\]\((.*?)\)/).replace("label",Se).getRegex(),reflink:N(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Se).getRegex()},Pe={...qe,emStrongRDelimAst:mr,emStrongLDelim:hr,delLDelim:kr,delRDelim:br,url:N(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",pt).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:N(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},ht=t=>Ar[t];function te(t,e){if(e){if(Q.escapeTest.test(t))return t.replace(Q.escapeReplace,ht)}else if(Q.escapeTestNoEncode.test(t))return t.replace(Q.escapeReplaceNoEncode,ht);return t}function gt(t){try{t=encodeURI(t).replace(Q.percentDecode,"%")}catch(e){return null}return t}function mt(t,e){var o;let n=t.replace(Q.findPipe,(a,s,c)=>{let l=!1,p=s;for(;--p>=0&&c[p]==="\\";)l=!l;return l?"|":" |"}),r=n.split(Q.splitPipe),i=0;if(r[0].trim()||r.shift(),r.length>0&&!((o=r.at(-1))!=null&&o.trim())&&r.pop(),e)if(r.length>e)r.splice(e);else for(;r.length0?-2:-1}function Cr(t,e=0){let n=e,r="";for(let i of t)if(i===" "){let o=4-n%4;r+=" ".repeat(o),n+=o}else r+=i,n++;return r}function ft(t,e,n,r,i){let o=e.href,a=e.title||null,s=t[1].replace(i.other.outputLinkReplace,"$1");r.state.inLink=!0;let c={type:t[0].charAt(0)==="!"?"image":"link",raw:n,href:o,title:a,text:s,tokens:r.inlineTokens(s)};return r.state.inLink=!1,c}function Nr(t,e,n){let r=t.match(n.other.indentCodeCompensation);if(r===null)return e;let i=r[1];return e.split(` `).map(o=>{let a=o.match(n.other.beginningSpace);if(a===null)return o;let[s]=a;return s.length>=i.length?o.slice(i.length):o}).join(` -`)}var be=class{constructor(t){$(this,"options");$(this,"rules");$(this,"lexer");this.options=t||ae}space(t){let e=this.rules.block.newline.exec(t);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(t){let e=this.rules.block.code.exec(t);if(e){let n=e[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:e[0],codeBlockStyle:"indented",text:this.options.pedantic?n:de(n,` -`)}}}fences(t){let e=this.rules.block.fences.exec(t);if(e){let n=e[0],r=yr(n,e[3]||"",this.rules);return{type:"code",raw:n,lang:e[2]?e[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):e[2],text:r}}}heading(t){let e=this.rules.block.heading.exec(t);if(e){let n=e[2].trim();if(this.rules.other.endingHash.test(n)){let r=de(n,"#");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:"heading",raw:e[0],depth:e[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(t){let e=this.rules.block.hr.exec(t);if(e)return{type:"hr",raw:de(e[0],` -`)}}blockquote(t){let e=this.rules.block.blockquote.exec(t);if(e){let n=de(e[0],` +`)}var Le=class{constructor(t){B(this,"options");B(this,"rules");B(this,"lexer");this.options=t||ce}space(t){let e=this.rules.block.newline.exec(t);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(t){let e=this.rules.block.code.exec(t);if(e){let n=e[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:e[0],codeBlockStyle:"indented",text:this.options.pedantic?n:me(n,` +`)}}}fences(t){let e=this.rules.block.fences.exec(t);if(e){let n=e[0],r=Nr(n,e[3]||"",this.rules);return{type:"code",raw:n,lang:e[2]?e[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):e[2],text:r}}}heading(t){let e=this.rules.block.heading.exec(t);if(e){let n=e[2].trim();if(this.rules.other.endingHash.test(n)){let r=me(n,"#");(this.options.pedantic||!r||this.rules.other.endingSpaceChar.test(r))&&(n=r.trim())}return{type:"heading",raw:e[0],depth:e[1].length,text:n,tokens:this.lexer.inline(n)}}}hr(t){let e=this.rules.block.hr.exec(t);if(e)return{type:"hr",raw:me(e[0],` +`)}}blockquote(t){let e=this.rules.block.blockquote.exec(t);if(e){let n=me(e[0],` `).split(` `),r="",i="",o=[];for(;n.length>0;){let a=!1,s=[],c;for(c=0;c1,a={type:"list",raw:"",ordered:o,start:o?+i.slice(0,-1):"",loose:!1,items:[]};i=o?`\\d{1,9}\\${i.slice(-1)}`:`\\${i}`,this.options.pedantic&&(i=o?i:"[*+-]");let s=this.rules.other.listItemRegex(i),c=!1;for(;t;){let d=!1,f="",u="";if(!(e=s.exec(t))||this.rules.block.hr.test(t))break;f=e[0],t=t.substring(f.length);let h=br(e[2].split(` -`,1)[0],e[1].length),p=t.split(` -`,1)[0],b=!h.trim(),m=0;if(this.options.pedantic?(m=2,u=h.trimStart()):b?m=e[1].length+1:(m=h.search(this.rules.other.nonSpaceChar),m=m>4?1:m,u=h.slice(m),m+=e[1].length),b&&this.rules.other.blankLine.test(p)&&(f+=p+` -`,t=t.substring(p.length+1),d=!0),!d){let w=this.rules.other.nextBulletRegex(m),g=this.rules.other.hrRegex(m),S=this.rules.other.fencesBeginRegex(m),x=this.rules.other.headingBeginRegex(m),y=this.rules.other.htmlBeginRegex(m),N=this.rules.other.blockquoteBeginRegex(m);for(;t;){let P=t.split(` -`,1)[0],I;if(p=P,this.options.pedantic?(p=p.replace(this.rules.other.listReplaceNesting," "),I=p):I=p.replace(this.rules.other.tabCharGlobal," "),S.test(p)||x.test(p)||y.test(p)||N.test(p)||w.test(p)||g.test(p))break;if(I.search(this.rules.other.nonSpaceChar)>=m||!p.trim())u+=` -`+I.slice(m);else{if(b||h.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||S.test(h)||x.test(h)||g.test(h))break;u+=` -`+p}b=!p.trim(),f+=P+` -`,t=t.substring(P.length+1),h=I.slice(m)}}a.loose||(c?a.loose=!0:this.rules.other.doubleBlankLine.test(f)&&(c=!0)),a.items.push({type:"list_item",raw:f,task:!!this.options.gfm&&this.rules.other.listIsTask.test(u),loose:!1,text:u,tokens:[]}),a.raw+=f}let l=a.items.at(-1);if(l)l.raw=l.raw.trimEnd(),l.text=l.text.trimEnd();else return;a.raw=a.raw.trimEnd();for(let d of a.items){if(this.lexer.state.top=!1,d.tokens=this.lexer.blockTokens(d.text,[]),d.task){if(d.text=d.text.replace(this.rules.other.listReplaceTask,""),((n=d.tokens[0])==null?void 0:n.type)==="text"||((r=d.tokens[0])==null?void 0:r.type)==="paragraph"){d.tokens[0].raw=d.tokens[0].raw.replace(this.rules.other.listReplaceTask,""),d.tokens[0].text=d.tokens[0].text.replace(this.rules.other.listReplaceTask,"");for(let u=this.lexer.inlineQueue.length-1;u>=0;u--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[u].src)){this.lexer.inlineQueue[u].src=this.lexer.inlineQueue[u].src.replace(this.rules.other.listReplaceTask,"");break}}let f=this.rules.other.listTaskCheckbox.exec(d.raw);if(f){let u={type:"checkbox",raw:f[0]+" ",checked:f[0]!=="[ ]"};d.checked=u.checked,a.loose?d.tokens[0]&&["paragraph","text"].includes(d.tokens[0].type)&&"tokens"in d.tokens[0]&&d.tokens[0].tokens?(d.tokens[0].raw=u.raw+d.tokens[0].raw,d.tokens[0].text=u.raw+d.tokens[0].text,d.tokens[0].tokens.unshift(u)):d.tokens.unshift({type:"paragraph",raw:u.raw,text:u.raw,tokens:[u]}):d.tokens.unshift(u)}}if(!a.loose){let f=d.tokens.filter(h=>h.type==="space"),u=f.length>0&&f.some(h=>this.rules.other.anyLine.test(h.raw));a.loose=u}}if(a.loose)for(let d of a.items){d.loose=!0;for(let f of d.tokens)f.type==="text"&&(f.type="paragraph")}return a}}html(t){let e=this.rules.block.html.exec(t);if(e)return{type:"html",block:!0,raw:e[0],pre:e[1]==="pre"||e[1]==="script"||e[1]==="style",text:e[0]}}def(t){let e=this.rules.block.def.exec(t);if(e){let n=e[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),r=e[2]?e[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=e[3]?e[3].substring(1,e[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):e[3];return{type:"def",tag:n,raw:e[0],href:r,title:i}}}table(t){var a;let e=this.rules.block.table.exec(t);if(!e||!this.rules.other.tableDelimiter.test(e[2]))return;let n=ct(e[1]),r=e[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=(a=e[3])!=null&&a.trim()?e[3].replace(this.rules.other.tableRowBlankLine,"").split(` -`):[],o={type:"table",raw:e[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let s of r)this.rules.other.tableAlignRight.test(s)?o.align.push("right"):this.rules.other.tableAlignCenter.test(s)?o.align.push("center"):this.rules.other.tableAlignLeft.test(s)?o.align.push("left"):o.align.push(null);for(let s=0;s({text:c,tokens:this.lexer.inline(c),header:!1,align:o.align[l]})));return o}}lheading(t){let e=this.rules.block.lheading.exec(t);if(e)return{type:"heading",raw:e[0],depth:e[2].charAt(0)==="="?1:2,text:e[1],tokens:this.lexer.inline(e[1])}}paragraph(t){let e=this.rules.block.paragraph.exec(t);if(e){let n=e[1].charAt(e[1].length-1)===` -`?e[1].slice(0,-1):e[1];return{type:"paragraph",raw:e[0],text:n,tokens:this.lexer.inline(n)}}}text(t){let e=this.rules.block.text.exec(t);if(e)return{type:"text",raw:e[0],text:e[0],tokens:this.lexer.inline(e[0])}}escape(t){let e=this.rules.inline.escape.exec(t);if(e)return{type:"escape",raw:e[0],text:e[1]}}tag(t){let e=this.rules.inline.tag.exec(t);if(e)return!this.lexer.state.inLink&&this.rules.other.startATag.test(e[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:e[0]}}link(t){let e=this.rules.inline.link.exec(t);if(e){let n=e[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let o=de(n.slice(0,-1),"\\");if((n.length-o.length)%2===0)return}else{let o=wr(e[2],"()");if(o===-2)return;if(o>-1){let a=(e[0].indexOf("!")===0?5:4)+e[1].length+o;e[2]=e[2].substring(0,o),e[0]=e[0].substring(0,a).trim(),e[3]=""}}let r=e[2],i="";if(this.options.pedantic){let o=this.rules.other.pedanticHrefTitle.exec(r);o&&(r=o[1],i=o[3])}else i=e[3]?e[3].slice(1,-1):"";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),ut(e,{href:r&&r.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},e[0],this.lexer,this.rules)}}reflink(t,e){let n;if((n=this.rules.inline.reflink.exec(t))||(n=this.rules.inline.nolink.exec(t))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=e[r.toLowerCase()];if(!i){let o=n[0].charAt(0);return{type:"text",raw:o,text:o}}return ut(n,i,n[0],this.lexer,this.rules)}}emStrong(t,e,n=""){let r=this.rules.inline.emStrongLDelim.exec(t);if(!(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(r[1]||r[2])||!n||this.rules.inline.punctuation.exec(n))){let i=[...r[0]].length-1,o,a,s=i,c=0,l=r[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(l.lastIndex=0,e=e.slice(-1*t.length+i);(r=l.exec(e))!=null;){if(o=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!o)continue;if(a=[...o].length,r[3]||r[4]){s+=a;continue}else if((r[5]||r[6])&&i%3&&!((i+a)%3)){c+=a;continue}if(s-=a,s>0)continue;a=Math.min(a,a+s+c);let d=[...r[0]][0].length,f=t.slice(0,i+r.index+d+a);if(Math.min(i,a)%2){let h=f.slice(1,-1);return{type:"em",raw:f,text:h,tokens:this.lexer.inlineTokens(h)}}let u=f.slice(2,-2);return{type:"strong",raw:f,text:u,tokens:this.lexer.inlineTokens(u)}}}}codespan(t){let e=this.rules.inline.code.exec(t);if(e){let n=e[2].replace(this.rules.other.newLineCharGlobal," "),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:e[0],text:n}}}br(t){let e=this.rules.inline.br.exec(t);if(e)return{type:"br",raw:e[0]}}del(t,e,n=""){let r=this.rules.inline.delLDelim.exec(t);if(r&&(!r[1]||!n||this.rules.inline.punctuation.exec(n))){let i=[...r[0]].length-1,o,a,s=i,c=this.rules.inline.delRDelim;for(c.lastIndex=0,e=e.slice(-1*t.length+i);(r=c.exec(e))!=null;){if(o=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!o||(a=[...o].length,a!==i))continue;if(r[3]||r[4]){s+=a;continue}if(s-=a,s>0)continue;a=Math.min(a,a+s);let l=[...r[0]][0].length,d=t.slice(0,i+r.index+l+a),f=d.slice(i,-i);return{type:"del",raw:d,text:f,tokens:this.lexer.inlineTokens(f)}}}}autolink(t){let e=this.rules.inline.autolink.exec(t);if(e){let n,r;return e[2]==="@"?(n=e[1],r="mailto:"+n):(n=e[1],r=n),{type:"link",raw:e[0],text:n,href:r,tokens:[{type:"text",raw:n,text:n}]}}}url(t){var n,r;let e;if(e=this.rules.inline.url.exec(t)){let i,o;if(e[2]==="@")i=e[0],o="mailto:"+i;else{let a;do a=e[0],e[0]=(r=(n=this.rules.inline._backpedal.exec(e[0]))==null?void 0:n[0])!=null?r:"";while(a!==e[0]);i=e[0],e[1]==="www."?o="http://"+e[0]:o=e[0]}return{type:"link",raw:e[0],text:i,href:o,tokens:[{type:"text",raw:i,text:i}]}}}inlineText(t){let e=this.rules.inline.text.exec(t);if(e){let n=this.lexer.state.inRawBlock;return{type:"text",raw:e[0],text:e[0],escaped:n}}}},O=class Ce{constructor(e){$(this,"tokens");$(this,"options");$(this,"state");$(this,"inlineQueue");$(this,"tokenizer");this.tokens=[],this.tokens.links=Object.create(null),this.options=e||ae,this.options.tokenizer=this.options.tokenizer||new be,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:z,block:fe.normal,inline:ue.normal};this.options.pedantic?(n.block=fe.pedantic,n.inline=ue.pedantic):this.options.gfm&&(n.block=fe.gfm,this.options.breaks?n.inline=ue.breaks:n.inline=ue.gfm),this.tokenizer.rules=n}static get rules(){return{block:fe,inline:ue}}static lex(e,n){return new Ce(n).lex(e)}static lexInline(e,n){return new Ce(n).inlineTokens(e)}lex(e){e=e.replace(z.carriageReturn,` -`),this.blockTokens(e,this.tokens);for(let n=0;n(s=l.call({lexer:this},e,n))?(e=e.substring(s.raw.length),n.push(s),!0):!1))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);let l=n.at(-1);s.raw.length===1&&l!==void 0?l.raw+=` +`),w=this.list(d);o[o.length-1]=w,r=r.substring(0,r.length-u.raw.length)+w.raw,i=i.substring(0,i.length-h.raw.length)+w.raw,n=d.substring(o.at(-1).raw.length).split(` +`);continue}}return{type:"blockquote",raw:r,tokens:o,text:i}}}list(t){var n,r;let e=this.rules.block.list.exec(t);if(e){let i=e[1].trim(),o=i.length>1,a={type:"list",raw:"",ordered:o,start:o?+i.slice(0,-1):"",loose:!1,items:[]};i=o?`\\d{1,9}\\${i.slice(-1)}`:`\\${i}`,this.options.pedantic&&(i=o?i:"[*+-]");let s=this.rules.other.listItemRegex(i),c=!1;for(;t;){let p=!1,m="",u="";if(!(e=s.exec(t))||this.rules.block.hr.test(t))break;m=e[0],t=t.substring(m.length);let h=Cr(e[2].split(` +`,1)[0],e[1].length),d=t.split(` +`,1)[0],w=!h.trim(),g=0;if(this.options.pedantic?(g=2,u=h.trimStart()):w?g=e[1].length+1:(g=h.search(this.rules.other.nonSpaceChar),g=g>4?1:g,u=h.slice(g),g+=e[1].length),w&&this.rules.other.blankLine.test(d)&&(m+=d+` +`,t=t.substring(d.length+1),p=!0),!p){let k=this.rules.other.nextBulletRegex(g),y=this.rules.other.hrRegex(g),H=this.rules.other.fencesBeginRegex(g),V=this.rules.other.headingBeginRegex(g),re=this.rules.other.htmlBeginRegex(g),oe=this.rules.other.blockquoteBeginRegex(g);for(;t;){let T=t.split(` +`,1)[0],v;if(d=T,this.options.pedantic?(d=d.replace(this.rules.other.listReplaceNesting," "),v=d):v=d.replace(this.rules.other.tabCharGlobal," "),H.test(d)||V.test(d)||re.test(d)||oe.test(d)||k.test(d)||y.test(d))break;if(v.search(this.rules.other.nonSpaceChar)>=g||!d.trim())u+=` +`+v.slice(g);else{if(w||h.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||H.test(h)||V.test(h)||y.test(h))break;u+=` +`+d}w=!d.trim(),m+=T+` +`,t=t.substring(T.length+1),h=v.slice(g)}}a.loose||(c?a.loose=!0:this.rules.other.doubleBlankLine.test(m)&&(c=!0)),a.items.push({type:"list_item",raw:m,task:!!this.options.gfm&&this.rules.other.listIsTask.test(u),loose:!1,text:u,tokens:[]}),a.raw+=m}let l=a.items.at(-1);if(l)l.raw=l.raw.trimEnd(),l.text=l.text.trimEnd();else return;a.raw=a.raw.trimEnd();for(let p of a.items){if(this.lexer.state.top=!1,p.tokens=this.lexer.blockTokens(p.text,[]),p.task){if(p.text=p.text.replace(this.rules.other.listReplaceTask,""),((n=p.tokens[0])==null?void 0:n.type)==="text"||((r=p.tokens[0])==null?void 0:r.type)==="paragraph"){p.tokens[0].raw=p.tokens[0].raw.replace(this.rules.other.listReplaceTask,""),p.tokens[0].text=p.tokens[0].text.replace(this.rules.other.listReplaceTask,"");for(let u=this.lexer.inlineQueue.length-1;u>=0;u--)if(this.rules.other.listIsTask.test(this.lexer.inlineQueue[u].src)){this.lexer.inlineQueue[u].src=this.lexer.inlineQueue[u].src.replace(this.rules.other.listReplaceTask,"");break}}let m=this.rules.other.listTaskCheckbox.exec(p.raw);if(m){let u={type:"checkbox",raw:m[0]+" ",checked:m[0]!=="[ ]"};p.checked=u.checked,a.loose?p.tokens[0]&&["paragraph","text"].includes(p.tokens[0].type)&&"tokens"in p.tokens[0]&&p.tokens[0].tokens?(p.tokens[0].raw=u.raw+p.tokens[0].raw,p.tokens[0].text=u.raw+p.tokens[0].text,p.tokens[0].tokens.unshift(u)):p.tokens.unshift({type:"paragraph",raw:u.raw,text:u.raw,tokens:[u]}):p.tokens.unshift(u)}}if(!a.loose){let m=p.tokens.filter(h=>h.type==="space"),u=m.length>0&&m.some(h=>this.rules.other.anyLine.test(h.raw));a.loose=u}}if(a.loose)for(let p of a.items){p.loose=!0;for(let m of p.tokens)m.type==="text"&&(m.type="paragraph")}return a}}html(t){let e=this.rules.block.html.exec(t);if(e)return{type:"html",block:!0,raw:e[0],pre:e[1]==="pre"||e[1]==="script"||e[1]==="style",text:e[0]}}def(t){let e=this.rules.block.def.exec(t);if(e){let n=e[1].toLowerCase().replace(this.rules.other.multipleSpaceGlobal," "),r=e[2]?e[2].replace(this.rules.other.hrefBrackets,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",i=e[3]?e[3].substring(1,e[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):e[3];return{type:"def",tag:n,raw:e[0],href:r,title:i}}}table(t){var a;let e=this.rules.block.table.exec(t);if(!e||!this.rules.other.tableDelimiter.test(e[2]))return;let n=mt(e[1]),r=e[2].replace(this.rules.other.tableAlignChars,"").split("|"),i=(a=e[3])!=null&&a.trim()?e[3].replace(this.rules.other.tableRowBlankLine,"").split(` +`):[],o={type:"table",raw:e[0],header:[],align:[],rows:[]};if(n.length===r.length){for(let s of r)this.rules.other.tableAlignRight.test(s)?o.align.push("right"):this.rules.other.tableAlignCenter.test(s)?o.align.push("center"):this.rules.other.tableAlignLeft.test(s)?o.align.push("left"):o.align.push(null);for(let s=0;s({text:c,tokens:this.lexer.inline(c),header:!1,align:o.align[l]})));return o}}lheading(t){let e=this.rules.block.lheading.exec(t);if(e)return{type:"heading",raw:e[0],depth:e[2].charAt(0)==="="?1:2,text:e[1],tokens:this.lexer.inline(e[1])}}paragraph(t){let e=this.rules.block.paragraph.exec(t);if(e){let n=e[1].charAt(e[1].length-1)===` +`?e[1].slice(0,-1):e[1];return{type:"paragraph",raw:e[0],text:n,tokens:this.lexer.inline(n)}}}text(t){let e=this.rules.block.text.exec(t);if(e)return{type:"text",raw:e[0],text:e[0],tokens:this.lexer.inline(e[0])}}escape(t){let e=this.rules.inline.escape.exec(t);if(e)return{type:"escape",raw:e[0],text:e[1]}}tag(t){let e=this.rules.inline.tag.exec(t);if(e)return!this.lexer.state.inLink&&this.rules.other.startATag.test(e[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:e[0]}}link(t){let e=this.rules.inline.link.exec(t);if(e){let n=e[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(n)){if(!this.rules.other.endAngleBracket.test(n))return;let o=me(n.slice(0,-1),"\\");if((n.length-o.length)%2===0)return}else{let o=Rr(e[2],"()");if(o===-2)return;if(o>-1){let a=(e[0].indexOf("!")===0?5:4)+e[1].length+o;e[2]=e[2].substring(0,o),e[0]=e[0].substring(0,a).trim(),e[3]=""}}let r=e[2],i="";if(this.options.pedantic){let o=this.rules.other.pedanticHrefTitle.exec(r);o&&(r=o[1],i=o[3])}else i=e[3]?e[3].slice(1,-1):"";return r=r.trim(),this.rules.other.startAngleBracket.test(r)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(n)?r=r.slice(1):r=r.slice(1,-1)),ft(e,{href:r&&r.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},e[0],this.lexer,this.rules)}}reflink(t,e){let n;if((n=this.rules.inline.reflink.exec(t))||(n=this.rules.inline.nolink.exec(t))){let r=(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=e[r.toLowerCase()];if(!i){let o=n[0].charAt(0);return{type:"text",raw:o,text:o}}return ft(n,i,n[0],this.lexer,this.rules)}}emStrong(t,e,n=""){let r=this.rules.inline.emStrongLDelim.exec(t);if(!(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!(r[1]||r[2])||!n||this.rules.inline.punctuation.exec(n))){let i=[...r[0]].length-1,o,a,s=i,c=0,l=r[0][0]==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(l.lastIndex=0,e=e.slice(-1*t.length+i);(r=l.exec(e))!=null;){if(o=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!o)continue;if(a=[...o].length,r[3]||r[4]){s+=a;continue}else if((r[5]||r[6])&&i%3&&!((i+a)%3)){c+=a;continue}if(s-=a,s>0)continue;a=Math.min(a,a+s+c);let p=[...r[0]][0].length,m=t.slice(0,i+r.index+p+a);if(Math.min(i,a)%2){let h=m.slice(1,-1);return{type:"em",raw:m,text:h,tokens:this.lexer.inlineTokens(h)}}let u=m.slice(2,-2);return{type:"strong",raw:m,text:u,tokens:this.lexer.inlineTokens(u)}}}}codespan(t){let e=this.rules.inline.code.exec(t);if(e){let n=e[2].replace(this.rules.other.newLineCharGlobal," "),r=this.rules.other.nonSpaceChar.test(n),i=this.rules.other.startingSpaceChar.test(n)&&this.rules.other.endingSpaceChar.test(n);return r&&i&&(n=n.substring(1,n.length-1)),{type:"codespan",raw:e[0],text:n}}}br(t){let e=this.rules.inline.br.exec(t);if(e)return{type:"br",raw:e[0]}}del(t,e,n=""){let r=this.rules.inline.delLDelim.exec(t);if(r&&(!r[1]||!n||this.rules.inline.punctuation.exec(n))){let i=[...r[0]].length-1,o,a,s=i,c=this.rules.inline.delRDelim;for(c.lastIndex=0,e=e.slice(-1*t.length+i);(r=c.exec(e))!=null;){if(o=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!o||(a=[...o].length,a!==i))continue;if(r[3]||r[4]){s+=a;continue}if(s-=a,s>0)continue;a=Math.min(a,a+s);let l=[...r[0]][0].length,p=t.slice(0,i+r.index+l+a),m=p.slice(i,-i);return{type:"del",raw:p,text:m,tokens:this.lexer.inlineTokens(m)}}}}autolink(t){let e=this.rules.inline.autolink.exec(t);if(e){let n,r;return e[2]==="@"?(n=e[1],r="mailto:"+n):(n=e[1],r=n),{type:"link",raw:e[0],text:n,href:r,tokens:[{type:"text",raw:n,text:n}]}}}url(t){var n,r;let e;if(e=this.rules.inline.url.exec(t)){let i,o;if(e[2]==="@")i=e[0],o="mailto:"+i;else{let a;do a=e[0],e[0]=(r=(n=this.rules.inline._backpedal.exec(e[0]))==null?void 0:n[0])!=null?r:"";while(a!==e[0]);i=e[0],e[1]==="www."?o="http://"+e[0]:o=e[0]}return{type:"link",raw:e[0],text:i,href:o,tokens:[{type:"text",raw:i,text:i}]}}}inlineText(t){let e=this.rules.inline.text.exec(t);if(e){let n=this.lexer.state.inRawBlock;return{type:"text",raw:e[0],text:e[0],escaped:n}}}},Z=class De{constructor(e){B(this,"tokens");B(this,"options");B(this,"state");B(this,"inlineQueue");B(this,"tokenizer");this.tokens=[],this.tokens.links=Object.create(null),this.options=e||ce,this.options.tokenizer=this.options.tokenizer||new Le,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let n={other:Q,block:xe.normal,inline:ge.normal};this.options.pedantic?(n.block=xe.pedantic,n.inline=ge.pedantic):this.options.gfm&&(n.block=xe.gfm,this.options.breaks?n.inline=ge.breaks:n.inline=ge.gfm),this.tokenizer.rules=n}static get rules(){return{block:xe,inline:ge}}static lex(e,n){return new De(n).lex(e)}static lexInline(e,n){return new De(n).inlineTokens(e)}lex(e){e=e.replace(Q.carriageReturn,` +`),this.blockTokens(e,this.tokens);for(let n=0;n(s=l.call({lexer:this},e,n))?(e=e.substring(s.raw.length),n.push(s),!0):!1))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);let l=n.at(-1);s.raw.length===1&&l!==void 0?l.raw+=` `:n.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);let l=n.at(-1);(l==null?void 0:l.type)==="paragraph"||(l==null?void 0:l.type)==="text"?(l.raw+=(l.raw.endsWith(` `)?"":` `)+s.raw,l.text+=` `+s.text,this.inlineQueue.at(-1).src=l.text):n.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);let l=n.at(-1);(l==null?void 0:l.type)==="paragraph"||(l==null?void 0:l.type)==="text"?(l.raw+=(l.raw.endsWith(` `)?"":` `)+s.raw,l.text+=` -`+s.raw,this.inlineQueue.at(-1).src=l.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title},n.push(s));continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),n.push(s);continue}let c=e;if((a=this.options.extensions)!=null&&a.startBlock){let l=1/0,d=e.slice(1),f;this.options.extensions.startBlock.forEach(u=>{f=u.call({lexer:this},d),typeof f=="number"&&f>=0&&(l=Math.min(l,f))}),l<1/0&&l>=0&&(c=e.substring(0,l+1))}if(this.state.top&&(s=this.tokenizer.paragraph(c))){let l=n.at(-1);r&&(l==null?void 0:l.type)==="paragraph"?(l.raw+=(l.raw.endsWith(` +`+s.raw,this.inlineQueue.at(-1).src=l.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title},n.push(s));continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),n.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),n.push(s);continue}let c=e;if((a=this.options.extensions)!=null&&a.startBlock){let l=1/0,p=e.slice(1),m;this.options.extensions.startBlock.forEach(u=>{m=u.call({lexer:this},p),typeof m=="number"&&m>=0&&(l=Math.min(l,m))}),l<1/0&&l>=0&&(c=e.substring(0,l+1))}if(this.state.top&&(s=this.tokenizer.paragraph(c))){let l=n.at(-1);r&&(l==null?void 0:l.type)==="paragraph"?(l.raw+=(l.raw.endsWith(` `)?"":` `)+s.raw,l.text+=` `+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):n.push(s),r=c.length!==e.length,e=e.substring(s.raw.length);continue}if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);let l=n.at(-1);(l==null?void 0:l.type)==="text"?(l.raw+=(l.raw.endsWith(` `)?"":` `)+s.raw,l.text+=` -`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):n.push(s);continue}if(e){let l="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return this.state.top=!0,n}inline(e,n=[]){return this.inlineQueue.push({src:e,tokens:n}),n}inlineTokens(e,n=[]){var c,l,d,f,u,h;let r=e,i=null;if(this.tokens.links){let p=Object.keys(this.tokens.links);if(p.length>0)for(;(i=this.tokenizer.rules.inline.reflinkSearch.exec(r))!=null;)p.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(r=r.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+r.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(i=this.tokenizer.rules.inline.anyPunctuation.exec(r))!=null;)r=r.slice(0,i.index)+"++"+r.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let o;for(;(i=this.tokenizer.rules.inline.blockSkip.exec(r))!=null;)o=i[2]?i[2].length:0,r=r.slice(0,i.index+o)+"["+"a".repeat(i[0].length-o-2)+"]"+r.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);r=(d=(l=(c=this.options.hooks)==null?void 0:c.emStrongMask)==null?void 0:l.call({lexer:this},r))!=null?d:r;let a=!1,s="";for(;e;){a||(s=""),a=!1;let p;if((u=(f=this.options.extensions)==null?void 0:f.inline)!=null&&u.some(m=>(p=m.call({lexer:this},e,n))?(e=e.substring(p.raw.length),n.push(p),!0):!1))continue;if(p=this.tokenizer.escape(e)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.tag(e)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.link(e)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(p.raw.length);let m=n.at(-1);p.type==="text"&&(m==null?void 0:m.type)==="text"?(m.raw+=p.raw,m.text+=p.text):n.push(p);continue}if(p=this.tokenizer.emStrong(e,r,s)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.codespan(e)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.br(e)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.del(e,r,s)){e=e.substring(p.raw.length),n.push(p);continue}if(p=this.tokenizer.autolink(e)){e=e.substring(p.raw.length),n.push(p);continue}if(!this.state.inLink&&(p=this.tokenizer.url(e))){e=e.substring(p.raw.length),n.push(p);continue}let b=e;if((h=this.options.extensions)!=null&&h.startInline){let m=1/0,w=e.slice(1),g;this.options.extensions.startInline.forEach(S=>{g=S.call({lexer:this},w),typeof g=="number"&&g>=0&&(m=Math.min(m,g))}),m<1/0&&m>=0&&(b=e.substring(0,m+1))}if(p=this.tokenizer.inlineText(b)){e=e.substring(p.raw.length),p.raw.slice(-1)!=="_"&&(s=p.raw.slice(-1)),a=!0;let m=n.at(-1);(m==null?void 0:m.type)==="text"?(m.raw+=p.raw,m.text+=p.text):n.push(p);continue}if(e){let m="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(m);break}else throw new Error(m)}}return n}},ye=class{constructor(t){$(this,"options");$(this,"parser");this.options=t||ae}space(t){return""}code({text:t,lang:e,escaped:n}){var o;let r=(o=(e||"").match(z.notSpaceStart))==null?void 0:o[0],i=t.replace(z.endingNewline,"")+` -`;return r?'
'+(n?i:U(i,!0))+`
-`:"
"+(n?i:U(i,!0))+`
+`+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):n.push(s);continue}if(e){let l="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(l);break}else throw new Error(l)}}return this.state.top=!0,n}inline(e,n=[]){return this.inlineQueue.push({src:e,tokens:n}),n}inlineTokens(e,n=[]){var c,l,p,m,u,h;let r=e,i=null;if(this.tokens.links){let d=Object.keys(this.tokens.links);if(d.length>0)for(;(i=this.tokenizer.rules.inline.reflinkSearch.exec(r))!=null;)d.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(r=r.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+r.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;(i=this.tokenizer.rules.inline.anyPunctuation.exec(r))!=null;)r=r.slice(0,i.index)+"++"+r.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let o;for(;(i=this.tokenizer.rules.inline.blockSkip.exec(r))!=null;)o=i[2]?i[2].length:0,r=r.slice(0,i.index+o)+"["+"a".repeat(i[0].length-o-2)+"]"+r.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);r=(p=(l=(c=this.options.hooks)==null?void 0:c.emStrongMask)==null?void 0:l.call({lexer:this},r))!=null?p:r;let a=!1,s="";for(;e;){a||(s=""),a=!1;let d;if((u=(m=this.options.extensions)==null?void 0:m.inline)!=null&&u.some(g=>(d=g.call({lexer:this},e,n))?(e=e.substring(d.raw.length),n.push(d),!0):!1))continue;if(d=this.tokenizer.escape(e)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.tag(e)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.link(e)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(d.raw.length);let g=n.at(-1);d.type==="text"&&(g==null?void 0:g.type)==="text"?(g.raw+=d.raw,g.text+=d.text):n.push(d);continue}if(d=this.tokenizer.emStrong(e,r,s)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.codespan(e)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.br(e)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.del(e,r,s)){e=e.substring(d.raw.length),n.push(d);continue}if(d=this.tokenizer.autolink(e)){e=e.substring(d.raw.length),n.push(d);continue}if(!this.state.inLink&&(d=this.tokenizer.url(e))){e=e.substring(d.raw.length),n.push(d);continue}let w=e;if((h=this.options.extensions)!=null&&h.startInline){let g=1/0,k=e.slice(1),y;this.options.extensions.startInline.forEach(H=>{y=H.call({lexer:this},k),typeof y=="number"&&y>=0&&(g=Math.min(g,y))}),g<1/0&&g>=0&&(w=e.substring(0,g+1))}if(d=this.tokenizer.inlineText(w)){e=e.substring(d.raw.length),d.raw.slice(-1)!=="_"&&(s=d.raw.slice(-1)),a=!0;let g=n.at(-1);(g==null?void 0:g.type)==="text"?(g.raw+=d.raw,g.text+=d.text):n.push(d);continue}if(e){let g="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(g);break}else throw new Error(g)}}return n}},Te=class{constructor(t){B(this,"options");B(this,"parser");this.options=t||ce}space(t){return""}code({text:t,lang:e,escaped:n}){var o;let r=(o=(e||"").match(Q.notSpaceStart))==null?void 0:o[0],i=t.replace(Q.endingNewline,"")+` +`;return r?'
'+(n?i:te(i,!0))+`
+`:"
"+(n?i:te(i,!0))+`
`}blockquote({tokens:t}){return`
${this.parser.parse(t)}
`}html({text:t}){return t}def(t){return""}heading({tokens:t,depth:e}){return`${this.parser.parseInline(t)} @@ -55,89 +55,92 @@ ${this.parser.parse(t)} `}tablerow({text:t}){return` ${t} `}tablecell(t){let e=this.parser.parseInline(t.tokens),n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+` -`}strong({tokens:t}){return`${this.parser.parseInline(t)}`}em({tokens:t}){return`${this.parser.parseInline(t)}`}codespan({text:t}){return`${U(t,!0)}`}br(t){return"
"}del({tokens:t}){return`${this.parser.parseInline(t)}`}link({href:t,title:e,tokens:n}){let r=this.parser.parseInline(n),i=lt(t);if(i===null)return r;t=i;let o='
",o}image({href:t,title:e,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=lt(t);if(i===null)return U(n);t=i;let o=`${U(n)}{let c=a[s].flat(1/0);n=n.concat(this.walkTokens(c,e))}):a.tokens&&(n=n.concat(this.walkTokens(a.tokens,e)))}}return n}use(...t){let e=this.defaults.extensions||{renderers:{},childTokens:{}};return t.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let o=e.renderers[i.name];o?e.renderers[i.name]=function(...a){let s=i.renderer.apply(this,a);return s===!1&&(s=o.apply(this,a)),s}:e.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let o=e[i.level];o?o.unshift(i.tokenizer):e[i.level]=[i.tokenizer],i.start&&(i.level==="block"?e.startBlock?e.startBlock.push(i.start):e.startBlock=[i.start]:i.level==="inline"&&(e.startInline?e.startInline.push(i.start):e.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(e.childTokens[i.name]=i.childTokens)}),r.extensions=e),n.renderer){let i=this.defaults.renderer||new ye(this.defaults);for(let o in n.renderer){if(!(o in i))throw new Error(`renderer '${o}' does not exist`);if(["options","parser"].includes(o))continue;let a=o,s=n.renderer[a],c=i[a];i[a]=(...l)=>{let d=s.apply(i,l);return d===!1&&(d=c.apply(i,l)),d||""}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new be(this.defaults);for(let o in n.tokenizer){if(!(o in i))throw new Error(`tokenizer '${o}' does not exist`);if(["options","rules","lexer"].includes(o))continue;let a=o,s=n.tokenizer[a],c=i[a];i[a]=(...l)=>{let d=s.apply(i,l);return d===!1&&(d=c.apply(i,l)),d}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new pe;for(let o in n.hooks){if(!(o in i))throw new Error(`hook '${o}' does not exist`);if(["options","block"].includes(o))continue;let a=o,s=n.hooks[a],c=i[a];pe.passThroughHooks.has(o)?i[a]=l=>{if(this.defaults.async&&pe.passThroughHooksRespectAsync.has(o))return(async()=>{let f=await s.call(i,l);return c.call(i,f)})();let d=s.call(i,l);return c.call(i,d)}:i[a]=(...l)=>{if(this.defaults.async)return(async()=>{let f=await s.apply(i,l);return f===!1&&(f=await c.apply(i,l)),f})();let d=s.apply(i,l);return d===!1&&(d=c.apply(i,l)),d}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,o=n.walkTokens;r.walkTokens=function(a){let s=[];return s.push(o.call(this,a)),i&&(s=s.concat(i.call(this,a))),s}}this.defaults={...this.defaults,...r}}),this}setOptions(t){return this.defaults={...this.defaults,...t},this}lexer(t,e){return O.lex(t,e!=null?e:this.defaults)}parser(t,e){return q.parse(t,e!=null?e:this.defaults)}parseMarkdown(t){return(e,n)=>{let r={...n},i={...this.defaults,...r},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof e>"u"||e===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof e!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=t),i.async)return(async()=>{let a=i.hooks?await i.hooks.preprocess(e):e,s=await(i.hooks?await i.hooks.provideLexer():t?O.lex:O.lexInline)(a,i),c=i.hooks?await i.hooks.processAllTokens(s):s;i.walkTokens&&await Promise.all(this.walkTokens(c,i.walkTokens));let l=await(i.hooks?await i.hooks.provideParser():t?q.parse:q.parseInline)(c,i);return i.hooks?await i.hooks.postprocess(l):l})().catch(o);try{i.hooks&&(e=i.hooks.preprocess(e));let a=(i.hooks?i.hooks.provideLexer():t?O.lex:O.lexInline)(e,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let s=(i.hooks?i.hooks.provideParser():t?q.parse:q.parseInline)(a,i);return i.hooks&&(s=i.hooks.postprocess(s)),s}catch(a){return o(a)}}}onError(t,e){return n=>{if(n.message+=` -Please report this to https://github.com/markedjs/marked.`,t){let r="

An error occurred:

"+U(n.message+"",!0)+"
";return e?Promise.resolve(r):r}if(e)return Promise.reject(n);throw n}}},se=new xr;function A(t,e){return se.parse(t,e)}A.options=A.setOptions=function(t){return se.setOptions(t),A.defaults=se.defaults,dt(A.defaults),A};A.getDefaults=$e;A.defaults=ae;A.use=function(...t){return se.use(...t),A.defaults=se.defaults,dt(A.defaults),A};A.walkTokens=function(t,e){return se.walkTokens(t,e)};A.parseInline=se.parseInline;A.Parser=q;A.parser=q.parse;A.Renderer=ye;A.TextRenderer=ze;A.Lexer=O;A.lexer=O.lex;A.Tokenizer=be;A.Hooks=pe;A.parse=A;var si=A.options,ai=A.setOptions,li=A.use,ci=A.walkTokens,ui=A.parseInline;var di=q.parse,pi=O.lex;function ge(t){return(t||"").trim().replace(/^#+/,"").replace(/[^\w\-/]+/g,"").toLowerCase()}function vr(t){if(!t.startsWith("---"))return"";let e=t.indexOf(` ----`,3);return e===-1?"":t.slice(3,e).trim()}function Sr(t){let e=new Set,n=t.split(` -`);for(let i of n){let o=i.match(/^\s*tags\s*:\s*\[(.*)\]\s*$/i);if(!o)continue;let a=o[1].split(",").map(s=>ge(s.replace(/["']/g,"")));for(let s of a)s&&e.add(s)}let r=!1;for(let i of n){if(/^\s*tags\s*:\s*$/i.test(i)){r=!0;continue}if(!r)continue;let o=i.match(/^\s*-\s*(.+)\s*$/);if(o){let a=ge(o[1].replace(/["']/g,""));a&&e.add(a);continue}i.trim()!==""&&!/^\s+/.test(i)&&(r=!1)}return e}function St(t){let e=new Set,n=vr(t);if(n){let i=Sr(n);for(let o of i)e.add(o)}let r=t.match(/\B#([a-zA-Z0-9][\w\-/]{1,50})\b/g);if(r)for(let i of r){let o=ge(i);o&&e.add(o)}return e}function M(t){let e=document.getElementById(t);if(!e)throw new Error(`Missing required element: #${t}`);return e}function Lt(){return{app:M("app"),menuBar:M("menuBar"),workspaceSidebar:M("workspaceSidebar"),sidebarToggleBtn:M("sidebarToggleBtn"),refreshBtn:M("refreshBtn"),sortBtn:M("sortBtn"),searchInput:M("searchInput"),webmcpNoteModal:M("webmcpNoteModal"),webmcpNoteModalBackdrop:M("webmcpNoteModalBackdrop"),webmcpNoteModalCloseBtn:M("webmcpNoteModalCloseBtn"),webmcpNoteForm:M("webmcpNoteForm"),webmcpTitleInput:M("webmcpTitleInput"),webmcpBodyInput:M("webmcpBodyInput"),webmcpTagInput:M("webmcpTagInput"),tree:M("tree"),tagRow:M("tagRow"),workspaceName:M("workspaceName"),countsPill:M("countsPill"),editor:M("editor"),editorSplit:M("editorSplit"),editorPane:M("editorPane"),previewPane:M("previewPane"),editorViewModeGroup:M("editorViewModeGroup"),editorViewSourceBtn:M("editorViewSourceBtn"),editorViewSplitBtn:M("editorViewSplitBtn"),editorViewPreviewBtn:M("editorViewPreviewBtn"),preview:M("preview"),currentFilename:M("currentFilename"),dirtyDot:M("dirtyDot"),statusBadge:M("statusBadge"),toast:M("toast"),toastMsg:M("toastMsg"),toastCloseBtn:M("toastCloseBtn"),temporarySessionBadge:M("temporarySessionBadge"),cogitoToggleBtn:M("cogitoToggleBtn"),cogitoPanel:M("cogitoPanel"),cogitoLiteBtn:M("cogitoLiteBtn"),cogitoDeepBtn:M("cogitoDeepBtn"),cogitoGenerateBtn:M("cogitoGenerateBtn"),cogitoStatus:M("cogitoStatus"),cogitoQuestionList:M("cogitoQuestionList"),documentLinterToggleBtn:M("documentLinterToggleBtn"),documentLinterPanel:M("documentLinterPanel"),documentLinterAnalyzeBtn:M("documentLinterAnalyzeBtn"),documentLinterExportBtn:M("documentLinterExportBtn"),documentLinterAutoRunToggle:M("documentLinterAutoRunToggle"),documentLinterStatus:M("documentLinterStatus"),documentLinterResults:M("documentLinterResults")}}function Tt(){return!!(window.showDirectoryPicker&&window.FileSystemHandle)}async function _e(t,e="read"){if(!t)return!1;if(!t.queryPermission||!t.requestPermission)return!0;let n={mode:e};return await t.queryPermission(n)==="granted"?!0:await t.requestPermission(n)==="granted"}function Mt({state:t,ensurePermission:e,rescanWorkspace:n,showToast:r,setStatus:i}){function o(){s(),t.autoRefreshTimer=setInterval(()=>{a().catch(c=>{r(`Auto-refresh failed: ${String(c)}`,{persist:!0}),i("Auto-refresh failed","err")})},t.autoRefreshMs)}async function a(){if(t.workspaceHandle){try{if(!await e(t.workspaceHandle,"read"))throw new Error("Folder permission revoked.")}catch(c){s(),r("Auto-refresh stopped: folder permission revoked.",{persist:!0}),i("Permission revoked","err");return}t.isDirty||await n({silent:!0})}}function s(){t.autoRefreshTimer&&(clearInterval(t.autoRefreshTimer),t.autoRefreshTimer=null)}return{startAutoRefresh:o,stopAutoRefresh:s}}function G(t){return String(t).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}var Et="ink-editor-view-mode",Lr=["split","source","preview"];function Tr(t){return t==="source"||t==="preview"?t:"split"}function Rt(){try{return Tr(localStorage.getItem(Et))}catch(t){return"split"}}function We(t,e){t.editorSplit.classList.toggle("view-split",e==="split"),t.editorSplit.classList.toggle("view-source",e==="source"),t.editorSplit.classList.toggle("view-preview",e==="preview"),t.editorPane.hidden=e==="preview",t.previewPane.hidden=e==="source",[[t.editorViewSourceBtn,"source"],[t.editorViewSplitBtn,"split"],[t.editorViewPreviewBtn,"preview"]].forEach(([r,i])=>{let o=i===e;r.classList.toggle("active",o),r.setAttribute("aria-pressed",String(o))})}function Ct(t,e,n){if(Lr.includes(n)){e.editorViewMode=n,We(t,n);try{localStorage.setItem(Et,n)}catch(r){}}}function Oe(t,e){try{t.preview.innerHTML=A.parse(e||"")}catch(n){let r=n instanceof Error?n.message:String(n);t.preview.innerHTML=`
${G(r)}
`}}function Se(t,e,n){t.dirtyDot.classList.toggle("show",e.isDirty);let r=e.currentRelPath?e.currentRelPath.split("/").pop():"No note open";t.currentFilename.textContent=`${r}${e.isDirty?" \u2022 Unsaved":""}`,e.isDirty&&n("Unsaved changes","warn")}var Le=["default","classic","cobalt","monokai","office","twilight","xcode"];function qe(t){if(!Le.includes(t))return;t==="default"?document.documentElement.removeAttribute("data-theme"):document.documentElement.setAttribute("data-theme",t);try{localStorage.setItem("ink-theme",t)}catch(n){}document.querySelectorAll(".menu-theme-check").forEach(n=>{n.classList.remove("active")});let e=document.getElementById(`themeCheck-${t}`);e&&e.classList.add("active")}function At(){var e;let t="default";try{t=(e=localStorage.getItem("ink-theme"))!=null?e:"default"}catch(n){}qe(Le.includes(t)?t:"default")}function $t(t,e){let n=e?"Cmd":"Ctrl";t.menuBar.querySelectorAll(".menu-shortcut").forEach(i=>{let o=i.textContent;o&&(o.includes("Cmd/Ctrl")?i.textContent=o.replace("Cmd/Ctrl",n):o.includes("Ctrl")&&(i.textContent=o.replace("Ctrl",n)))})}function Nt({state:t,els:e,showToast:n,renderTree:r,callbacks:i}){function o(){t.sortMode=t.sortMode==="name"?"modified":"name",e.sortBtn.textContent=`Sort: ${t.sortMode==="name"?"Name":"Last modified"}`;let c=document.querySelector('[data-action="sort"] .menu-label-text');c&&(c.textContent=`Sort: ${t.sortMode==="name"?"Name":"Modified"}`),r().catch(l=>{n(`Sort render failed: ${String(l)}`,{persist:!0})})}function a(){t.isDirty&&!confirm("You have unsaved changes. Are you sure you want to exit?")||window.close()}function s(c){switch(c){case"new-note":i.createNewNote().catch(l=>{n(`Create note failed: ${String(l)}`,{persist:!0})});break;case"new-folder":i.createNewFolder().catch(l=>{n(`Create folder failed: ${String(l)}`,{persist:!0})});break;case"open-workspace":i.openWorkspace().catch(l=>{n(`Failed to open workspace: ${String(l)}`,{persist:!0})});break;case"close-workspace":i.closeWorkspace();break;case"exit":a();break;case"save":i.saveCurrentNote().catch(l=>{n(`Save failed: ${String(l)}`,{persist:!0})});break;case"save-as":i.saveAsNewNote().catch(l=>{n(`Save As failed: ${String(l)}`,{persist:!0})});break;case"refresh":i.handleRefresh();break;case"sort":o();break;case"collapse-sidebar":i.setSidebarCollapsed(!t.isSidebarCollapsed);break;case"export-json":i.exportAsJson();break;case"export-markdown":i.exportAsMarkdown();break;case"theme-default":case"theme-classic":case"theme-cobalt":case"theme-monokai":case"theme-office":case"theme-twilight":case"theme-xcode":{let l=c.replace("theme-","");Le.includes(l)&&qe(l);break}}}return{toggleSort:o,handleMenuAction:s,handleExit:a}}function je(t,e){let n=e.isSidebarCollapsed;t.app.classList.toggle("sidebar-collapsed",n),t.workspaceSidebar.classList.toggle("collapsed",n),t.sidebarToggleBtn.setAttribute("aria-expanded",String(!n)),t.sidebarToggleBtn.setAttribute("aria-label",n?"Expand sidebar":"Collapse sidebar"),t.sidebarToggleBtn.title=n?"Expand sidebar":"Collapse sidebar",t.sidebarToggleBtn.textContent=n?"\u25BC Expand":"\u25B6 Collapse"}function Qe(t,e,n){e.isSidebarCollapsed=n,je(t,e)}function ee(t,e,n="neutral"){t.statusBadge.textContent=e,t.statusBadge.classList.remove("ok","warn","err"),n!=="neutral"&&t.statusBadge.classList.add(n)}function Pt(t,e){function n(i,o={}){t.toastMsg.textContent=i,t.toast.classList.add("show"),e.current&&(clearTimeout(e.current),e.current=null),o.persist||(e.current=setTimeout(()=>{t.toast.classList.remove("show")},3500))}function r(){t.toast.classList.remove("show")}return{showToast:n,hideToast:r}}var me={folder:()=>'',folderOpen:()=>'',fileText:()=>'',library:()=>'',check:()=>''};function Dt({state:t,els:e,handlers:n,showToast:r}){function i(){let u=new Map,h=t.isTemporarySession?t.inMemoryNotes:t.notes;for(let m of h)for(let w of m.tags)u.set(w,(u.get(w)||0)+1);let p=[...u.entries()].sort((m,w)=>w[1]-m[1]||m[0].localeCompare(w[0])).slice(0,50);if(e.tagRow.innerHTML="",p.length===0)return;let b=document.createElement("button");b.className=`tag${t.tagFilter?"":" active"}`,b.textContent="All",b.title="Clear tag filter",b.addEventListener("click",()=>{t.tagFilter="",i(),t.isTemporarySession?l():a().catch(m=>{r(`Tag render failed: ${String(m)}`,{persist:!0})})}),e.tagRow.appendChild(b);for(let[m,w]of p){let g=document.createElement("button");g.className=`tag${t.tagFilter===m?" active":""}`,g.textContent=`#${m}`,g.title=`${w} note${w===1?"":"s"} tagged #${m}`,g.addEventListener("click",()=>{t.tagFilter=t.tagFilter===m?"":m,i(),t.isTemporarySession?l():a().catch(S=>{r(`Tag render failed: ${String(S)}`,{persist:!0})})}),e.tagRow.appendChild(g)}}async function o(){let u=[...t.notes];t.tagFilter&&(u=u.filter(b=>b.tags.has(t.tagFilter))),t.sortMode==="modified"?u.sort((b,m)=>(m.lastModified||0)-(b.lastModified||0)):u.sort((b,m)=>b.relPath.localeCompare(m.relPath));let h=t.searchQuery.trim().toLowerCase();if(!h)return new Set(u.map(b=>b.relPath));let p=new Set;for(let b of u){if(b.relPath.toLowerCase().includes(h)){p.add(b.relPath);continue}try{(await(await b.handle.getFile()).text()).toLowerCase().includes(h)&&p.add(b.relPath)}catch(m){}}return p}async function a(){if(!t.fileTree){e.tree.innerHTML='
Open a folder to begin.
';return}let u=await o(),h=s(t.fileTree,u);if(e.tree.innerHTML="",!h||h.type!=="dir"||h.children.length===0){e.tree.innerHTML='
No matching notes.
';return}for(let p of h.children)c(p,0)}function s(u,h){if(u.type==="file")return h.has(u.relPath)?u:null;let p=[];for(let b of u.children){let m=s(b,h);m&&p.push(m)}return u.relPath===""?{...u,children:p}:p.length===0?null:{...u,children:p}}function c(u,h){let p=document.createElement("div");if(p.className="node",p.style.paddingLeft=`${8+h*12}px`,u.type==="dir"){let w=t.collapsedDirs.has(u.relPath);if(p.innerHTML=` - ${w?"\u25B6":"\u25BC"} - ${w?me.folder():me.folderOpen()} - ${G(u.name)} - `,p.addEventListener("click",g=>{g.stopPropagation(),w?t.collapsedDirs.delete(u.relPath):t.collapsedDirs.add(u.relPath),a().catch(S=>{r(`Tree render failed: ${String(S)}`,{persist:!0})})}),e.tree.appendChild(p),!w)for(let g of u.children)c(g,h+1);return}u.relPath===t.currentRelPath&&p.classList.add("active");let m=t.sortMode==="modified"&&u.noteRef.lastModified?new Date(u.noteRef.lastModified).toLocaleDateString():"";p.innerHTML=` - ${me.fileText()} - ${G(u.noteRef.name)} - ${G(m)} - `,p.addEventListener("click",w=>{w.stopPropagation(),n.openNoteByRelPath(u.relPath,u.handle).catch(g=>{r(`Open note failed: ${String(g)}`,{persist:!0})})}),e.tree.appendChild(p)}function l(){if(e.tree.innerHTML="",t.inMemoryNotes.length===0){e.tree.innerHTML='
Temporary session. Create a note to begin.
';return}let u=[...t.inMemoryNotes].sort((h,p)=>h.relPath.localeCompare(p.relPath));for(let h of u)d(h)}function d(u){let h=document.createElement("div");h.className="node",u.relPath===t.currentRelPath&&h.classList.add("active");let b=t.sortMode==="modified"?new Date(u.lastModified).toLocaleDateString():"";h.innerHTML=` - ${me.fileText()} - ${G(u.name)} - ${G(b)} - `,h.addEventListener("click",()=>{n.openInMemoryNote(u.relPath)}),e.tree.appendChild(h)}function f(){let u=t.inMemoryNotes.length;e.countsPill.textContent=`${u} note${u===1?"":"s"}`}return{computeMatches:o,renderTree:a,renderTags:i,renderInMemoryTree:l,updateCountsPill:f}}function Bt({state:t,els:e,actions:n}){let r=!1,i=Ve(e),o=null;function a(h){e.webmcpNoteModal.setAttribute("aria-hidden","false"),e.webmcpNoteModal.classList.add("show"),h!=null&&h.focusTitle&&queueMicrotask(()=>{e.webmcpTitleInput.focus()})}function s(){e.webmcpNoteModal.classList.remove("show"),e.webmcpNoteModal.setAttribute("aria-hidden","true")}function c(h){r=!0,a(h)}function l(){if(e.webmcpNoteModal.classList.contains("show")){s();return}a({focusTitle:!0})}function d(){let h=Ve(e);h!==i&&(i=h,Mr(e)&&c())}function f(){o===null&&(o=window.setInterval(()=>{d()},150))}Cr(e,n.handleMenuAction),e.sidebarToggleBtn.addEventListener("click",()=>{n.toggleSidebar()}),e.refreshBtn.addEventListener("click",()=>{n.handleRefresh()}),e.sortBtn.addEventListener("click",()=>{n.toggleSort()}),e.searchInput.addEventListener("input",()=>{n.handleSearchInput(e.searchInput.value)});let u=[e.webmcpTitleInput,e.webmcpBodyInput,e.webmcpTagInput];u.forEach(h=>{h.addEventListener("input",d),h.addEventListener("change",d),h.addEventListener("focus",()=>{e.webmcpNoteModal.classList.contains("show")||c()})}),u.forEach(h=>{Er(h,d)}),e.webmcpNoteModalCloseBtn.addEventListener("click",()=>{s()}),e.webmcpNoteModalBackdrop.addEventListener("click",()=>{s()}),e.webmcpNoteForm.addEventListener("submit",h=>{let p=h,b=typeof p.respondWith=="function";h.preventDefault(),p.agentInvoked&&c();let m={title:e.webmcpTitleInput.value,body:e.webmcpBodyInput.value,tag:e.webmcpTagInput.value},w=n.createNoteFromTool(m).then(g=>(g.ok&&(b||(e.webmcpNoteForm.reset(),i=Ve(e)),r&&(s(),r=!1)),g));if(typeof p.respondWith=="function"){p.respondWith(w);return}w.catch(g=>{n.showToast(`WebMCP note creation failed: ${String(g)}`,{persist:!0})})}),e.editor.addEventListener("input",()=>{t.currentRelPath&&n.handleEditorInput(e.editor.value)}),e.editor.addEventListener("keydown",h=>{if((navigator.platform.toLowerCase().includes("mac")?h.metaKey:h.ctrlKey)&&!h.shiftKey&&h.key.toLowerCase()==="s"){h.preventDefault(),n.saveCurrentNote().catch(m=>{n.showToast(`Save failed: ${String(m)}`,{persist:!0})});return}if(h.key==="Tab"){h.preventDefault();let m=e.editor.selectionStart;e.editor.setRangeText(" ",m,m,"end")}}),e.toastCloseBtn.addEventListener("click",()=>{n.hideToast()}),e.cogitoToggleBtn.addEventListener("click",()=>{n.toggleCogitoPanel()}),e.cogitoLiteBtn.addEventListener("click",()=>{n.selectCogitoModel("lite")}),e.cogitoDeepBtn.addEventListener("click",()=>{n.selectCogitoModel("deep")}),e.cogitoGenerateBtn.addEventListener("click",()=>{n.generateCogitoQuestions().catch(h=>{n.showToast(`Cogito generation failed: ${String(h)}`,{persist:!0})})}),e.cogitoQuestionList.addEventListener("click",h=>{let p=h.target;if(!p)return;let b=p.closest("[data-question-index]");if(!b)return;let m=b.getAttribute("data-question-index"),w=Number(m);Number.isNaN(w)||n.insertCogitoQuestion(w)}),e.editorViewModeGroup.addEventListener("click",h=>{let p=h.target;if(!p)return;let b=p.closest("[data-editor-view-mode]");if(!b)return;let m=b.getAttribute("data-editor-view-mode");m&&n.setEditorViewMode(m)}),e.documentLinterToggleBtn.addEventListener("click",()=>{n.toggleDocumentLinterPanel()}),e.documentLinterAnalyzeBtn.addEventListener("click",()=>{n.analyzeDocument().catch(h=>{n.showToast(`Document analysis failed: ${String(h)}`,{persist:!0})})}),e.documentLinterExportBtn.addEventListener("click",()=>{n.exportDocumentLinterSuggestions().catch(h=>{n.showToast(`Document export failed: ${String(h)}`,{persist:!0})})}),window.addEventListener("beforeunload",h=>{t.isDirty&&(h.preventDefault(),h.returnValue="")}),window.addEventListener("keydown",h=>{h.key==="Escape"&&e.webmcpNoteModal.classList.contains("show")&&s()}),window.addEventListener("keydown",h=>{(/Mac|iPod|iPhone|iPad/.test(navigator.userAgent)?h.metaKey:h.ctrlKey)&&h.altKey&&h.key.toLowerCase()==="n"&&(h.preventDefault(),l())}),Rr()&&a({focusTitle:!0}),f(),Ar(n)}function Ve(t){return[t.webmcpTitleInput.value,t.webmcpBodyInput.value,t.webmcpTagInput.value].join("\u241F")}function Mr(t){return t.webmcpTitleInput.value.trim()!==""||t.webmcpBodyInput.value.trim()!==""||t.webmcpTagInput.value.trim()!==""}function Er(t,e){var i;let n=Object.getPrototypeOf(t),r=Object.getOwnPropertyDescriptor(n,"value");!(r!=null&&r.get)||!r.set||Object.defineProperty(t,"value",{configurable:!0,enumerable:(i=r.enumerable)!=null?i:!0,get(){return r.get.call(this)},set(o){r.set.call(this,o),e()}})}function Rr(){try{return new URLSearchParams(window.location.search).get("debugWebMcpNote")==="1"}catch(t){return!1}}function Cr(t,e){let n=t.menuBar.querySelectorAll(".menu-item");n.forEach(o=>{o.addEventListener("click",a=>{a.stopPropagation();let s=o.getAttribute("aria-expanded")==="true";n.forEach(c=>c.setAttribute("aria-expanded","false")),s||o.setAttribute("aria-expanded","true")}),o.addEventListener("keydown",a=>{a.key==="Escape"&&o.setAttribute("aria-expanded","false")})});let r=t.menuBar.querySelectorAll(".submenu-parent");r.forEach(o=>{o.addEventListener("click",a=>{a.stopPropagation();let s=o.getAttribute("aria-expanded")==="true";r.forEach(c=>c.setAttribute("aria-expanded","false")),s||o.setAttribute("aria-expanded","true")}),o.addEventListener("keydown",a=>{a.key==="Escape"&&o.setAttribute("aria-expanded","false")})}),document.addEventListener("click",()=>{n.forEach(o=>o.setAttribute("aria-expanded","false")),r.forEach(o=>o.setAttribute("aria-expanded","false"))}),t.menuBar.querySelectorAll(".dropdown li[data-action]").forEach(o=>{o.addEventListener("click",()=>{let a=o.getAttribute("data-action");a&&e(a),n.forEach(s=>s.setAttribute("aria-expanded","false")),r.forEach(s=>s.setAttribute("aria-expanded","false"))})})}function Ar(t){window.addEventListener("keydown",e=>{let r=/Mac|iPod|iPhone|iPad/.test(navigator.userAgent)?e.metaKey:e.ctrlKey,i=e.altKey;if(r&&e.key.toLowerCase()==="l"){e.preventDefault(),t.handleRefresh();return}if(r&&e.key.toLowerCase()==="s"&&!e.shiftKey){e.preventDefault(),t.saveCurrentNote().catch(o=>{t.showToast(`Save failed: ${String(o)}`,{persist:!0})});return}if(r&&e.shiftKey&&e.key.toLowerCase()==="s"){e.preventDefault(),t.exportAsJson();return}if(r&&e.shiftKey&&e.key.toLowerCase()==="m"){e.preventDefault(),t.exportAsMarkdown();return}if(r&&e.key.toLowerCase()==="e"){e.preventDefault(),t.createNewNote().catch(o=>{t.showToast(`Create note failed: ${String(o)}`,{persist:!0})});return}if(r&&e.shiftKey&&e.key.toLowerCase()==="o"){e.preventDefault(),t.openWorkspace().catch(o=>{t.showToast(`Failed to open workspace: ${String(o)}`,{persist:!0})});return}i&&e.shiftKey&&e.key.toLowerCase()==="o"&&(e.preventDefault(),t.openWorkspace().catch(o=>{t.showToast(`Failed to open workspace: ${String(o)}`,{persist:!0})}))})}function It({state:t,els:e,showToast:n,setStatus:r,renderPreview:i,updateDirtyUi:o,renderTree:a,renderInMemoryTree:s,renderTags:c,updateCountsPill:l,fsApi:d,parseTags:f,normalizeTag:u,autoRefresh:h}){function p(){let k=t.isTemporarySession;t.workspaceHandle=null,t.workspaceName="Temporary Session",t.fileTree=null,t.notes=[],t.currentFileHandle=null,k||(t.inMemoryNotes=[],t.currentRelPath="",t.currentContent="",t.isDirty=!1,e.editor.value="",i(e,""),o(e,t,r)),t.isTemporarySession=!0,e.workspaceName.textContent=t.workspaceName,e.workspaceName.title="Temporary Session - Data not persisted",e.temporarySessionBadge.style.display="inline",e.countsPill.textContent=`${t.inMemoryNotes.length} note${t.inMemoryNotes.length===1?"":"s"}`,e.tagRow.innerHTML="",s(),c()}function b(k){let v=k.trim().replace(/[\\/:*?"<>|]/g," ").replace(/\s+/g," ").trim()||"Untitled";return v.endsWith(".md")?v:`${v}.md`}function m({title:k,body:L,tag:v}){let T=k.trim()||"Untitled",E=L.trim(),R=u(v),B=[];return R&&B.push("---",`tags: [${R}]`,"---",""),B.push(`# ${T}`),E&&B.push("",E),B.join(` -`)}async function w(){if(!d.isFileSystemApiAvailable()){p(),e.tree.innerHTML='
Temporary session. Create a note to begin.
',n("Temporary in-memory workspace enabled. Use Export to save your notes.",{persist:!0}),r("Temporary session","warn");return}try{if(!window.showDirectoryPicker)throw new Error("File System Access API not available");let k=await window.showDirectoryPicker({id:"local-md-workspace",mode:"readwrite"});if(!await d.ensurePermission(k,"readwrite")){n("Permission denied. Please allow access to the folder.",{persist:!0}),r("Permission denied","err");return}t.workspaceHandle=k,t.workspaceName=k.name||"Selected folder",t.isTemporarySession=!1,t.collapsedDirs.clear(),t.tagFilter="",e.temporarySessionBadge.style.display="none",e.workspaceName.textContent=t.workspaceName,e.workspaceName.title=t.workspaceName,e.tagRow.innerHTML="",r("Scanning folder..."),await g(),h.startAutoRefresh(),n("Workspace opened."),r("Workspace ready","ok")}catch(k){if(k instanceof Error&&k.name==="AbortError"){r("Open folder cancelled");return}let L=k instanceof Error?k.message:String(k);n(`Failed to open folder: ${L}`,{persist:!0}),r("Failed to open folder","err")}}async function g(k={}){if(t.workspaceHandle)try{if(!await d.ensurePermission(t.workspaceHandle,"read"))throw new Error("Folder permission not granted (read)");let v=[],T={type:"dir",name:t.workspaceName,relPath:"",children:[]};await S(t.workspaceHandle,T,"",v),await x(v),t.notes=v,t.fileTree=T,e.countsPill.textContent=`${v.length} note${v.length===1?"":"s"}`,c(),await a(),k.silent||(n("Workspace refreshed."),r("Refreshed","ok"))}catch(L){let v=L instanceof Error?L.message:String(L);n(`Refresh failed: ${v}`,{persist:!0}),r("Refresh failed","err")}}async function S(k,L,v,T){for await(let[E,R]of k.entries()){if(E.startsWith("."))continue;if(R.kind==="directory"){let Z=v?`${v}/${E}`:E,it={type:"dir",name:E,relPath:Z,children:[]};L.children.push(it),await S(R,it,Z,T);continue}if(!E.toLowerCase().endsWith(".md"))continue;let B=v?`${v}/${E}`:E,ie=0,W=0;try{let Z=await R.getFile();ie=Z.lastModified||0,W=Z.size||0}catch(Z){n(`Skipped a file that couldn't be read: ${B}`);continue}let V={handle:R,name:E,relPath:B,lastModified:ie,size:W,tags:new Set};T.push(V);let X={type:"file",name:E,relPath:B,handle:R,noteRef:V};L.children.push(X)}L.children.sort((E,R)=>E.type!==R.type?E.type==="dir"?-1:1:E.name.localeCompare(R.name))}async function x(k){for(let v of k)try{let T=await v.handle.getFile(),R=await(T.size>262144?T.slice(0,262144):T).text();v.tags=f(R)}catch(T){v.tags=new Set}}async function y(k,L=null){var v;if(!(t.isDirty&&t.currentFileHandle&&!confirm("You have unsaved changes. Discard them?")))try{let T=L||((v=t.notes.find(B=>B.relPath===k))==null?void 0:v.handle);if(!T)throw new Error("File not found");let R=await(await T.getFile()).text();t.currentFileHandle=T,t.currentRelPath=k,t.currentContent=R,t.isDirty=!1,e.editor.value=R,i(e,R),o(e,t,r),r("Opened \u2713","ok"),await a()}catch(T){let E=T instanceof Error?T.message:String(T);n(`Failed to open note: ${E}`,{persist:!0}),r("Open failed","err")}}async function N(){if(t.isTemporarySession)return K();if(t.currentFileHandle)try{let k=await t.currentFileHandle.createWritable();await k.write(e.editor.value),await k.close(),t.currentContent=e.editor.value,t.isDirty=!1,o(e,t,r),r("Saved \u2713","ok"),n("Saved \u2713"),await g({silent:!0})}catch(k){let L=k instanceof Error?k.message:String(k);n(`Save failed: ${L}`,{persist:!0}),r("Save failed","err")}}async function P(){if(!t.workspaceHandle&&!t.isTemporarySession){n("Open a workspace first.");return}if(t.isDirty&&!confirm("You have unsaved changes. Continue and discard them?"))return;let k=prompt("New note name (without .md)");if(!k)return;let L=k.endsWith(".md")?k:`${k}.md`;if(t.isTemporarySession)return D(L,k);try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");let v=t.workspaceHandle;for await(let[B]of v.entries())if(B===L){n("A file with that name already exists.",{persist:!0});return}let T=await t.workspaceHandle.getFileHandle(L,{create:!0}),E=`# ${k} +`}strong({tokens:t}){return`${this.parser.parseInline(t)}`}em({tokens:t}){return`${this.parser.parseInline(t)}`}codespan({text:t}){return`${te(t,!0)}`}br(t){return"
"}del({tokens:t}){return`${this.parser.parseInline(t)}`}link({href:t,title:e,tokens:n}){let r=this.parser.parseInline(n),i=gt(t);if(i===null)return r;t=i;let o='
",o}image({href:t,title:e,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let i=gt(t);if(i===null)return te(n);t=i;let o=`${te(n)}{let c=a[s].flat(1/0);n=n.concat(this.walkTokens(c,e))}):a.tokens&&(n=n.concat(this.walkTokens(a.tokens,e)))}}return n}use(...t){let e=this.defaults.extensions||{renderers:{},childTokens:{}};return t.forEach(n=>{let r={...n};if(r.async=this.defaults.async||r.async||!1,n.extensions&&(n.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let o=e.renderers[i.name];o?e.renderers[i.name]=function(...a){let s=i.renderer.apply(this,a);return s===!1&&(s=o.apply(this,a)),s}:e.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let o=e[i.level];o?o.unshift(i.tokenizer):e[i.level]=[i.tokenizer],i.start&&(i.level==="block"?e.startBlock?e.startBlock.push(i.start):e.startBlock=[i.start]:i.level==="inline"&&(e.startInline?e.startInline.push(i.start):e.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(e.childTokens[i.name]=i.childTokens)}),r.extensions=e),n.renderer){let i=this.defaults.renderer||new Te(this.defaults);for(let o in n.renderer){if(!(o in i))throw new Error(`renderer '${o}' does not exist`);if(["options","parser"].includes(o))continue;let a=o,s=n.renderer[a],c=i[a];i[a]=(...l)=>{let p=s.apply(i,l);return p===!1&&(p=c.apply(i,l)),p||""}}r.renderer=i}if(n.tokenizer){let i=this.defaults.tokenizer||new Le(this.defaults);for(let o in n.tokenizer){if(!(o in i))throw new Error(`tokenizer '${o}' does not exist`);if(["options","rules","lexer"].includes(o))continue;let a=o,s=n.tokenizer[a],c=i[a];i[a]=(...l)=>{let p=s.apply(i,l);return p===!1&&(p=c.apply(i,l)),p}}r.tokenizer=i}if(n.hooks){let i=this.defaults.hooks||new fe;for(let o in n.hooks){if(!(o in i))throw new Error(`hook '${o}' does not exist`);if(["options","block"].includes(o))continue;let a=o,s=n.hooks[a],c=i[a];fe.passThroughHooks.has(o)?i[a]=l=>{if(this.defaults.async&&fe.passThroughHooksRespectAsync.has(o))return(async()=>{let m=await s.call(i,l);return c.call(i,m)})();let p=s.call(i,l);return c.call(i,p)}:i[a]=(...l)=>{if(this.defaults.async)return(async()=>{let m=await s.apply(i,l);return m===!1&&(m=await c.apply(i,l)),m})();let p=s.apply(i,l);return p===!1&&(p=c.apply(i,l)),p}}r.hooks=i}if(n.walkTokens){let i=this.defaults.walkTokens,o=n.walkTokens;r.walkTokens=function(a){let s=[];return s.push(o.call(this,a)),i&&(s=s.concat(i.call(this,a))),s}}this.defaults={...this.defaults,...r}}),this}setOptions(t){return this.defaults={...this.defaults,...t},this}lexer(t,e){return Z.lex(t,e!=null?e:this.defaults)}parser(t,e){return Y.parse(t,e!=null?e:this.defaults)}parseMarkdown(t){return(e,n)=>{let r={...n},i={...this.defaults,...r},o=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&r.async===!1)return o(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof e>"u"||e===null)return o(new Error("marked(): input parameter is undefined or null"));if(typeof e!="string")return o(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=t),i.async)return(async()=>{let a=i.hooks?await i.hooks.preprocess(e):e,s=await(i.hooks?await i.hooks.provideLexer():t?Z.lex:Z.lexInline)(a,i),c=i.hooks?await i.hooks.processAllTokens(s):s;i.walkTokens&&await Promise.all(this.walkTokens(c,i.walkTokens));let l=await(i.hooks?await i.hooks.provideParser():t?Y.parse:Y.parseInline)(c,i);return i.hooks?await i.hooks.postprocess(l):l})().catch(o);try{i.hooks&&(e=i.hooks.preprocess(e));let a=(i.hooks?i.hooks.provideLexer():t?Z.lex:Z.lexInline)(e,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let s=(i.hooks?i.hooks.provideParser():t?Y.parse:Y.parseInline)(a,i);return i.hooks&&(s=i.hooks.postprocess(s)),s}catch(a){return o(a)}}}onError(t,e){return n=>{if(n.message+=` +Please report this to https://github.com/markedjs/marked.`,t){let r="

An error occurred:

"+te(n.message+"",!0)+"
";return e?Promise.resolve(r):r}if(e)return Promise.reject(n);throw n}}},le=new $r;function P(t,e){return le.parse(t,e)}P.options=P.setOptions=function(t){return le.setOptions(t),P.defaults=le.defaults,kt(P.defaults),P};P.getDefaults=Ie;P.defaults=ce;P.use=function(...t){return le.use(...t),P.defaults=le.defaults,kt(P.defaults),P};P.walkTokens=function(t,e){return le.walkTokens(t,e)};P.parseInline=le.parseInline;P.Parser=Y;P.parser=Y.parse;P.Renderer=Te;P.TextRenderer=je;P.Lexer=Z;P.lexer=Z.lex;P.Tokenizer=Le;P.Hooks=fe;P.parse=P;var Si=P.options,Li=P.setOptions,Ti=P.use,Ei=P.walkTokens,Mi=P.parseInline;var Ai=Y.parse,Ri=Z.lex;function we(t){return(t||"").trim().replace(/^#+/,"").replace(/[^\w\-/]+/g,"").toLowerCase()}function Pr(t){if(!t.startsWith("---"))return"";let e=t.indexOf(` +---`,3);return e===-1?"":t.slice(3,e).trim()}function Dr(t){let e=new Set,n=t.split(` +`);for(let i of n){let o=i.match(/^\s*tags\s*:\s*\[(.*)\]\s*$/i);if(!o)continue;let a=o[1].split(",").map(s=>we(s.replace(/["']/g,"")));for(let s of a)s&&e.add(s)}let r=!1;for(let i of n){if(/^\s*tags\s*:\s*$/i.test(i)){r=!0;continue}if(!r)continue;let o=i.match(/^\s*-\s*(.+)\s*$/);if(o){let a=we(o[1].replace(/["']/g,""));a&&e.add(a);continue}i.trim()!==""&&!/^\s+/.test(i)&&(r=!1)}return e}function Rt(t){let e=new Set,n=Pr(t);if(n){let i=Dr(n);for(let o of i)e.add(o)}let r=t.match(/\B#([a-zA-Z0-9][\w\-/]{1,50})\b/g);if(r)for(let i of r){let o=we(i);o&&e.add(o)}return e}function E(t){let e=document.getElementById(t);if(!e)throw new Error(`Missing required element: #${t}`);return e}function Ct(){return{app:E("app"),menuBar:E("menuBar"),workspaceSidebar:E("workspaceSidebar"),sidebarToggleBtn:E("sidebarToggleBtn"),refreshBtn:E("refreshBtn"),sortBtn:E("sortBtn"),searchInput:E("searchInput"),webmcpNoteModal:E("webmcpNoteModal"),webmcpNoteModalBackdrop:E("webmcpNoteModalBackdrop"),webmcpNoteModalCloseBtn:E("webmcpNoteModalCloseBtn"),webmcpNoteForm:E("webmcpNoteForm"),webmcpTitleInput:E("webmcpTitleInput"),webmcpBodyInput:E("webmcpBodyInput"),webmcpTagInput:E("webmcpTagInput"),tree:E("tree"),tagRow:E("tagRow"),workspaceName:E("workspaceName"),countsPill:E("countsPill"),editor:E("editor"),editorSplit:E("editorSplit"),editorPane:E("editorPane"),previewPane:E("previewPane"),editorViewModeGroup:E("editorViewModeGroup"),editorViewSourceBtn:E("editorViewSourceBtn"),editorViewSplitBtn:E("editorViewSplitBtn"),editorViewPreviewBtn:E("editorViewPreviewBtn"),preview:E("preview"),currentFilename:E("currentFilename"),dirtyDot:E("dirtyDot"),statusBadge:E("statusBadge"),toast:E("toast"),toastMsg:E("toastMsg"),toastCloseBtn:E("toastCloseBtn"),temporarySessionBadge:E("temporarySessionBadge"),cogitoToggleBtn:E("cogitoToggleBtn"),cogitoPanel:E("cogitoPanel"),cogitoLiteBtn:E("cogitoLiteBtn"),cogitoDeepBtn:E("cogitoDeepBtn"),cogitoGenerateBtn:E("cogitoGenerateBtn"),cogitoStatus:E("cogitoStatus"),cogitoQuestionList:E("cogitoQuestionList"),documentLinterAnalyzeBtn:E("documentLinterAnalyzeBtn"),documentLinterExportBtn:E("documentLinterExportBtn"),documentLinterAutoRunToggle:E("documentLinterAutoRunToggle"),documentLinterStatus:E("documentLinterStatus"),documentLinterResults:E("documentLinterResults")}}function Nt(){return!!(window.showDirectoryPicker&&window.FileSystemHandle)}async function Qe(t,e="read"){if(!t)return!1;if(!t.queryPermission||!t.requestPermission)return!0;let n={mode:e};return await t.queryPermission(n)==="granted"?!0:await t.requestPermission(n)==="granted"}function $t({state:t,ensurePermission:e,rescanWorkspace:n,showToast:r,setStatus:i}){function o(){s(),t.autoRefreshTimer=setInterval(()=>{a().catch(c=>{r(`Auto-refresh failed: ${String(c)}`,{persist:!0}),i("Auto-refresh failed","err")})},t.autoRefreshMs)}async function a(){if(t.workspaceHandle&&!t.isDirty&&!(Date.now()-t.lastWorkspaceInteractionAt<5e3)){try{if(!await e(t.workspaceHandle,"read"))throw new Error("Folder permission revoked.")}catch(c){s(),r("Auto-refresh stopped: folder permission revoked.",{persist:!0}),i("Permission revoked","err");return}await n({silent:!0})}}function s(){t.autoRefreshTimer&&(clearInterval(t.autoRefreshTimer),t.autoRefreshTimer=null)}return{startAutoRefresh:o,stopAutoRefresh:s,runAutoRefresh:a}}function ne(t){return String(t).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}var Pt="ink-editor-view-mode",Br=["split","source","preview"];function Ir(t){return t==="source"||t==="preview"?t:"split"}function Dt(){try{return Ir(localStorage.getItem(Pt))}catch(t){return"split"}}function Ve(t,e){t.editorSplit.classList.toggle("view-split",e==="split"),t.editorSplit.classList.toggle("view-source",e==="source"),t.editorSplit.classList.toggle("view-preview",e==="preview"),t.editorPane.hidden=e==="preview",t.previewPane.hidden=e==="source",[[t.editorViewSourceBtn,"source"],[t.editorViewSplitBtn,"split"],[t.editorViewPreviewBtn,"preview"]].forEach(([r,i])=>{let o=i===e;r.classList.toggle("active",o),r.setAttribute("aria-pressed",String(o))})}function Bt(t,e,n){if(Br.includes(n)){e.editorViewMode=n,Ve(t,n);try{localStorage.setItem(Pt,n)}catch(r){}}}function Ue(t,e){try{t.preview.innerHTML=P.parse(e||"")}catch(n){let r=n instanceof Error?n.message:String(n);t.preview.innerHTML=`
${ne(r)}
`}}function Ae(t,e,n){t.dirtyDot.classList.toggle("show",e.isDirty);let r=e.currentRelPath?e.currentRelPath.split("/").pop():"No note open";t.currentFilename.textContent=`${r}${e.isDirty?" \u2022 Unsaved":""}`,e.isDirty&&n("Unsaved changes","warn")}var Re=["default","classic","cobalt","monokai","office","twilight","xcode"];function Ge(t){if(!Re.includes(t))return;t==="default"?document.documentElement.removeAttribute("data-theme"):document.documentElement.setAttribute("data-theme",t);try{localStorage.setItem("ink-theme",t)}catch(n){}document.querySelectorAll(".menu-theme-check").forEach(n=>{n.classList.remove("active")});let e=document.getElementById(`themeCheck-${t}`);e&&e.classList.add("active")}function It(){var e;let t="default";try{t=(e=localStorage.getItem("ink-theme"))!=null?e:"default"}catch(n){}Ge(Re.includes(t)?t:"default")}function Ht(t,e){let n=e?"Cmd":"Ctrl";t.menuBar.querySelectorAll(".menu-shortcut").forEach(i=>{let o=i.textContent;o&&(o.includes("Cmd/Ctrl")?i.textContent=o.replace("Cmd/Ctrl",n):o.includes("Ctrl")&&(i.textContent=o.replace("Ctrl",n)))})}function _t({state:t,els:e,showToast:n,renderTree:r,callbacks:i}){function o(){t.sortMode=t.sortMode==="name"?"modified":"name",e.sortBtn.textContent=`Sort: ${t.sortMode==="name"?"Name":"Last modified"}`;let c=document.querySelector('[data-action="sort"] .menu-label-text');c&&(c.textContent=`Sort: ${t.sortMode==="name"?"Name":"Modified"}`),r().catch(l=>{n(`Sort render failed: ${String(l)}`,{persist:!0})})}function a(){t.isDirty&&!confirm("You have unsaved changes. Are you sure you want to exit?")||window.close()}function s(c){switch(c){case"new-note":i.createNewNote().catch(l=>{n(`Create note failed: ${String(l)}`,{persist:!0})});break;case"new-folder":i.createNewFolder().catch(l=>{n(`Create folder failed: ${String(l)}`,{persist:!0})});break;case"open-workspace":i.openWorkspace().catch(l=>{n(`Failed to open workspace: ${String(l)}`,{persist:!0})});break;case"close-workspace":i.closeWorkspace();break;case"exit":a();break;case"save":i.saveCurrentNote().catch(l=>{n(`Save failed: ${String(l)}`,{persist:!0})});break;case"save-as":i.saveAsNewNote().catch(l=>{n(`Save As failed: ${String(l)}`,{persist:!0})});break;case"refresh":i.handleRefresh();break;case"sort":o();break;case"collapse-sidebar":i.setSidebarCollapsed(!t.isSidebarCollapsed);break;case"export-json":i.exportAsJson();break;case"export-markdown":i.exportAsMarkdown();break;case"theme-default":case"theme-classic":case"theme-cobalt":case"theme-monokai":case"theme-office":case"theme-twilight":case"theme-xcode":{let l=c.replace("theme-","");Re.includes(l)&&Ge(l);break}}}return{toggleSort:o,handleMenuAction:s,handleExit:a}}function Ke(t,e){let n=e.isSidebarCollapsed;t.app.classList.toggle("sidebar-collapsed",n),t.workspaceSidebar.classList.toggle("collapsed",n),t.sidebarToggleBtn.setAttribute("aria-expanded",String(!n)),t.sidebarToggleBtn.setAttribute("aria-label",n?"Expand sidebar":"Collapse sidebar"),t.sidebarToggleBtn.title=n?"Expand sidebar":"Collapse sidebar",t.sidebarToggleBtn.textContent=n?"\u25BC Expand":"\u25B6 Collapse"}function Ze(t,e,n){e.isSidebarCollapsed=n,Ke(t,e)}function se(t,e,n="neutral"){t.statusBadge.textContent=e,t.statusBadge.classList.remove("ok","warn","err"),n!=="neutral"&&t.statusBadge.classList.add(n)}function Ft(t,e){function n(i,o={}){t.toastMsg.textContent=i,t.toast.classList.add("show"),e.current&&(clearTimeout(e.current),e.current=null),o.persist||(e.current=setTimeout(()=>{t.toast.classList.remove("show")},3500))}function r(){t.toast.classList.remove("show")}return{showToast:n,hideToast:r}}var be={folder:()=>'',folderOpen:()=>'',fileText:()=>'',library:()=>'',check:()=>''};function zt({state:t,els:e,handlers:n,showToast:r}){function i(){let u=new Map,h=t.isTemporarySession?t.inMemoryNotes:t.notes;for(let g of h)for(let k of g.tags)u.set(k,(u.get(k)||0)+1);let d=[...u.entries()].sort((g,k)=>k[1]-g[1]||g[0].localeCompare(k[0])).slice(0,50);if(e.tagRow.innerHTML="",d.length===0)return;let w=document.createElement("button");w.className=`tag${t.tagFilter?"":" active"}`,w.textContent="All",w.title="Clear tag filter",w.addEventListener("click",()=>{t.tagFilter="",i(),t.isTemporarySession?l():a().catch(g=>{r(`Tag render failed: ${String(g)}`,{persist:!0})})}),e.tagRow.appendChild(w);for(let[g,k]of d){let y=document.createElement("button");y.className=`tag${t.tagFilter===g?" active":""}`,y.textContent=`#${g}`,y.title=`${k} note${k===1?"":"s"} tagged #${g}`,y.addEventListener("click",()=>{t.tagFilter=t.tagFilter===g?"":g,i(),t.isTemporarySession?l():a().catch(H=>{r(`Tag render failed: ${String(H)}`,{persist:!0})})}),e.tagRow.appendChild(y)}}async function o(){let u=[...t.notes];t.tagFilter&&(u=u.filter(w=>w.tags.has(t.tagFilter))),t.sortMode==="modified"?u.sort((w,g)=>(g.lastModified||0)-(w.lastModified||0)):u.sort((w,g)=>w.relPath.localeCompare(g.relPath));let h=t.searchQuery.trim().toLowerCase();if(!h)return new Set(u.map(w=>w.relPath));let d=new Set;for(let w of u){if(w.relPath.toLowerCase().includes(h)){d.add(w.relPath);continue}try{(await(await w.handle.getFile()).text()).toLowerCase().includes(h)&&d.add(w.relPath)}catch(g){}}return d}async function a(){if(!t.fileTree){e.tree.innerHTML='
Open a folder to begin.
';return}let u=await o(),h=s(t.fileTree,u);if(e.tree.innerHTML="",!h||h.type!=="dir"||h.children.length===0){e.tree.innerHTML='
No matching notes.
';return}for(let d of h.children)c(d,0)}function s(u,h){if(u.type==="file")return h.has(u.relPath)?u:null;let d=[];for(let w of u.children){let g=s(w,h);g&&d.push(g)}return u.relPath===""?{...u,children:d}:d.length===0?null:{...u,children:d}}function c(u,h){let d=document.createElement("div");if(d.className="node",d.style.paddingLeft=`${8+h*12}px`,u.type==="dir"){let k=t.collapsedDirs.has(u.relPath);if(d.innerHTML=` + ${k?"\u25B6":"\u25BC"} + ${k?be.folder():be.folderOpen()} + ${ne(u.name)} + `,d.addEventListener("click",y=>{y.stopPropagation(),k?t.collapsedDirs.delete(u.relPath):t.collapsedDirs.add(u.relPath),a().catch(H=>{r(`Tree render failed: ${String(H)}`,{persist:!0})})}),e.tree.appendChild(d),!k)for(let y of u.children)c(y,h+1);return}u.relPath===t.currentRelPath&&d.classList.add("active");let g=t.sortMode==="modified"&&u.noteRef.lastModified?new Date(u.noteRef.lastModified).toLocaleDateString():"";d.innerHTML=` + ${be.fileText()} + ${ne(u.noteRef.name)} + ${ne(g)} + `,d.addEventListener("click",k=>{k.stopPropagation(),n.openNoteByRelPath(u.relPath,u.handle).catch(y=>{r(`Open note failed: ${String(y)}`,{persist:!0})})}),e.tree.appendChild(d)}function l(){if(e.tree.innerHTML="",t.inMemoryNotes.length===0){e.tree.innerHTML='
Temporary session. Create a note to begin.
';return}let u=[...t.inMemoryNotes].sort((h,d)=>h.relPath.localeCompare(d.relPath));for(let h of u)p(h)}function p(u){let h=document.createElement("div");h.className="node",u.relPath===t.currentRelPath&&h.classList.add("active");let w=t.sortMode==="modified"?new Date(u.lastModified).toLocaleDateString():"";h.innerHTML=` + ${be.fileText()} + ${ne(u.name)} + ${ne(w)} + `,h.addEventListener("click",()=>{n.openInMemoryNote(u.relPath)}),e.tree.appendChild(h)}function m(){let u=t.inMemoryNotes.length;e.countsPill.textContent=`${u} note${u===1?"":"s"}`}return{computeMatches:o,renderTree:a,renderTags:i,renderInMemoryTree:l,updateCountsPill:m}}function Ot({state:t,els:e,actions:n}){let r=!1,i=Ye(e),o=null;function a(h){e.webmcpNoteModal.setAttribute("aria-hidden","false"),e.webmcpNoteModal.classList.add("show"),h!=null&&h.focusTitle&&queueMicrotask(()=>{e.webmcpTitleInput.focus()})}function s(){e.webmcpNoteModal.classList.remove("show"),e.webmcpNoteModal.setAttribute("aria-hidden","true")}function c(h){r=!0,a(h)}function l(){if(e.webmcpNoteModal.classList.contains("show")){s();return}a({focusTitle:!0})}function p(){let h=Ye(e);h!==i&&(i=h,Hr(e)&&c())}function m(){o===null&&(o=window.setInterval(()=>{p()},150))}zr(e,n.handleMenuAction),e.sidebarToggleBtn.addEventListener("click",()=>{n.toggleSidebar()}),e.refreshBtn.addEventListener("click",()=>{n.handleRefresh()}),e.sortBtn.addEventListener("click",()=>{n.toggleSort()}),e.searchInput.addEventListener("input",()=>{n.handleSearchInput(e.searchInput.value)});let u=[e.webmcpTitleInput,e.webmcpBodyInput,e.webmcpTagInput];u.forEach(h=>{h.addEventListener("input",p),h.addEventListener("change",p),h.addEventListener("focus",()=>{e.webmcpNoteModal.classList.contains("show")||c()})}),u.forEach(h=>{_r(h,p)}),e.webmcpNoteModalCloseBtn.addEventListener("click",()=>{s()}),e.webmcpNoteModalBackdrop.addEventListener("click",()=>{s()}),e.webmcpNoteForm.addEventListener("submit",h=>{let d=h,w=typeof d.respondWith=="function";h.preventDefault(),d.agentInvoked&&c();let g={title:e.webmcpTitleInput.value,body:e.webmcpBodyInput.value,tag:e.webmcpTagInput.value},k=n.createNoteFromTool(g).then(y=>(y.ok&&(w||(e.webmcpNoteForm.reset(),i=Ye(e)),r&&(s(),r=!1)),y));if(typeof d.respondWith=="function"){d.respondWith(k);return}k.catch(y=>{n.showToast(`WebMCP note creation failed: ${String(y)}`,{persist:!0})})}),e.editor.addEventListener("input",()=>{t.currentRelPath&&n.handleEditorInput(e.editor.value)}),e.editor.addEventListener("keydown",h=>{if((navigator.platform.toLowerCase().includes("mac")?h.metaKey:h.ctrlKey)&&!h.shiftKey&&h.key.toLowerCase()==="s"){h.preventDefault(),n.saveCurrentNote().catch(g=>{n.showToast(`Save failed: ${String(g)}`,{persist:!0})});return}if(h.key==="Tab"){h.preventDefault();let g=e.editor.selectionStart;e.editor.setRangeText(" ",g,g,"end")}}),e.toastCloseBtn.addEventListener("click",()=>{n.hideToast()}),e.cogitoToggleBtn.addEventListener("click",()=>{n.toggleCogitoPanel()}),e.cogitoLiteBtn.addEventListener("click",()=>{n.selectCogitoModel("lite")}),e.cogitoDeepBtn.addEventListener("click",()=>{n.selectCogitoModel("deep")}),e.cogitoGenerateBtn.addEventListener("click",()=>{n.generateCogitoQuestions().catch(h=>{n.showToast(`Cogito generation failed: ${String(h)}`,{persist:!0})})}),e.cogitoQuestionList.addEventListener("click",h=>{let d=h.target;if(!d)return;let w=d.closest("[data-question-index]");if(!w)return;let g=w.getAttribute("data-question-index"),k=Number(g);Number.isNaN(k)||n.insertCogitoQuestion(k)}),e.editorViewModeGroup.addEventListener("click",h=>{let d=h.target;if(!d)return;let w=d.closest("[data-editor-view-mode]");if(!w)return;let g=w.getAttribute("data-editor-view-mode");g&&n.setEditorViewMode(g)}),e.documentLinterAnalyzeBtn.addEventListener("click",()=>{n.analyzeDocument().catch(h=>{n.showToast(`Document analysis failed: ${String(h)}`,{persist:!0})})}),e.documentLinterExportBtn.addEventListener("click",()=>{n.exportDocumentLinterSuggestions().catch(h=>{n.showToast(`Document export failed: ${String(h)}`,{persist:!0})})}),window.addEventListener("beforeunload",h=>{t.isDirty&&(h.preventDefault(),h.returnValue="")}),window.addEventListener("keydown",h=>{h.key==="Escape"&&e.webmcpNoteModal.classList.contains("show")&&s()}),window.addEventListener("keydown",h=>{(/Mac|iPod|iPhone|iPad/.test(navigator.userAgent)?h.metaKey:h.ctrlKey)&&h.altKey&&h.key.toLowerCase()==="n"&&(h.preventDefault(),l())}),Fr()&&a({focusTitle:!0}),m(),Or(n)}function Ye(t){return[t.webmcpTitleInput.value,t.webmcpBodyInput.value,t.webmcpTagInput.value].join("\u241F")}function Hr(t){return t.webmcpTitleInput.value.trim()!==""||t.webmcpBodyInput.value.trim()!==""||t.webmcpTagInput.value.trim()!==""}function _r(t,e){var i;let n=Object.getPrototypeOf(t),r=Object.getOwnPropertyDescriptor(n,"value");!(r!=null&&r.get)||!r.set||Object.defineProperty(t,"value",{configurable:!0,enumerable:(i=r.enumerable)!=null?i:!0,get(){return r.get.call(this)},set(o){r.set.call(this,o),e()}})}function Fr(){try{return new URLSearchParams(window.location.search).get("debugWebMcpNote")==="1"}catch(t){return!1}}function zr(t,e){let n=t.menuBar.querySelectorAll(".menu-item");n.forEach(o=>{o.addEventListener("click",a=>{a.stopPropagation();let s=o.getAttribute("aria-expanded")==="true";n.forEach(c=>c.setAttribute("aria-expanded","false")),s||o.setAttribute("aria-expanded","true")}),o.addEventListener("keydown",a=>{a.key==="Escape"&&o.setAttribute("aria-expanded","false")})});let r=t.menuBar.querySelectorAll(".submenu-parent");r.forEach(o=>{o.addEventListener("click",a=>{a.stopPropagation();let s=o.getAttribute("aria-expanded")==="true";r.forEach(c=>c.setAttribute("aria-expanded","false")),s||o.setAttribute("aria-expanded","true")}),o.addEventListener("keydown",a=>{a.key==="Escape"&&o.setAttribute("aria-expanded","false")})}),document.addEventListener("click",()=>{n.forEach(o=>o.setAttribute("aria-expanded","false")),r.forEach(o=>o.setAttribute("aria-expanded","false"))}),t.menuBar.querySelectorAll(".dropdown li[data-action]").forEach(o=>{o.addEventListener("click",()=>{let a=o.getAttribute("data-action");a&&e(a),n.forEach(s=>s.setAttribute("aria-expanded","false")),r.forEach(s=>s.setAttribute("aria-expanded","false"))})})}function Or(t){window.addEventListener("keydown",e=>{let r=/Mac|iPod|iPhone|iPad/.test(navigator.userAgent)?e.metaKey:e.ctrlKey,i=e.altKey;if(r&&e.key.toLowerCase()==="l"){e.preventDefault(),t.handleRefresh();return}if(r&&e.key.toLowerCase()==="s"&&!e.shiftKey){e.preventDefault(),t.saveCurrentNote().catch(o=>{t.showToast(`Save failed: ${String(o)}`,{persist:!0})});return}if(r&&e.shiftKey&&e.key.toLowerCase()==="s"){e.preventDefault(),t.exportAsJson();return}if(r&&e.shiftKey&&e.key.toLowerCase()==="m"){e.preventDefault(),t.exportAsMarkdown();return}if(r&&e.key.toLowerCase()==="e"){e.preventDefault(),t.createNewNote().catch(o=>{t.showToast(`Create note failed: ${String(o)}`,{persist:!0})});return}if(r&&e.shiftKey&&e.key.toLowerCase()==="o"){e.preventDefault(),t.openWorkspace().catch(o=>{t.showToast(`Failed to open workspace: ${String(o)}`,{persist:!0})});return}i&&e.shiftKey&&e.key.toLowerCase()==="o"&&(e.preventDefault(),t.openWorkspace().catch(o=>{t.showToast(`Failed to open workspace: ${String(o)}`,{persist:!0})}))})}var Wr=4;function Wt({state:t,els:e,showToast:n,setStatus:r,renderPreview:i,updateDirtyUi:o,renderTree:a,renderInMemoryTree:s,renderTags:c,updateCountsPill:l,fsApi:p,parseTags:m,normalizeTag:u,autoRefresh:h}){let d=!1,w=null,g=null,k=0;function y(){t.lastWorkspaceInteractionAt=Date.now()}function H(){k+=1,w=null,g=null}function V(){let f=t.isTemporarySession;t.workspaceHandle=null,t.workspaceName="Temporary Session",t.fileTree=null,t.notes=[],t.currentFileHandle=null,f||(t.inMemoryNotes=[],t.currentRelPath="",t.currentContent="",t.isDirty=!1,e.editor.value="",i(e,""),o(e,t,r)),t.isTemporarySession=!0,e.workspaceName.textContent=t.workspaceName,e.workspaceName.title="Temporary Session - Data not persisted",e.temporarySessionBadge.style.display="inline",e.countsPill.textContent=`${t.inMemoryNotes.length} note${t.inMemoryNotes.length===1?"":"s"}`,e.tagRow.innerHTML="",s(),c()}function re(f){let S=f.trim().replace(/[\\/:*?"<>|]/g," ").replace(/\s+/g," ").trim()||"Untitled";return S.endsWith(".md")?S:`${S}.md`}function oe({title:f,body:x,tag:S}){let L=f.trim()||"Untitled",M=x.trim(),C=u(S),$=[];return C&&$.push("---",`tags: [${C}]`,"---",""),$.push(`# ${L}`),M&&$.push("",M),$.join(` +`)}async function T(){if(!p.isFileSystemApiAvailable()){V(),e.tree.innerHTML='
Temporary session. Create a note to begin.
',n("Temporary in-memory workspace enabled. Use Export to save your notes.",{persist:!0}),r("Temporary session","warn");return}if(d){r("Folder picker already open","warn");return}d=!0,H(),y(),h.stopAutoRefresh(),r("Choose a workspace folder...");try{if(!window.showDirectoryPicker)throw new Error("File System Access API not available");let f=await window.showDirectoryPicker({id:"local-md-workspace",mode:"readwrite"});if(r("Checking folder access..."),!await p.ensurePermission(f,"readwrite")){n("Permission denied. Please allow access to the folder.",{persist:!0}),r("Permission denied","err");return}t.workspaceHandle=f,t.workspaceName=f.name||"Selected folder",t.isTemporarySession=!1,t.collapsedDirs.clear(),t.tagFilter="",e.temporarySessionBadge.style.display="none",e.workspaceName.textContent=t.workspaceName,e.workspaceName.title=t.workspaceName,e.tagRow.innerHTML="",r("Scanning folder..."),await v({silent:!0,throwOnError:!0,showProgress:!0}),h.startAutoRefresh(),n("Workspace opened."),r("Workspace ready","ok")}catch(f){if(f instanceof Error&&f.name==="AbortError"){r("Open folder cancelled");return}let x=f instanceof Error?f.message:String(f);n(`Failed to open folder: ${x}`,{persist:!0}),r("Failed to open folder","err")}finally{d=!1}}async function v(f={}){if(!t.workspaceHandle)return!1;if(w&&g===t.workspaceHandle)return w;let x=t.workspaceHandle,S=t.workspaceName,L=k+1;k=L;let M=(async()=>{if(!await p.ensurePermission(x,"read"))throw new Error("Folder permission not granted (read)");let W=[],O=[],U={type:"dir",name:S,relPath:"",children:[]};return await b(x,U,"",O),k!==L||(await A(O,W,f.showProgress===!0,L),F(U),t.workspaceHandle!==x||k!==L)?!1:(t.notes=W,t.fileTree=U,e.countsPill.textContent=`${W.length} note${W.length===1?"":"s"}`,c(),await a(),f.silent||(n("Workspace refreshed."),r("Refreshed","ok")),!0)})().catch($=>{if(f.throwOnError)throw $;let W=$ instanceof Error?$.message:String($);return n(`Refresh failed: ${W}`,{persist:!0}),r("Refresh failed","err"),!1});g=x;let C=M.finally(()=>{w===C&&(w=null,g=null)});return w=C,w}async function b(f,x,S,L){for await(let[M,C]of f.entries()){if(M.startsWith("."))continue;if(C.kind==="directory"){let W=S?`${S}/${M}`:M,O={type:"dir",name:M,relPath:W,children:[]};x.children.push(O),await b(C,O,W,L);continue}if(!M.toLowerCase().endsWith(".md"))continue;let $=S?`${S}/${M}`:M;L.push({handle:C,name:M,relPath:$,parentNode:x})}}async function A(f,x,S,L){let C=0,$=0;async function W(){let U=C;if(C+=1,U>=f.length||k!==L)return;let j=f[U];try{let ie=await j.handle.getFile(),Pn=await(ie.size>262144?ie.slice(0,262144):ie).text(),ut={handle:j.handle,name:j.name,relPath:j.relPath,lastModified:ie.lastModified||0,size:ie.size||0,tags:m(Pn)};x.push(ut);let Dn={type:"file",name:j.name,relPath:j.relPath,handle:j.handle,noteRef:ut};j.parentNode.children.push(Dn)}catch(ie){n(`Skipped a file that couldn't be read: ${j.relPath}`)}finally{$+=1,S&&($===f.length||$%25===0)&&r(`Loading notes ${$}/${f.length}...`)}k===L&&await W()}let O=Math.min(Wr,f.length);await Promise.all(Array.from({length:O},()=>W()))}function F(f){for(let x of f.children)x.type==="dir"&&F(x);f.children.sort((x,S)=>x.type!==S.type?x.type==="dir"?-1:1:x.name.localeCompare(S.name))}async function D(f,x=null){var S;if(!(t.isDirty&&t.currentFileHandle&&!confirm("You have unsaved changes. Discard them?"))){H(),y();try{let L=x||((S=t.notes.find($=>$.relPath===f))==null?void 0:S.handle);if(!L)throw new Error("File not found");let C=await(await L.getFile()).text();t.currentFileHandle=L,t.currentRelPath=f,t.currentContent=C,t.isDirty=!1,e.editor.value=C,i(e,C),o(e,t,r),r("Opened \u2713","ok"),await a()}catch(L){let M=L instanceof Error?L.message:String(L);n(`Failed to open note: ${M}`,{persist:!0}),r("Open failed","err")}}}async function _(){if(t.isTemporarySession)return pe();if(t.currentFileHandle){H(),y();try{let f=await t.currentFileHandle.createWritable();await f.write(e.editor.value),await f.close(),t.currentContent=e.editor.value,t.isDirty=!1,o(e,t,r),r("Saved \u2713","ok"),n("Saved \u2713"),await v({silent:!0})}catch(f){let x=f instanceof Error?f.message:String(f);n(`Save failed: ${x}`,{persist:!0}),r("Save failed","err")}}}async function z(){if(!t.workspaceHandle&&!t.isTemporarySession){n("Open a workspace first.");return}if(t.isDirty&&!confirm("You have unsaved changes. Continue and discard them?"))return;let f=prompt("New note name (without .md)");if(!f)return;let x=f.endsWith(".md")?f:`${f}.md`;if(t.isTemporarySession)return I(x,f);try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");let S=t.workspaceHandle;for await(let[$]of S.entries())if($===x){n("A file with that name already exists.",{persist:!0});return}let L=await t.workspaceHandle.getFileHandle(x,{create:!0}),M=`# ${f} Created ${new Date().toLocaleString()} -`,R=await T.createWritable();await R.write(E),await R.close(),await g({silent:!0}),await y(L,T),n("New note created \u2713"),r("New note","ok")}catch(v){let T=v instanceof Error?v.message:String(v);n(`Failed to create note: ${T}`,{persist:!0}),r("Create failed","err")}}async function I(){if(!t.workspaceHandle&&!t.isTemporarySession){n("Open a workspace first.");return}let k=t.isTemporarySession?e.editor.value:t.currentContent;if(!k){n("Nothing to save.");return}let L=prompt("Save note as (filename without .md):");if(!L)return;let v=L.endsWith(".md")?L:`${L}.md`;if(t.isTemporarySession)return D(v,L);try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");for await(let[R]of t.workspaceHandle.entries())if(R===v){n("A file with that name already exists.",{persist:!0});return}let T=await t.workspaceHandle.getFileHandle(v,{create:!0}),E=await T.createWritable();await E.write(k),await E.close(),await g({silent:!0}),await y(v,T),n(`Saved as ${v} \u2713`),r("Saved as","ok")}catch(T){let E=T instanceof Error?T.message:String(T);n(`Failed to save: ${E}`,{persist:!0}),r("Save failed","err")}}async function Q(k=t.workspaceHandle){if(!k){if(t.isTemporarySession){n("Folders are not supported in temporary session.");return}n("Open a workspace first.");return}let L=prompt("Folder name:");if(L)try{await k.getDirectoryHandle(L,{create:!0}),await g({silent:!0}),n("Folder created \u2713"),r("Folder created","ok")}catch(v){let T=v instanceof Error?v.message:String(v);n(`Failed to create folder: ${T}`,{persist:!0}),r("Create folder failed","err")}}async function D(k,L){if(t.inMemoryNotes.find(R=>R.relPath===k)){n("A note with that name already exists.",{persist:!0});return}let T=`# ${L} +`,C=await L.createWritable();await C.write(M),await C.close(),await v({silent:!0}),await D(x,L),n("New note created \u2713"),r("New note","ok")}catch(S){let L=S instanceof Error?S.message:String(S);n(`Failed to create note: ${L}`,{persist:!0}),r("Create failed","err")}}async function R(){if(!t.workspaceHandle&&!t.isTemporarySession){n("Open a workspace first.");return}let f=t.isTemporarySession?e.editor.value:t.currentContent;if(!f){n("Nothing to save.");return}let x=prompt("Save note as (filename without .md):");if(!x)return;let S=x.endsWith(".md")?x:`${x}.md`;if(t.isTemporarySession)return I(S,x);try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");for await(let[C]of t.workspaceHandle.entries())if(C===S){n("A file with that name already exists.",{persist:!0});return}let L=await t.workspaceHandle.getFileHandle(S,{create:!0}),M=await L.createWritable();await M.write(f),await M.close(),await v({silent:!0}),await D(S,L),n(`Saved as ${S} \u2713`),r("Saved as","ok")}catch(L){let M=L instanceof Error?L.message:String(L);n(`Failed to save: ${M}`,{persist:!0}),r("Save failed","err")}}async function J(f=t.workspaceHandle){if(!f){if(t.isTemporarySession){n("Folders are not supported in temporary session.");return}n("Open a workspace first.");return}let x=prompt("Folder name:");if(x)try{await f.getDirectoryHandle(x,{create:!0}),await v({silent:!0}),n("Folder created \u2713"),r("Folder created","ok")}catch(S){let L=S instanceof Error?S.message:String(S);n(`Failed to create folder: ${L}`,{persist:!0}),r("Create folder failed","err")}}async function I(f,x){if(t.inMemoryNotes.find(C=>C.relPath===f)){n("A note with that name already exists.",{persist:!0});return}let L=`# ${x} Created ${new Date().toLocaleString()} -`,E={name:k,relPath:k,content:T,lastModified:Date.now(),tags:f(T)};t.inMemoryNotes.push(E),t.currentRelPath=k,t.currentContent=T,t.isDirty=!1,e.editor.value=T,i(e,T),o(e,t,r),s(),l(),n("New note created \u2713"),r("New note","ok")}async function J(k){let L=k.title.trim(),v=k.body.trim(),T=k.tag.trim();if(!L||!v){let W="Title and body are required.";return n(W,{persist:!0}),r("Create failed","err"),{ok:!1,message:W}}!t.workspaceHandle&&!t.isTemporarySession&&(p(),n("Temporary in-memory workspace enabled for note creation.",{persist:!0}),r("Temporary session","warn"));let E=b(L),R=m({title:L,body:v,tag:T}),B=t.isDirty,ie=B?`Created ${E}. Current unsaved note was left open.`:`Created ${E} \u2713`;if(t.isTemporarySession){if(t.inMemoryNotes.find(X=>X.relPath===E)){let X="A note with that name already exists.";return n(X,{persist:!0}),r("Create failed","err"),{ok:!1,message:X}}let V={name:E,relPath:E,content:R,lastModified:Date.now(),tags:f(R)};return t.inMemoryNotes.push(V),B||(t.currentRelPath=E,t.currentContent=R,t.isDirty=!1,e.editor.value=R,i(e,R),o(e,t,r)),s(),c(),l(),n(ie),r("New note","ok"),{ok:!0,message:ie,notePath:E,sessionType:"temporary",keptCurrentNote:B}}try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");for await(let[X]of t.workspaceHandle.entries())if(X===E){let Z="A file with that name already exists.";return n(Z,{persist:!0}),r("Create failed","err"),{ok:!1,message:Z}}let W=await t.workspaceHandle.getFileHandle(E,{create:!0}),V=await W.createWritable();return await V.write(R),await V.close(),await g({silent:!0}),B?r("New note","ok"):await y(E,W),n(ie),{ok:!0,message:ie,notePath:E,sessionType:"workspace",keptCurrentNote:B}}catch(W){let V=W instanceof Error?W.message:String(W);return n(`Failed to create note: ${V}`,{persist:!0}),r("Create failed","err"),{ok:!1,message:V}}}async function te(k){if(t.isDirty&&t.currentRelPath&&!confirm("You have unsaved changes. Discard them?"))return;let L=t.inMemoryNotes.find(v=>v.relPath===k);if(!L){n("Note not found",{persist:!0});return}t.currentRelPath=k,t.currentContent=L.content,t.isDirty=!1,e.editor.value=L.content,i(e,L.content),o(e,t,r),r("Opened \u2713","ok"),s()}async function K(){if(!t.currentRelPath||!t.isTemporarySession)return;let k=t.inMemoryNotes.find(L=>L.relPath===t.currentRelPath);if(!k){n("Note not found",{persist:!0});return}k.content=e.editor.value,k.lastModified=Date.now(),k.tags=f(k.content),t.currentContent=k.content,t.isDirty=!1,o(e,t,r),r("Saved \u2713","ok"),n("Saved \u2713"),s(),c()}function ne(){if(t.inMemoryNotes.length===0){n("No notes to export.");return}let k={exportedAt:new Date().toISOString(),notes:t.inMemoryNotes.map(B=>({name:B.name,path:B.relPath,content:B.content,lastModified:new Date(B.lastModified).toISOString()}))},L=new Blob([JSON.stringify(k,null,2)],{type:"application/json"}),v=URL.createObjectURL(L),E=`ink-export-${new Date().toISOString().split("T")[0]}.json`,R=document.createElement("a");R.href=v,R.download=E,document.body.appendChild(R),R.click(),document.body.removeChild(R),URL.revokeObjectURL(v),n(`Exported ${t.inMemoryNotes.length} note(s) as JSON.`),r("Exported JSON","ok")}function re(){if(!t.currentRelPath){n("No note selected to export.");return}let k=t.inMemoryNotes.find(E=>E.relPath===t.currentRelPath);if(!k){n("Note not found.",{persist:!0});return}let L=new Blob([k.content],{type:"text/markdown"}),v=URL.createObjectURL(L),T=document.createElement("a");T.href=v,T.download=k.name,document.body.appendChild(T),T.click(),document.body.removeChild(T),URL.revokeObjectURL(v),n(`Exported ${k.name} as Markdown.`),r("Exported MD","ok")}function Y(){if(t.isTemporarySession){n("Refresh not available in temporary session. Your data is in memory.");return}if(!t.workspaceHandle){n("No workspace open.");return}g().catch(k=>{n(`Refresh failed: ${String(k)}`,{persist:!0}),r("Refresh failed","err")})}function H(){h.stopAutoRefresh(),t.workspaceHandle=null,t.workspaceName="",t.fileTree=null,t.notes=[],t.currentFileHandle=null,t.currentRelPath="",t.currentContent="",t.isDirty=!1,t.searchQuery="",t.tagFilter="",t.collapsedDirs.clear(),e.workspaceName.textContent="No folder selected",e.workspaceName.title="No folder selected",e.countsPill.textContent="0 notes",e.tagRow.innerHTML="",e.tree.innerHTML='
Open a folder to begin.
',e.editor.value="",e.preview.innerHTML="",e.currentFilename.textContent="No note open",e.dirtyDot.classList.remove("show"),r("Ready"),n("Workspace closed.")}return{openWorkspace:w,rescanWorkspace:g,openNoteByRelPath:y,saveCurrentNote:N,createNewNote:P,saveAsNewNote:I,createNoteFromTool:J,createNewFolder:Q,createInMemoryNote:D,openInMemoryNote:te,saveInMemoryNote:K,exportAsJson:ne,exportAsMarkdown:re,handleRefresh:Y,closeWorkspace:H}}var $r=`You are a writing coach. +`,M={name:f,relPath:f,content:L,lastModified:Date.now(),tags:m(L)};t.inMemoryNotes.push(M),t.currentRelPath=f,t.currentContent=L,t.isDirty=!1,e.editor.value=L,i(e,L),o(e,t,r),s(),l(),n("New note created \u2713"),r("New note","ok")}async function K(f){let x=f.title.trim(),S=f.body.trim(),L=f.tag.trim();if(!x||!S){let O="Title and body are required.";return n(O,{persist:!0}),r("Create failed","err"),{ok:!1,message:O}}!t.workspaceHandle&&!t.isTemporarySession&&(V(),n("Temporary in-memory workspace enabled for note creation.",{persist:!0}),r("Temporary session","warn"));let M=re(x),C=oe({title:x,body:S,tag:L}),$=t.isDirty,W=$?`Created ${M}. Current unsaved note was left open.`:`Created ${M} \u2713`;if(t.isTemporarySession){if(t.inMemoryNotes.find(j=>j.relPath===M)){let j="A note with that name already exists.";return n(j,{persist:!0}),r("Create failed","err"),{ok:!1,message:j}}let U={name:M,relPath:M,content:C,lastModified:Date.now(),tags:m(C)};return t.inMemoryNotes.push(U),$||(t.currentRelPath=M,t.currentContent=C,t.isDirty=!1,e.editor.value=C,i(e,C),o(e,t,r)),s(),c(),l(),n(W),r("New note","ok"),{ok:!0,message:W,notePath:M,sessionType:"temporary",keptCurrentNote:$}}try{if(!t.workspaceHandle)throw new Error("Workspace handle is not available");for await(let[j]of t.workspaceHandle.entries())if(j===M){let ie="A file with that name already exists.";return n(ie,{persist:!0}),r("Create failed","err"),{ok:!1,message:ie}}let O=await t.workspaceHandle.getFileHandle(M,{create:!0}),U=await O.createWritable();return await U.write(C),await U.close(),await v({silent:!0}),$?r("New note","ok"):await D(M,O),n(W),{ok:!0,message:W,notePath:M,sessionType:"workspace",keptCurrentNote:$}}catch(O){let U=O instanceof Error?O.message:String(O);return n(`Failed to create note: ${U}`,{persist:!0}),r("Create failed","err"),{ok:!1,message:U}}}async function ee(f){if(t.isDirty&&t.currentRelPath&&!confirm("You have unsaved changes. Discard them?"))return;let x=t.inMemoryNotes.find(S=>S.relPath===f);if(!x){n("Note not found",{persist:!0});return}t.currentRelPath=f,t.currentContent=x.content,t.isDirty=!1,e.editor.value=x.content,i(e,x.content),o(e,t,r),r("Opened \u2713","ok"),s()}async function pe(){if(!t.currentRelPath||!t.isTemporarySession)return;let f=t.inMemoryNotes.find(x=>x.relPath===t.currentRelPath);if(!f){n("Note not found",{persist:!0});return}f.content=e.editor.value,f.lastModified=Date.now(),f.tags=m(f.content),t.currentContent=f.content,t.isDirty=!1,o(e,t,r),r("Saved \u2713","ok"),n("Saved \u2713"),s(),c()}function he(){if(t.inMemoryNotes.length===0){n("No notes to export.");return}let f={exportedAt:new Date().toISOString(),notes:t.inMemoryNotes.map($=>({name:$.name,path:$.relPath,content:$.content,lastModified:new Date($.lastModified).toISOString()}))},x=new Blob([JSON.stringify(f,null,2)],{type:"application/json"}),S=URL.createObjectURL(x),M=`ink-export-${new Date().toISOString().split("T")[0]}.json`,C=document.createElement("a");C.href=S,C.download=M,document.body.appendChild(C),C.click(),document.body.removeChild(C),URL.revokeObjectURL(S),n(`Exported ${t.inMemoryNotes.length} note(s) as JSON.`),r("Exported JSON","ok")}function Cn(){if(!t.currentRelPath){n("No note selected to export.");return}let f=t.inMemoryNotes.find(M=>M.relPath===t.currentRelPath);if(!f){n("Note not found.",{persist:!0});return}let x=new Blob([f.content],{type:"text/markdown"}),S=URL.createObjectURL(x),L=document.createElement("a");L.href=S,L.download=f.name,document.body.appendChild(L),L.click(),document.body.removeChild(L),URL.revokeObjectURL(S),n(`Exported ${f.name} as Markdown.`),r("Exported MD","ok")}function Nn(){if(t.isTemporarySession){n("Refresh not available in temporary session. Your data is in memory.");return}if(!t.workspaceHandle){n("No workspace open.");return}H(),y(),v().catch(f=>{n(`Refresh failed: ${String(f)}`,{persist:!0}),r("Refresh failed","err")})}function $n(){h.stopAutoRefresh(),H(),y(),t.workspaceHandle=null,t.workspaceName="",t.fileTree=null,t.notes=[],t.currentFileHandle=null,t.currentRelPath="",t.currentContent="",t.isDirty=!1,t.searchQuery="",t.tagFilter="",t.collapsedDirs.clear(),e.workspaceName.textContent="No folder selected",e.workspaceName.title="No folder selected",e.countsPill.textContent="0 notes",e.tagRow.innerHTML="",e.tree.innerHTML='
Open a folder to begin.
',e.editor.value="",e.preview.innerHTML="",e.currentFilename.textContent="No note open",e.dirtyDot.classList.remove("show"),r("Ready"),n("Workspace closed.")}return{openWorkspace:T,rescanWorkspace:v,openNoteByRelPath:D,saveCurrentNote:_,createNewNote:z,saveAsNewNote:R,createNoteFromTool:K,createNewFolder:J,createInMemoryNote:I,openInMemoryNote:ee,saveInMemoryNote:pe,exportAsJson:he,exportAsMarkdown:Cn,handleRefresh:Nn,closeWorkspace:$n}}var qr=`You are a writing coach. Rules: - Do NOT write prose. - Do NOT suggest sentences. - Ask exactly 3 questions. - Questions must be grounded in the user's last sentence. +- Use document analysis only to focus what the questions explore. - Output JSON only in this format: { "questions": ["...", "...", "..."] -}`,Nr="Llama-3.2-1B-Instruct-q4f32_1-MLC",Pr="Qwen3-8B-q4f16_1-MLC";function Dr(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=e.split(/(?<=[.!?])\s+/).map(r=>r.trim()).filter(Boolean);return n.length===0?"":n[n.length-1]}function Br(t){let e=t.replace(/[\s\S]*?<\/think>/gi,"").replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/,"").trim(),n=JSON.parse(e);if(!n||!Array.isArray(n.questions))throw new Error("Cogito response did not include a questions array.");let r=n.questions.filter(i=>typeof i=="string"&&i.trim().length>0).map(i=>i.trim());if(r.length===0)throw new Error("Cogito response contained no valid questions.");if(r.length!==3)throw new Error("Cogito response must contain exactly 3 questions.");return r}function Ir(t){return`> ### AI +}`,jr="Llama-3.2-1B-Instruct-q4f32_1-MLC",Qr="Qwen3-8B-q4f16_1-MLC";function ye(t,e,n=!1){let r=t.replace(/\s+/g," ").trim();return r.length<=e?r:n?`\u2026${r.slice(-(e-1))}`:`${r.slice(0,e-1)}\u2026`}function Xe(t){let e=t instanceof Error?t.message:String(t);return/cache|network|fetch|failed to execute ['"]?add|load failed|connection/i.test(e)}function Vr(t){return new Promise(e=>{setTimeout(e,t)})}function Ur(t){return t?{overallScore:t.overallScore,priorities:t.overview.priorities.slice(0,3).map(e=>ye(e,240)),strengths:t.overview.strengths.slice(0,2).map(e=>ye(e,240))}:null}function Gr(t,e){let r=[`Last sentence: ${ye(t,1200,!0)}`];if(!e)return r.push("Document analysis: unavailable. Focus only on the last sentence."),r.join(` +`);let i=e.priorities.slice(0,3).map(a=>ye(a,240)),o=e.strengths.slice(0,2).map(a=>ye(a,240));return r.push(`Document strength: ${e.overallScore}/100`,`Highest-priority improvements: ${i.join(" | ")||"No major fixes identified."}`,`Current strengths: ${o.join(" | ")||"No clear strengths identified yet."}`),r.join(` +`)}function Kr(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=e.split(/(?<=[.!?])\s+/).map(r=>r.trim()).filter(Boolean);return n.length===0?"":n[n.length-1]}function Zr(t){let e=t.replace(/[\s\S]*?<\/think>/gi,"").replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/,"").trim(),n=JSON.parse(e);if(!n||!Array.isArray(n.questions))throw new Error("Cogito response did not include a questions array.");let r=n.questions.filter(i=>typeof i=="string"&&i.trim().length>0).map(i=>i.trim());if(r.length===0)throw new Error("Cogito response contained no valid questions.");if(r.length!==3)throw new Error("Cogito response must contain exactly 3 questions.");return r}function Yr(t){return`> ### AI ${t.trim()} -`}function Hr(t,e){let{selectionStart:n,selectionEnd:r,value:i}=t,o=i.slice(0,n),a=i.slice(r);t.value=`${o}${e}${a}`;let s=o.length+e.length;t.setSelectionRange(s,s)}function Ht({els:t,getEditorText:e,onEditorContentReplaced:n,showToast:r,setStatus:i}){let o=!1,a=[],s="lite",c={};async function l(){let w=globalThis.__INK_TEST_WEBLLM__;return w||import("https://esm.run/@mlc-ai/web-llm")}function d(w){s=w,t.cogitoLiteBtn.classList.toggle("active",w==="lite"),t.cogitoDeepBtn.classList.toggle("active",w==="deep")}function f(w){o=w,t.cogitoPanel.hidden=!w,t.cogitoToggleBtn.setAttribute("aria-expanded",String(w));let g=t.cogitoPanel.closest(".split");g&&g.classList.toggle("with-cogito",w)}function u(w){t.cogitoQuestionList.innerHTML="",w.forEach((g,S)=>{let x=document.createElement("li");x.className="cogitoQuestionItem";let y=document.createElement("p");y.className="cogitoQuestionText",y.textContent=g;let N=document.createElement("button");N.type="button",N.className="ghost cogitoInsertBtn",N.dataset.questionIndex=String(S),N.textContent="Insert",N.title="Insert question into markdown",x.append(y,N),t.cogitoQuestionList.appendChild(x)})}function h(w){t.cogitoStatus.textContent=w}async function p(){let w=s==="deep"?Pr:Nr;return c[s]||(c[s]=(async()=>(h(`Loading ${s==="deep"?"Deep (Qwen3 8B)":"Lite (Llama 1B)"} model...`),await(await l()).CreateMLCEngine(w,{initProgressCallback:x=>{x!=null&&x.text&&h(x.text)}})))().catch(g=>{throw delete c[s],g})),c[s]}async function b(){var g,S,x;let w=Dr(e());if(!w){h("Write at least one sentence first, then generate Cogito questions."),i("Cogito needs a sentence","warn");return}try{t.cogitoGenerateBtn.disabled=!0,h("Generating 3 questions...");let P=(x=(S=(g=(await(await p()).chat.completions.create({messages:[{role:"system",content:$r},{role:"user",content:`Last sentence: ${w}`}],temperature:.2})).choices)==null?void 0:g[0])==null?void 0:S.message)==null?void 0:x.content,I=Array.isArray(P)?P.map(Q=>typeof Q=="string"?Q:"").join("").trim():typeof P=="string"?P.trim():"";if(!I)throw new Error("Cogito returned an empty response.");a=Br(I),u(a),h("Questions ready. Insert one into your markdown when useful."),i("Cogito questions ready","ok")}catch(y){a=[],u(a);let N=y instanceof Error?y.message:String(y);h(`Cogito error: ${N}`),i("Cogito unavailable","warn"),r(`Cogito failed: ${N}`,{persist:!0})}finally{t.cogitoGenerateBtn.disabled=!1}}function m(w){let g=a[w];if(!g){r("Cogito question not found.",{persist:!0});return}let S=Ir(g);Hr(t.editor,S),n(t.editor.value),i("Inserted AI question","ok")}return f(!1),{togglePanel:()=>{f(!o),o&&h("Cogito Mode enabled. Generate questions from your last sentence.")},setPanelOpen:w=>{f(w),w&&h("Cogito Mode enabled. Generate questions from your last sentence.")},isPanelOpen:()=>o,closePanel:()=>{f(!1)},selectModel:d,generateQuestions:b,insertQuestionAtIndex:m}}var Ft="No clear strengths stand out yet; the draft needs more signal before the linter can praise specific choices.",Ue="No major fixes stood out.",Ge="No notable issues in this category.",Ke="This note did not expose any obvious structural sections.";function zt(){return"The note opens cleanly, but it still needs a visible structure signal."}function Ze(){return"The first sentence does a lot of work. Tighten it and let the rest of the section carry the supporting detail."}function _t(){return"The opening is understandable, but it could be shaped into a sharper lead sentence."}function Je(){return"Lead with the payoff or takeaway first, then use the topic sentence to support it."}function Wt(){return"There are real strengths here: concrete details and structure cues give the document memory and shape."}function Ot(){return"The content is solid, but it needs a clearer scaffold so the reader can move through it faster."}function qt(){return"The draft is too thin or fragmentary to score well yet; add a clearer claim and one or two supporting details."}function jt(){return"Add a heading at the top so the chapter reads as a structured note instead of a raw outline."}function Qt(t){return`Turn "${t}" into a heading so the bullet list has a clean anchor.`}function Vt(){return"The bullet list has good content, but several bullets carry more than one idea. Consider grouping them by theme or splitting the longest ones."}function Ut(){return"This would scan better with three visible beats: a short lead, a grouped facts section, and a closing takeaway."}function Gt(){return"The document's logic is good, but the middle section is carrying too much at once. A mid-document heading would make the progression easier to follow."}function Kt(){return"This section leans on a few abstract nouns. It still reads clearly, but a couple of them could be replaced with plainer words."}function Ye(){return"A word is repeated back-to-back. Clean that up before worrying about higher-level style."}function Xe(){return"There is not enough developed prose yet to judge the document fairly. Add a clearer point and at least one supporting sentence."}function Zt(){return"This section is too thin to evaluate well; add a clearer claim or supporting detail."}function Jt(){return"A question can be a good hook, but it needs an immediate answer or payoff."}function Yt(){return"This section is balanced and easy to follow."}function Xt(t){return`The "${t}" block introduces a real section. Promoting it to a heading would make the document easier to scan.`}function en(t,e){return`The section starting at line ${t} has ${e} bullets and several carry more than one idea.`}function tn(t){return`The section starting at line ${t} is carrying a lot of text and would be easier to scan if it were split.`}function nn(t){return`Turn "${t}:" into a heading so the section reads as intentional structure.`}function rn(){return"This bullet run is dense; split the longest items or group them by theme."}function on(){return"This section carries a lot of material; consider splitting it into two smaller beats."}function sn(){return"The heading works, but this section is list-only; a short lead sentence could help orient the reader."}function an(t,e){return`The "${t}" section deserves attention: ${e.toLowerCase()}`}function ln(){return"Concrete anchors such as names, dates, or numbers make the material easier to remember."}function cn(){return"The document gives the reader a memory hook, which makes the takeaway easier to retain."}function un(){return"The parallel list structure creates a strong rhythm and makes the sequence easy to scan."}function dn(){return"The opening question creates forward pull and gives the reader a reason to keep going."}function pn(){return"The lead uses clear directive language, which gives the document momentum."}function hn(){return"The document already has enough visible structure that a reader can scan it quickly."}var wn=[{id:"readability",title:"Readability",color:"#4CAF50"},{id:"skimmability",title:"Skimmability",color:"#2196F3"},{id:"engagement",title:"Engagement",color:"#FF9800"},{id:"style",title:"Style",color:"#9C27B0"},{id:"structure",title:"Structure",color:"#607D8B"}];function ce(t){return Math.max(0,Math.min(100,t))}function _(t){var e,n;return(n=(e=t.match(/\b\w+\b/g))==null?void 0:e.length)!=null?n:0}function Me(t){return t.replaceAll(/\s+/g," ").split(/(?<=[.!?])\s+/).map(e=>e.trim()).filter(Boolean)}function Fr(t){return Me(t).length}function j(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function gn(t){return t==="high"?3:t==="medium"?2:1}function zr(t,e=`Line ${t}`){return``}function et(t){return/^```/.test(t.trim())}function mn(t){return/^#{1,6}\s+/.test(t.trim())}function _r(t){return/^\s*\d+[.)]\s+/.test(t)}function Wr(t){return/^\s*[-*+]\s+/.test(t)}function tt(t){return Wr(t)||_r(t)}function Te(t){return/^\s*>\s?/.test(t)}function nt(t){return/^(?:\t| {4,})/.test(t)}function bn(t,e){return t.type==="paragraph"&&/:\s*$/.test(t.text)&&(e==null?void 0:e.type)==="list_item"?t.text.replace(/:\s*$/,""):null}function fn(t){return t.replace(/^(\s*(?:[-*+]|\d+[.)]))\s+.*$/,"$1")}function kn(t){return t.replace(/^\s*(?:[-*+]|\d+[.)])\s+/,"").trim()}function Or(t){return t.map(e=>e.trim()).join(" ").replaceAll(/\s+/g," ").trim()}function yn(t){let e=t.split(` -`),n=[],r=0;for(;r\s?/,"").trim()),r+=1;continue}if(f.trim()===""&&r+1n.type===e)}function rt(t){return t.filter(e=>e.type==="paragraph"||e.type==="blockquote")}function xn(t){let e=t.match(/\b([a-z][a-z'-]{1,})\b(?:\s+\1\b)/i);return!e||typeof e.index!="number"?null:{word:e[1],index:e.index}}function F(t,e,n,r,i,o=!1){return{category:t,severity:e,title:n,detail:r,section:i,isStrength:o}}function qr(t){let e=t.find(n=>n.type==="paragraph");return e?_(e.text)>=22?Ze():/^(this|these)\b/i.test(e.text)?Je():_t():zt()}function jr(t){for(let e=0;ed.title==="Repeated word")||n.push(F("readability","medium","Repeated word",Ye(),`Line ${s.lineStart}`)));for(let d of c){let f=_(d),u=d.length;if((f>=24||u>=140)&&(r+=1,n.length<3)){let h=d.length>70?`${d.slice(0,70)}...`:d;n.push(F("readability","medium","Dense sentence",`This sentence is carrying too much at once: "${h}". Split the idea or move the setup into a heading.`,`Line ${s.lineStart}`))}}}let o=e[0];return o&&_(o.text)>=22&&n.unshift(F("readability","high","Opening is dense",Ze(),`Line ${o.lineStart}`)),{result:{score:ce(100-r*14-i*10-(o&&_(o.text)>=22?8:0)),suggestions:n.map(s=>`${s.title}: ${s.detail}`)},findings:n}}function Ur(t){let e=le(t,"heading"),n=le(t,"list_item"),r=jr(t),i=[];return e.length===0&&i.push(F("skimmability","high","Missing heading",jt(),"Document")),r&&i.push(F("skimmability","medium","Make the section label explicit",Qt(r.text),`Line ${r.lineStart}`)),n.length>=4&&n.reduce((s,c)=>s+_(c.text),0)/n.length>=11&&i.push(F("skimmability","medium","Bullets are dense",Vt(),"Bullet list")),{result:{score:ce(100-(e.length===0?20:0)-(r?6:0)-(n.length>=4?10:0)),suggestions:i.map(a=>`${a.title}: ${a.detail}`)},findings:i,pseudoSectionLabel:r}}function Gr(t){var P,I,Q,D,J,te,K,ne,re,Y;let e=t.map(H=>H.text).join(" "),n=t.find(H=>H.type==="paragraph"),r=(P=n==null?void 0:n.text)!=null?P:"",i=[],o=[],a=_(e),s=(I=Me(r)[0])!=null?I:r,c=_(s),l=/\?\s*$/.test(s),d=/^(remember|consider|notice|start|imagine|picture|look|think)\b/i.test(s),f=(Q=e.match(/\b(?:was|were|is|are|be|been|being)\s+\w+ed\b/gi))!=null?Q:[],u=Me(e),h=u.length>0?f.length/u.length:0,p=new Set(((D=e.toLowerCase().match(/\b[a-z][a-z'-]+\b/g))!=null?D:[]).filter(H=>H.length>=4)),b=_(e)>0?p.size/_(e):0,m=(J=e.match(/\b(?:remember|consider|notice|focus|compare|look|keep|start)\b/gi))!=null?J:[],w=(te=e.match(/\b(?:\d{2,4}|[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b/g))!=null?te:[],g=(K=e.match(/\b(?:remember|key takeaway|simple way to remember|think of|in short)\b/gi))!=null?K:[],S=le(t,"list_item").map(H=>H.text),x=S.map(H=>{var k,L;return((L=(k=H.match(/^[A-Za-z]+/))==null?void 0:k[0])!=null?L:"").toLowerCase()}).filter(Boolean),y=x.find((H,k)=>H&&x.indexOf(H)!==k);return/^(this|these)\b/i.test(s)&&c>=8&&i.push(F("engagement","medium","Opening is informative, not directive",Je(),`Line ${(ne=n==null?void 0:n.lineStart)!=null?ne:1}`)),a>0&&a<12&&i.push(F("engagement","high","Too little context",Xe(),`Line ${(re=n==null?void 0:n.lineStart)!=null?re:1}`)),l&&a<18&&i.push(F("engagement","medium","Question needs payoff",Jt(),`Line ${(Y=n==null?void 0:n.lineStart)!=null?Y:1}`)),l&&o.push(dn()),(d||m.length>=2)&&o.push(pn()),g.length>0&&o.push(cn()),w.length>=2&&o.push(ln()),y&&S.length>=3&&o.push(un()),o.length===0&&(le(t,"heading").length>0||S.length>=3)&&o.push(hn()),{result:{score:ce(48+(l?10:0)+(d?10:0)+Math.min(o.length,3)*8+(b>=.28?6:0)+(c>0&&c<=16?6:0)-(a>0&&a<12?18:0)-(h>=.4?12:0)-i.length*8),suggestions:i.map(H=>`${H.title}: ${H.detail}`)},findings:i,strengths:o}}function Kr(t){var s;let e=rt(t).map(c=>c.text).join(" "),n=(s=e.match(/\b\w+(?:tion|sion|ment|ness|ance|ence)\b/gi))!=null?s:[],r=[],i=xn(e),o=[...new Set(n.map(c=>c.toLowerCase()))];return o.length>=4&&r.push(F("style","low","Dense abstract language",Kt(),"Document")),i&&r.unshift(F("style","medium","Repeated word",Ye(),"Document")),{result:{score:ce(96-Math.min(o.length,4)*2-(i?14:0)),suggestions:r.map(c=>`${c.title}: ${c.detail}`)},findings:r}}function Zr(t){let e=le(t,"heading"),n=le(t,"list_item"),r=le(t,"paragraph"),i=[],o=_(t.map(s=>s.text).join(" "));return e.length===0&&r.length>0&&n.length>0&&i.push(F("structure","high","Add a simple outline",Ut(),"Document")),n.length>=5&&e.length===0&&i.push(F("structure","medium","Break up the middle",Gt(),"Bullet list")),o>0&&o<12&&i.push(F("structure","high","Draft is too thin",Xe(),"Document")),{result:{score:ce(86-(e.length===0?12:0)-(n.length>=5?8:0)-(o>0&&o<12?22:0)),suggestions:i.map(s=>`${s.title}: ${s.detail}`)},findings:i}}function Jr(t){let e=[],n=t.map(r=>{let i=r.blocks.map(f=>f.text).join(" "),o=r.blocks.filter(f=>f.type==="list_item"),a=r.blocks.filter(f=>f.type==="paragraph"||f.type==="blockquote"),s=_(i),c=o.length>0?o.reduce((f,u)=>f+_(u.text),0)/o.length:0,l=[],d=!1;return r.kind==="label"&&(l.push(nn(r.title)),d=!0,e.push(F("structure","medium","Convert label into heading",Xt(r.title),`Line ${r.lineStart}`))),o.length>=4&&c>=11&&(l.push(rn()),d=!0,e.push(F("structure","medium","Dense bullet section",en(r.lineStart,o.length),`Line ${r.lineStart}`))),s>=90&&a.length>=2&&(l.push(on()),d=!0,e.push(F("structure","low","Long section",tn(r.lineStart),`Line ${r.lineStart}`))),r.kind==="heading"&&o.length>0&&a.length===0&&l.push(sn()),s>0&&s<12&&a.length>0&&(l.push(Zt()),d=!0),l.length===0&&l.push(Yt()),{title:r.title,kind:r.kind,lineStart:r.lineStart,lineEnd:r.lineEnd,notes:l,needsAttention:d}});return{findings:e,sectionNotes:n}}function Yr(t,e,n,r){let i=_(n.map(c=>c.text).join(" ")),o=t.filter(c=>!c.isStrength).slice().sort((c,l)=>gn(l.severity)-gn(c.severity)).slice(0,4).map(c=>`${c.title} \u2014 ${c.detail}`),a=[qr(n)],s=r.find(c=>c.needsAttention);return s&&a.push(an(s.title,s.notes[0])),e.length>0?a.push(Wt()):i>0&&i<12?a.push(qt()):a.push(Ot()),{quickTake:a,priorities:o,strengths:e.length>0?e:[Ft]}}function Xr(t){let e={readability:.25,skimmability:.2,engagement:.2,style:.15,structure:.2},n=Object.entries(t).reduce((r,[i,o])=>r+o.score*e[i],0);return ce(Math.round(n))}function ei(t,e,n){if(t.scrollTop=e,!n||typeof t.querySelector!="function")return;let r=t.querySelector(`[data-linter-focus-key="${n}"]`);r==null||r.focus()}function ti(t){return typeof t=="object"&&t!==null&&"getAttribute"in t}function ni(t){let e=yn(t),n=Qr(e),r=Vr(e),i=Ur(e),o=Gr(e),a=Kr(e),s=Zr(e),c=Jr(n),l=[...r.findings,...i.findings,...o.findings,...a.findings,...s.findings,...c.findings],d=[{title:"Readability",lines:r.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Skimmability",lines:i.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Engagement",lines:o.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Style",lines:a.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Structure",lines:s.findings.map(u=>`- ${u.title}: ${u.detail}`)}],f={readability:r.result,skimmability:i.result,engagement:o.result,style:a.result,structure:s.result};return{scores:f,overallScore:Xr(f),overview:Yr(l,o.strengths,e,c.sectionNotes),sections:d,documentSections:c.sectionNotes}}function ri(t,e){let n=yn(t),r=_(t),i=Fr(rt(n).map(c=>c.text).join(" ")),o=wn.map(c=>{var d;let l=e.sections.find(f=>f.title===c.title);return{title:c.title,lines:(d=l==null?void 0:l.lines)!=null&&d.length?l.lines:[`- ${Ge}`]}}),a=e.documentSections.length>0?e.documentSections:[{title:"Document",kind:"implicit",lineStart:1,lineEnd:1,notes:[Ke],needsAttention:!1}];return["# Document Linter Review","","## Overall",`- Overall score: ${e.overallScore}/100`,"","## Quick take",...e.overview.quickTake.map(c=>`- ${c}`),"","## What to fix first",...e.overview.priorities.length>0?e.overview.priorities.map((c,l)=>`${l+1}. ${c}`):[`- ${Ue}`],"","## What is working",...e.overview.strengths.map(c=>`- ${c}`),"","## Section notes",...o.flatMap(c=>[`### ${c.title}`,...c.lines,""]),"## Section analysis",...a.flatMap(c=>[`### ${c.title}`,`- Type: ${c.kind}`,`- Lines: ${c.lineStart}\u2013${c.lineEnd}`,`- Needs attention: ${c.needsAttention?"yes":"no"}`,...c.notes.map(l=>`- ${l}`),""]),"## Snapshot",`- Words: ${r}`,`- Sentences: ${i}`,`- Blocks: ${n.length}`].join(` -`)}function vn({els:t,getEditorText:e,onEditorContentReplaced:n,showToast:r,setStatus:i}){let o=!1,a=!1,s=!1,c=null,l="";async function d(g=e()){return Promise.resolve(ni(g))}function f(g){let S=t.editor.value.split(` -`),x=Math.max(1,Math.min(g,S.length||1)),y=0;for(let I=0;I{let D=document.createElement("li");D.className="cogitoQuestionItem";let _=document.createElement("p");_.className="cogitoQuestionText",_.textContent=A;let z=document.createElement("button");z.type="button",z.className="ghost cogitoInsertBtn",z.dataset.questionIndex=String(F),z.textContent="Insert",z.title="Insert question into markdown",D.append(_,z),t.cogitoQuestionList.appendChild(D)})}function g(b){t.cogitoStatus.textContent=b}function k(b){return b==="deep"?Qr:jr}function y(b){return b==="deep"?"Deep (Qwen3 8B)":"Lite (Llama 1B)"}async function H(b){var z;let A=k(b),F=await u(),D={...F.prebuiltAppConfig,cacheBackend:"indexeddb"};async function _(){return F.CreateMLCEngine(A,{appConfig:D,initProgressCallback:R=>{R!=null&&R.text&&g(R.text)}})}try{return await _()}catch(R){if(!Xe(R))throw R;g(`Repairing ${y(b)} cache and retrying...`);try{await((z=F.deleteModelAllInfoInCache)==null?void 0:z.call(F,A,D))}catch(J){}return await Vr(750),_()}}async function V(b=l){return m[b]||(g(`Loading ${y(b)} model...`),m[b]=H(b).catch(A=>{throw delete m[b],A})),m[b]}async function re(){try{return await V(l)}catch(b){if(l!=="deep"||!Xe(b))throw b;return g("Deep model download failed. Falling back to Lite..."),h("lite"),V("lite")}}async function oe(){var A,F,D;let b=Kr(e());if(!b){g("Write at least one sentence first, then generate Cogito questions."),i("Cogito needs a sentence","warn");return}try{t.cogitoGenerateBtn.disabled=!0,g("Generating 3 questions...");let _=await re(),z=Ur(o()),R=a(),I=(D=(F=(A=(await _.chat.completions.create({messages:[{role:"system",content:qr},{role:"user",content:Gr(b,z)}],temperature:.2})).choices)==null?void 0:A[0])==null?void 0:F.message)==null?void 0:D.content,K=Array.isArray(I)?I.map(ee=>typeof ee=="string"?ee:"").join("").trim():typeof I=="string"?I.trim():"";if(!K)throw new Error("Cogito returned an empty response.");c=Zr(K),p=z?R:null,w(c),g(z?"Questions ready and focused by the current document analysis.":"Questions ready. Analyze the document for more focused coaching."),i("Cogito questions ready","ok")}catch(_){c=[],w(c);let z=_ instanceof Error?_.message:String(_),R=Xe(_)?"The local model could not finish downloading. Check the connection and available browser storage, then retry.":z;g(`Cogito error: ${R}`),i("Cogito unavailable","warn"),r(`Cogito failed: ${R}`,{persist:!0})}finally{t.cogitoGenerateBtn.disabled=!1}}function T(b){let A=c[b];if(!A){r("Cogito question not found.",{persist:!0});return}let F=Yr(A);Xr(t.editor,F),n(t.editor.value),i("Inserted AI question","ok")}function v(b){c.length===0||p===b||g("Document analysis changed. Regenerate to focus these questions on the latest findings.")}return d(!1),{togglePanel:()=>{d(!s),s&&g(o()?"Generate questions focused by the current document analysis.":"Analyze first for more focused coaching, or generate from your latest sentence now.")},setPanelOpen:b=>{d(b),b&&g(o()?"Generate questions focused by the current document analysis.":"Analyze first for more focused coaching, or generate from your latest sentence now.")},isPanelOpen:()=>s,closePanel:()=>{d(!1)},selectModel:h,generateQuestions:oe,insertQuestionAtIndex:T,markAnalysisChanged:v}}var jt="No clear strengths stand out yet; the draft needs more signal before the linter can praise specific choices.",Je="No major fixes stood out.",et="No notable issues in this category.",tt="This note did not expose any obvious structural sections.";function Qt(){return"The note opens cleanly, but it still needs a visible structure signal."}function nt(){return"The first sentence does a lot of work. Tighten it and let the rest of the section carry the supporting detail."}function Vt(){return"The opening is understandable, but it could be shaped into a sharper lead sentence."}function rt(){return"Lead with the payoff or takeaway first, then use the topic sentence to support it."}function Ut(){return"There are real strengths here: concrete details and structure cues give the document memory and shape."}function Gt(){return"The content is solid, but it needs a clearer scaffold so the reader can move through it faster."}function Kt(){return"The draft is too thin or fragmentary to score well yet; add a clearer claim and one or two supporting details."}function Zt(){return"Add a heading at the top so the chapter reads as a structured note instead of a raw outline."}function Yt(t){return`Turn "${t}" into a heading so the bullet list has a clean anchor.`}function Xt(){return"The bullet list has good content, but several bullets carry more than one idea. Consider grouping them by theme or splitting the longest ones."}function Jt(){return"This would scan better with three visible beats: a short lead, a grouped facts section, and a closing takeaway."}function en(){return"The document's logic is good, but the middle section is carrying too much at once. A mid-document heading would make the progression easier to follow."}function tn(){return"This section leans on a few abstract nouns. It still reads clearly, but a couple of them could be replaced with plainer words."}function it(){return"A word is repeated back-to-back. Clean that up before worrying about higher-level style."}function ot(){return"There is not enough developed prose yet to judge the document fairly. Add a clearer point and at least one supporting sentence."}function nn(){return"This section is too thin to evaluate well; add a clearer claim or supporting detail."}function rn(){return"A question can be a good hook, but it needs an immediate answer or payoff."}function on(){return"This section is balanced and easy to follow."}function sn(t){return`The "${t}" block introduces a real section. Promoting it to a heading would make the document easier to scan.`}function an(t,e){return`The section starting at line ${t} has ${e} bullets and several carry more than one idea.`}function ln(t){return`The section starting at line ${t} is carrying a lot of text and would be easier to scan if it were split.`}function cn(t){return`Turn "${t}:" into a heading so the section reads as intentional structure.`}function un(){return"This bullet run is dense; split the longest items or group them by theme."}function dn(){return"This section carries a lot of material; consider splitting it into two smaller beats."}function pn(){return"The heading works, but this section is list-only; a short lead sentence could help orient the reader."}function hn(t,e){return`The "${t}" section deserves attention: ${e.toLowerCase()}`}function gn(){return"Concrete anchors such as names, dates, or numbers make the material easier to remember."}function mn(){return"The document gives the reader a memory hook, which makes the takeaway easier to retain."}function fn(){return"The parallel list structure creates a strong rhythm and makes the sequence easy to scan."}function kn(){return"The opening question creates forward pull and gives the reader a reason to keep going."}function wn(){return"The lead uses clear directive language, which gives the document momentum."}function bn(){return"The document already has enough visible structure that a reader can scan it quickly."}var Ln=[{id:"readability",title:"Readability",color:"#4CAF50"},{id:"skimmability",title:"Skimmability",color:"#2196F3"},{id:"engagement",title:"Engagement",color:"#FF9800"},{id:"style",title:"Style",color:"#9C27B0"},{id:"structure",title:"Structure",color:"#607D8B"}],Jr=300;function de(t){return Math.max(0,Math.min(100,t))}function G(t){var e,n;return(n=(e=t.match(/\b\w+\b/g))==null?void 0:e.length)!=null?n:0}function Ne(t){return t.replaceAll(/\s+/g," ").split(/(?<=[.!?])\s+/).map(e=>e.trim()).filter(Boolean)}function ei(t){return Ne(t).length}function X(t){return t.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function yn(t){return t==="high"?3:t==="medium"?2:1}function ti(t,e=`Line ${t}`){return``}function st(t){return/^```/.test(t.trim())}function xn(t){return/^#{1,6}\s+/.test(t.trim())}function ni(t){return/^\s*\d+[.)]\s+/.test(t)}function ri(t){return/^\s*[-*+]\s+/.test(t)}function at(t){return ri(t)||ni(t)}function Ce(t){return/^\s*>\s?/.test(t)}function lt(t){return/^(?:\t| {4,})/.test(t)}function Tn(t,e){return t.type==="paragraph"&&/:\s*$/.test(t.text)&&(e==null?void 0:e.type)==="list_item"?t.text.replace(/:\s*$/,""):null}function vn(t){return t.replace(/^(\s*(?:[-*+]|\d+[.)]))\s+.*$/,"$1")}function Sn(t){return t.replace(/^\s*(?:[-*+]|\d+[.)])\s+/,"").trim()}function ii(t){return t.map(e=>e.trim()).join(" ").replaceAll(/\s+/g," ").trim()}function En(t){let e=t.split(` +`),n=[],r=0;for(;r\s?/,"").trim()),r+=1;continue}if(m.trim()===""&&r+1n.type===e)}function ct(t){return t.filter(e=>e.type==="paragraph"||e.type==="blockquote")}function Mn(t){let e=t.match(/\b([a-z][a-z'-]{1,})\b(?:\s+\1\b)/i);return!e||typeof e.index!="number"?null:{word:e[1],index:e.index}}function q(t,e,n,r,i,o=!1){return{category:t,severity:e,title:n,detail:r,section:i,isStrength:o}}function oi(t){let e=t.find(n=>n.type==="paragraph");return e?G(e.text)>=22?nt():/^(this|these)\b/i.test(e.text)?rt():Vt():Qt()}function si(t){for(let e=0;ep.title==="Repeated word")||n.push(q("readability","medium","Repeated word",it(),`Line ${s.lineStart}`)));for(let p of c){let m=G(p),u=p.length;if((m>=24||u>=140)&&(r+=1,n.length<3)){let h=p.length>70?`${p.slice(0,70)}...`:p;n.push(q("readability","medium","Dense sentence",`This sentence is carrying too much at once: "${h}". Split the idea or move the setup into a heading.`,`Line ${s.lineStart}`))}}}let o=e[0];return o&&G(o.text)>=22&&n.unshift(q("readability","high","Opening is dense",nt(),`Line ${o.lineStart}`)),{result:{score:de(100-r*14-i*10-(o&&G(o.text)>=22?8:0)),suggestions:n.map(s=>`${s.title}: ${s.detail}`)},findings:n}}function ci(t){let e=ue(t,"heading"),n=ue(t,"list_item"),r=si(t),i=[];return e.length===0&&i.push(q("skimmability","high","Missing heading",Zt(),"Document")),r&&i.push(q("skimmability","medium","Make the section label explicit",Yt(r.text),`Line ${r.lineStart}`)),n.length>=4&&n.reduce((s,c)=>s+G(c.text),0)/n.length>=11&&i.push(q("skimmability","medium","Bullets are dense",Xt(),"Bullet list")),{result:{score:de(100-(e.length===0?20:0)-(r?6:0)-(n.length>=4?10:0)),suggestions:i.map(a=>`${a.title}: ${a.detail}`)},findings:i,pseudoSectionLabel:r}}function ui(t){var T,v,b,A,F,D,_,z,R,J;let e=t.map(I=>I.text).join(" "),n=t.find(I=>I.type==="paragraph"),r=(T=n==null?void 0:n.text)!=null?T:"",i=[],o=[],a=G(e),s=(v=Ne(r)[0])!=null?v:r,c=G(s),l=/\?\s*$/.test(s),p=/^(remember|consider|notice|start|imagine|picture|look|think)\b/i.test(s),m=(b=e.match(/\b(?:was|were|is|are|be|been|being)\s+\w+ed\b/gi))!=null?b:[],u=Ne(e),h=u.length>0?m.length/u.length:0,d=new Set(((A=e.toLowerCase().match(/\b[a-z][a-z'-]+\b/g))!=null?A:[]).filter(I=>I.length>=4)),w=G(e)>0?d.size/G(e):0,g=(F=e.match(/\b(?:remember|consider|notice|focus|compare|look|keep|start)\b/gi))!=null?F:[],k=(D=e.match(/\b(?:\d{2,4}|[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b/g))!=null?D:[],y=(_=e.match(/\b(?:remember|key takeaway|simple way to remember|think of|in short)\b/gi))!=null?_:[],H=ue(t,"list_item").map(I=>I.text),V=H.map(I=>{var K,ee;return((ee=(K=I.match(/^[A-Za-z]+/))==null?void 0:K[0])!=null?ee:"").toLowerCase()}).filter(Boolean),re=V.find((I,K)=>I&&V.indexOf(I)!==K);return/^(this|these)\b/i.test(s)&&c>=8&&i.push(q("engagement","medium","Opening is informative, not directive",rt(),`Line ${(z=n==null?void 0:n.lineStart)!=null?z:1}`)),a>0&&a<12&&i.push(q("engagement","high","Too little context",ot(),`Line ${(R=n==null?void 0:n.lineStart)!=null?R:1}`)),l&&a<18&&i.push(q("engagement","medium","Question needs payoff",rn(),`Line ${(J=n==null?void 0:n.lineStart)!=null?J:1}`)),l&&o.push(kn()),(p||g.length>=2)&&o.push(wn()),y.length>0&&o.push(mn()),k.length>=2&&o.push(gn()),re&&H.length>=3&&o.push(fn()),o.length===0&&(ue(t,"heading").length>0||H.length>=3)&&o.push(bn()),{result:{score:de(48+(l?10:0)+(p?10:0)+Math.min(o.length,3)*8+(w>=.28?6:0)+(c>0&&c<=16?6:0)-(a>0&&a<12?18:0)-(h>=.4?12:0)-i.length*8),suggestions:i.map(I=>`${I.title}: ${I.detail}`)},findings:i,strengths:o}}function di(t){var s;let e=ct(t).map(c=>c.text).join(" "),n=(s=e.match(/\b\w+(?:tion|sion|ment|ness|ance|ence)\b/gi))!=null?s:[],r=[],i=Mn(e),o=[...new Set(n.map(c=>c.toLowerCase()))];return o.length>=4&&r.push(q("style","low","Dense abstract language",tn(),"Document")),i&&r.unshift(q("style","medium","Repeated word",it(),"Document")),{result:{score:de(96-Math.min(o.length,4)*2-(i?14:0)),suggestions:r.map(c=>`${c.title}: ${c.detail}`)},findings:r}}function pi(t){let e=ue(t,"heading"),n=ue(t,"list_item"),r=ue(t,"paragraph"),i=[],o=G(t.map(s=>s.text).join(" "));return e.length===0&&r.length>0&&n.length>0&&i.push(q("structure","high","Add a simple outline",Jt(),"Document")),n.length>=5&&e.length===0&&i.push(q("structure","medium","Break up the middle",en(),"Bullet list")),o>0&&o<12&&i.push(q("structure","high","Draft is too thin",ot(),"Document")),{result:{score:de(86-(e.length===0?12:0)-(n.length>=5?8:0)-(o>0&&o<12?22:0)),suggestions:i.map(s=>`${s.title}: ${s.detail}`)},findings:i}}function hi(t){let e=[],n=t.map(r=>{let i=r.blocks.map(m=>m.text).join(" "),o=r.blocks.filter(m=>m.type==="list_item"),a=r.blocks.filter(m=>m.type==="paragraph"||m.type==="blockquote"),s=G(i),c=o.length>0?o.reduce((m,u)=>m+G(u.text),0)/o.length:0,l=[],p=!1;return r.kind==="label"&&(l.push(cn(r.title)),p=!0,e.push(q("structure","medium","Convert label into heading",sn(r.title),`Line ${r.lineStart}`))),o.length>=4&&c>=11&&(l.push(un()),p=!0,e.push(q("structure","medium","Dense bullet section",an(r.lineStart,o.length),`Line ${r.lineStart}`))),s>=90&&a.length>=2&&(l.push(dn()),p=!0,e.push(q("structure","low","Long section",ln(r.lineStart),`Line ${r.lineStart}`))),r.kind==="heading"&&o.length>0&&a.length===0&&l.push(pn()),s>0&&s<12&&a.length>0&&(l.push(nn()),p=!0),l.length===0&&l.push(on()),{title:r.title,kind:r.kind,lineStart:r.lineStart,lineEnd:r.lineEnd,notes:l,needsAttention:p}});return{findings:e,sectionNotes:n}}function gi(t,e,n,r){let i=G(n.map(c=>c.text).join(" ")),o=t.filter(c=>!c.isStrength).slice().sort((c,l)=>yn(l.severity)-yn(c.severity)).slice(0,4).map(c=>`${c.title} \u2014 ${c.detail}`),a=[oi(n)],s=r.find(c=>c.needsAttention);return s&&a.push(hn(s.title,s.notes[0])),e.length>0?a.push(Ut()):i>0&&i<12?a.push(Kt()):a.push(Gt()),{quickTake:a,priorities:o,strengths:e.length>0?e:[jt]}}function mi(t){let e={readability:.25,skimmability:.2,engagement:.2,style:.15,structure:.2},n=Object.entries(t).reduce((r,[i,o])=>r+o.score*e[i],0);return de(Math.round(n))}function fi(t,e,n){if(t.scrollTop=e,!n||typeof t.querySelector!="function")return;let r=t.querySelector(`[data-linter-focus-key="${n}"]`);r==null||r.focus()}function ki(t){return typeof t=="object"&&t!==null&&"getAttribute"in t}function wi(t){let e=En(t),n=ai(e),r=li(e),i=ci(e),o=ui(e),a=di(e),s=pi(e),c=hi(n),l=[...r.findings,...i.findings,...o.findings,...a.findings,...s.findings,...c.findings],p=[{title:"Readability",lines:r.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Skimmability",lines:i.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Engagement",lines:o.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Style",lines:a.findings.map(u=>`- ${u.title}: ${u.detail}`)},{title:"Structure",lines:s.findings.map(u=>`- ${u.title}: ${u.detail}`)}],m={readability:r.result,skimmability:i.result,engagement:o.result,style:a.result,structure:s.result};return{scores:m,overallScore:mi(m),overview:gi(l,o.strengths,e,c.sectionNotes),sections:p,documentSections:c.sectionNotes}}function bi(t,e){let n=En(t),r=G(t),i=ei(ct(n).map(c=>c.text).join(" ")),o=Ln.map(c=>{var p;let l=e.sections.find(m=>m.title===c.title);return{title:c.title,lines:(p=l==null?void 0:l.lines)!=null&&p.length?l.lines:[`- ${et}`]}}),a=e.documentSections.length>0?e.documentSections:[{title:"Document",kind:"implicit",lineStart:1,lineEnd:1,notes:[tt],needsAttention:!1}];return["# Document Linter Review","","## Overall",`- Overall score: ${e.overallScore}/100`,"","## Quick take",...e.overview.quickTake.map(c=>`- ${c}`),"","## What to fix first",...e.overview.priorities.length>0?e.overview.priorities.map((c,l)=>`${l+1}. ${c}`):[`- ${Je}`],"","## What is working",...e.overview.strengths.map(c=>`- ${c}`),"","## Section notes",...o.flatMap(c=>[`### ${c.title}`,...c.lines,""]),"## Section analysis",...a.flatMap(c=>[`### ${c.title}`,`- Type: ${c.kind}`,`- Lines: ${c.lineStart}\u2013${c.lineEnd}`,`- Needs attention: ${c.needsAttention?"yes":"no"}`,...c.notes.map(l=>`- ${l}`),""]),"## Snapshot",`- Words: ${r}`,`- Sentences: ${i}`,`- Blocks: ${n.length}`].join(` +`)}function An({els:t,getEditorText:e,onEditorContentReplaced:n,showToast:r,setStatus:i,onAnalysisUpdated:o}){let a=!1,s=!1,c=!1,l=null,p="",m=0,u=null,h=[];async function d(T=e()){return Promise.resolve(wi(T))}function w(){let T=h;h=[],T.forEach(v=>v())}function g(){u!==null&&(clearTimeout(u),u=null),w()}function k(T){let v=t.editor.value.split(` +`),b=Math.max(1,Math.min(T,v.length||1)),A=0;for(let _=0;_

Overall

-
${g.overallScore}/100
+
${T.overallScore}/100

Quick take

    - ${g.overview.quickTake.map(D=>`
  • ${j(D)}
  • `).join("")} + ${T.overview.quickTake.map(R=>`
  • ${X(R)}
  • `).join("")}

What to fix first

    - ${g.overview.priorities.map(D=>`
  1. ${j(D)}
  2. `).join("")||`
  3. ${j(Ue)}
  4. `} + ${T.overview.priorities.map(R=>`
  5. ${X(R)}
  6. `).join("")||`
  7. ${X(Je)}
  8. `}

What is working

    - ${g.overview.strengths.map(D=>`
  • ${j(D)}
  • `).join("")} + ${T.overview.strengths.map(R=>`
  • ${X(R)}
  • `).join("")}
- `,S.appendChild(P),wn.forEach(D=>{var ne,re;let J=g.scores[D.id],te=(re=(ne=g.sections.find(Y=>Y.title===D.title))==null?void 0:ne.lines)!=null?re:[],K=document.createElement("div");K.className="documentLinterCategory",K.innerHTML=` -
-

${D.title}

+ `,v.appendChild(D),Ln.forEach(R=>{var ee,pe;let J=T.scores[R.id],I=(pe=(ee=T.sections.find(he=>he.title===R.title))==null?void 0:ee.lines)!=null?pe:[],K=document.createElement("div");K.className="documentLinterCategory",K.innerHTML=` +
+

${R.title}

${J.score}/100
- ${te.length>0?te.map(Y=>`
${j(Y)}
`).join(""):`
${j(Ge)}
`} + ${I.length>0?I.map(he=>`
${X(he)}
`).join(""):`
${X(et)}
`}
- `,S.appendChild(K)});let I=document.createElement("section");I.className="documentLinterSectionAnalysis",I.innerHTML=` + `,v.appendChild(K)});let _=document.createElement("section");_.className="documentLinterSectionAnalysis",_.innerHTML=`

Section analysis

- ${g.documentSections.length>0?g.documentSections.map(D=>` + ${T.documentSections.length>0?T.documentSections.map(R=>`
-

${j(D.title)}

- ${j(D.kind)} \xB7 ${zr(D.lineStart,`Lines ${D.lineStart}\u2013${D.lineEnd}`)} \xB7 ${D.needsAttention?"needs attention":"stable"} +

${X(R.title)}

+ ${X(R.kind)} \xB7 ${ti(R.lineStart,`Lines ${R.lineStart}\u2013${R.lineEnd}`)} \xB7 ${R.needsAttention?"needs attention":"stable"}
- ${D.notes.map(J=>`
${j(J)}
`).join("")} + ${R.notes.map(J=>`
${X(J)}
`).join("")}
- `).join(""):`
${j(Ke)}
`} + `).join(""):`
${X(tt)}
`}
- `,S.appendChild(I),ei(S,x,N)}function p(g){let S=new Blob([g],{type:"text/markdown"}),x=URL.createObjectURL(S),N=`ink-linter-report-${new Date().toISOString().split("T")[0]}.md`,P=document.createElement("a");P.href=x,P.download=N,document.body.appendChild(P),P.click(),document.body.removeChild(P),URL.revokeObjectURL(x)}async function b(){if(a)return;let g=e();if(!g.trim()){r("No content to analyze",{persist:!0}),i("No content to analyze","warn");return}a=!0,l=g,t.documentLinterAnalyzeBtn.disabled=!0,t.documentLinterStatus.textContent="Analyzing document...";try{let S=await d(g);c=S,h(S),t.documentLinterStatus.textContent="Analysis complete",r("Document analysis completed"),i("Analysis complete","ok")}catch(S){t.documentLinterStatus.textContent="Analysis failed",r(`Document analysis failed: ${String(S)}`,{persist:!0}),i("Analysis failed","err")}finally{a=!1,t.documentLinterAnalyzeBtn.disabled=!1}}async function m(){let g=e();if(!g.trim()){r("No content to export",{persist:!0}),i("No content to export","warn");return}try{let S=g!==l||!c;S&&(t.documentLinterStatus.textContent="Document changed; re-analyzing before export...",i("Re-analyzing before export","warn"));let x=S?await d(g):c;c=x,l=g;let y=ri(g,x);p(y),r("Exported linter review as Markdown."),i("Exported report","ok")}catch(S){r(`Failed to export linter report: ${String(S)}`,{persist:!0}),i("Export failed","err")}}async function w(g=e()){return!o||!s||a||!g.trim()?(!g.trim()&&s&&o&&(c=null,l=g,t.documentLinterStatus.textContent="No content to analyze"),Promise.resolve()):g===l?Promise.resolve():b()}return t.documentLinterAutoRunToggle.checked=s,t.documentLinterAutoRunToggle.addEventListener("change",()=>{s=t.documentLinterAutoRunToggle.checked,t.documentLinterStatus.textContent=s?"Rerun on change enabled":"Rerun on change disabled"}),t.documentLinterResults.addEventListener("click",g=>{let S=g.target,x=S==null?void 0:S.closest("[data-linter-line]");if(!x)return;let y=Number(x.getAttribute("data-linter-line"));Number.isNaN(y)||f(y)}),u(!1),{togglePanel:()=>{u(!o),o&&(t.documentLinterStatus.textContent="Ready to analyze document")},setPanelOpen:g=>{u(g),g&&(t.documentLinterStatus.textContent="Ready to analyze document")},isPanelOpen:()=>o,closePanel:()=>{u(!1)},handleEditorChanged:w,analyzeDocument:b,exportSuggestions:m}}function Sn(){ii(Lt()).initialize()}function ii(t){let e={workspaceHandle:null,workspaceName:"",fileTree:null,notes:[],inMemoryNotes:[],currentFileHandle:null,currentRelPath:"",currentContent:"",isDirty:!1,searchQuery:"",tagFilter:"",sortMode:"name",collapsedDirs:new Set,autoRefreshMs:1e4,autoRefreshTimer:null,isSidebarCollapsed:!1,isTemporarySession:!1,isCogitoModeEnabled:!1,editorViewMode:Rt()},n={current:null},{showToast:r,hideToast:i}=Pt(t,n),o={openNoteByRelPath:async()=>{},openInMemoryNote:async()=>{}},a=Dt({state:e,els:t,handlers:o,showToast:r}),s={current:async()=>{}},c=Mt({state:e,ensurePermission:_e,rescanWorkspace:x=>s.current(x),showToast:r,setStatus:(x,y)=>ee(t,x,y)}),l=It({state:e,els:t,showToast:r,setStatus:(x,y)=>ee(t,x,y),renderPreview:Oe,updateDirtyUi:Se,renderTree:a.renderTree,renderInMemoryTree:a.renderInMemoryTree,renderTags:a.renderTags,updateCountsPill:a.updateCountsPill,fsApi:{ensurePermission:_e,isFileSystemApiAvailable:Tt},parseTags:St,normalizeTag:ge,autoRefresh:c});s.current=l.rescanWorkspace,o.openNoteByRelPath=l.openNoteByRelPath,o.openInMemoryNote=l.openInMemoryNote;let d=Nt({state:e,els:t,showToast:r,renderTree:a.renderTree,callbacks:{createNewNote:l.createNewNote,createNewFolder:l.createNewFolder,openWorkspace:l.openWorkspace,closeWorkspace:l.closeWorkspace,saveCurrentNote:l.saveCurrentNote,saveAsNewNote:l.saveAsNewNote,handleRefresh:l.handleRefresh,exportAsJson:l.exportAsJson,exportAsMarkdown:l.exportAsMarkdown,setSidebarCollapsed:x=>Qe(t,e,x)}});function f(x){e.searchQuery=x,a.renderTree().catch(y=>{r(`Search render failed: ${String(y)}`,{persist:!0})})}function u(x){e.isDirty=x!==e.currentContent,Se(t,e,(y,N)=>ee(t,y,N)),Oe(t,x),g.handleEditorChanged(x)}function h(){g.closePanel(),e.isCogitoModeEnabled=!0,w.setPanelOpen(!0)}function p(){e.isCogitoModeEnabled=!1,w.closePanel()}function b(){w.isPanelOpen()&&p(),g.setPanelOpen(!0)}function m(){g.closePanel()}let w=Ht({els:t,getEditorText:()=>t.editor.value,onEditorContentReplaced:x=>u(x),showToast:r,setStatus:(x,y)=>ee(t,x,y)}),g=vn({els:t,getEditorText:()=>t.editor.value,onEditorContentReplaced:x=>u(x),showToast:r,setStatus:(x,y)=>ee(t,x,y)});function S(){A.use({breaks:!0});let x=navigator.platform.toLowerCase().includes("mac");$t(t,x),Bt({state:e,els:t,actions:{handleMenuAction:d.handleMenuAction,toggleSidebar:()=>Qe(t,e,!e.isSidebarCollapsed),handleRefresh:l.handleRefresh,toggleSort:d.toggleSort,handleSearchInput:f,handleEditorInput:u,saveCurrentNote:l.saveCurrentNote,createNewNote:l.createNewNote,createNoteFromTool:y=>l.createNoteFromTool(y),openWorkspace:l.openWorkspace,exportAsJson:l.exportAsJson,exportAsMarkdown:l.exportAsMarkdown,toggleCogitoPanel:()=>{if(w.isPanelOpen()){p();return}h()},selectCogitoModel:y=>w.selectModel(y),generateCogitoQuestions:()=>w.generateQuestions(),insertCogitoQuestion:y=>w.insertQuestionAtIndex(y),setEditorViewMode:y=>Ct(t,e,y),toggleDocumentLinterPanel:()=>{if(g.isPanelOpen()){m();return}b()},analyzeDocument:()=>g.analyzeDocument(),exportDocumentLinterSuggestions:()=>g.exportSuggestions(),hideToast:i,showToast:r}}),At(),je(t,e),We(t,e.editorViewMode),Se(t,e,(y,N)=>ee(t,y,N)),a.renderTree().catch(y=>{r(`Failed to render tree: ${String(y)}`,{persist:!0}),ee(t,"Render failed","err")})}return{initialize:S,state:e}}Sn();})(); + `,v.appendChild(_),fi(v,b,F)}function H(T){let v=new Blob([T],{type:"text/markdown"}),b=URL.createObjectURL(v),F=`ink-linter-report-${new Date().toISOString().split("T")[0]}.md`,D=document.createElement("a");D.href=b,D.download=F,document.body.appendChild(D),D.click(),document.body.removeChild(D),URL.revokeObjectURL(b)}async function V(){if(s)return;let T=e();if(!T.trim()){r("No content to analyze",{persist:!0}),i("No content to analyze","warn");return}s=!0,p=T,t.documentLinterAnalyzeBtn.disabled=!0,t.documentLinterStatus.textContent="Analyzing document...";try{let v=await d(T);l=v,m+=1,y(v),t.documentLinterStatus.textContent="Analysis complete",o==null||o(v,m),r("Document analysis completed"),i("Analysis complete","ok")}catch(v){t.documentLinterStatus.textContent="Analysis failed",r(`Document analysis failed: ${String(v)}`,{persist:!0}),i("Analysis failed","err")}finally{s=!1,t.documentLinterAnalyzeBtn.disabled=!1}}async function re(){let T=e();if(!T.trim()){r("No content to export",{persist:!0}),i("No content to export","warn");return}try{let v=T!==p||!l;v&&(t.documentLinterStatus.textContent="Document changed; re-analyzing before export...",i("Re-analyzing before export","warn"));let b=v?await d(T):l;l=b,p=T,v&&(m+=1,y(b),o==null||o(b,m));let A=bi(T,b);H(A),r("Exported linter review as Markdown."),i("Exported report","ok")}catch(v){r(`Failed to export linter report: ${String(v)}`,{persist:!0}),i("Export failed","err")}}async function oe(T=e()){return!a||!c||s||!T.trim()?(g(),!T.trim()&&c&&a&&(l=null,p=T,t.documentLinterStatus.textContent="No content to analyze"),Promise.resolve()):T===p?Promise.resolve():(u!==null&&clearTimeout(u),t.documentLinterStatus.textContent="Waiting for typing to pause...",new Promise(v=>{h.push(v),u=setTimeout(()=>{u=null,(a&&c&&T!==p?V():Promise.resolve()).finally(w)},Jr)}))}return t.documentLinterAutoRunToggle.checked=c,t.documentLinterAutoRunToggle.addEventListener("change",()=>{c=t.documentLinterAutoRunToggle.checked,c||g(),t.documentLinterStatus.textContent=c?"Rerun on change enabled":"Rerun on change disabled"}),t.documentLinterResults.addEventListener("click",T=>{let v=T.target,b=v==null?void 0:v.closest("[data-linter-line]");if(!b)return;let A=Number(b.getAttribute("data-linter-line"));Number.isNaN(A)||k(A)}),{setActive:T=>{a=T,T||g(),T&&!l&&(t.documentLinterStatus.textContent="Ready to analyze document")},handleEditorChanged:oe,analyzeDocument:V,exportSuggestions:re,getLatestAnalysis:()=>l,getAnalysisRevision:()=>m}}function Rn(){yi(Ct()).initialize()}function yi(t){let e={workspaceHandle:null,workspaceName:"",fileTree:null,notes:[],inMemoryNotes:[],currentFileHandle:null,currentRelPath:"",currentContent:"",isDirty:!1,searchQuery:"",tagFilter:"",sortMode:"name",collapsedDirs:new Set,autoRefreshMs:6e4,lastWorkspaceInteractionAt:Date.now(),autoRefreshTimer:null,isSidebarCollapsed:!1,isTemporarySession:!1,isCogitoModeEnabled:!1,editorViewMode:Dt()},n={current:null},{showToast:r,hideToast:i}=Ft(t,n),o={openNoteByRelPath:async()=>{},openInMemoryNote:async()=>{}},a=zt({state:e,els:t,handlers:o,showToast:r}),s={current:async()=>!1},c=$t({state:e,ensurePermission:Qe,rescanWorkspace:k=>s.current(k),showToast:r,setStatus:(k,y)=>se(t,k,y)}),l=Wt({state:e,els:t,showToast:r,setStatus:(k,y)=>se(t,k,y),renderPreview:Ue,updateDirtyUi:Ae,renderTree:a.renderTree,renderInMemoryTree:a.renderInMemoryTree,renderTags:a.renderTags,updateCountsPill:a.updateCountsPill,fsApi:{ensurePermission:Qe,isFileSystemApiAvailable:Nt},parseTags:Rt,normalizeTag:we,autoRefresh:c});s.current=l.rescanWorkspace,o.openNoteByRelPath=l.openNoteByRelPath,o.openInMemoryNote=l.openInMemoryNote;let p=_t({state:e,els:t,showToast:r,renderTree:a.renderTree,callbacks:{createNewNote:l.createNewNote,createNewFolder:l.createNewFolder,openWorkspace:l.openWorkspace,closeWorkspace:l.closeWorkspace,saveCurrentNote:l.saveCurrentNote,saveAsNewNote:l.saveAsNewNote,handleRefresh:l.handleRefresh,exportAsJson:l.exportAsJson,exportAsMarkdown:l.exportAsMarkdown,setSidebarCollapsed:k=>Ze(t,e,k)}});function m(k){e.searchQuery=k,a.renderTree().catch(y=>{r(`Search render failed: ${String(y)}`,{persist:!0})})}function u(k){e.isDirty=k!==e.currentContent,Ae(t,e,(y,H)=>se(t,y,H)),Ue(t,k),d.handleEditorChanged(k)}let h,d=An({els:t,getEditorText:()=>t.editor.value,onEditorContentReplaced:k=>u(k),showToast:r,setStatus:(k,y)=>se(t,k,y),onAnalysisUpdated:(k,y)=>{h==null||h.markAnalysisChanged(y)}});h=qt({els:t,getEditorText:()=>t.editor.value,onEditorContentReplaced:k=>u(k),showToast:r,setStatus:(k,y)=>se(t,k,y),getDocumentAnalysis:()=>d.getLatestAnalysis(),getAnalysisRevision:()=>d.getAnalysisRevision()});function w(k){e.isCogitoModeEnabled=k,h.setPanelOpen(k),d.setActive(k)}function g(){P.use({breaks:!0});let k=navigator.platform.toLowerCase().includes("mac");Ht(t,k),Ot({state:e,els:t,actions:{handleMenuAction:p.handleMenuAction,toggleSidebar:()=>Ze(t,e,!e.isSidebarCollapsed),handleRefresh:l.handleRefresh,toggleSort:p.toggleSort,handleSearchInput:m,handleEditorInput:u,saveCurrentNote:l.saveCurrentNote,createNewNote:l.createNewNote,createNoteFromTool:y=>l.createNoteFromTool(y),openWorkspace:l.openWorkspace,exportAsJson:l.exportAsJson,exportAsMarkdown:l.exportAsMarkdown,toggleCogitoPanel:()=>{w(!h.isPanelOpen())},selectCogitoModel:y=>h.selectModel(y),generateCogitoQuestions:()=>h.generateQuestions(),insertCogitoQuestion:y=>h.insertQuestionAtIndex(y),setEditorViewMode:y=>Bt(t,e,y),analyzeDocument:()=>d.analyzeDocument(),exportDocumentLinterSuggestions:()=>d.exportSuggestions(),hideToast:i,showToast:r}}),It(),Ke(t,e),Ve(t,e.editorViewMode),Ae(t,e,(y,H)=>se(t,y,H)),a.renderTree().catch(y=>{r(`Failed to render tree: ${String(y)}`,{persist:!0}),se(t,"Render failed","err")})}return{initialize:g,state:e}}Rn();})(); diff --git a/dist/styles.min.css b/dist/styles.min.css index 859fef4..fc7532f 100644 --- a/dist/styles.min.css +++ b/dist/styles.min.css @@ -1 +1 @@ -:root{--bg: #0b0f14;--panel: #0f1620;--panel2: #0c121a;--border: rgba(255, 255, 255, 0.10);--muted: rgba(255, 255, 255, 0.70);--text: rgba(255, 255, 255, 0.92);--accent: #5eead4;--danger: #fb7185;--ok: #34d399;--warn: #fbbf24;--shadow: 0 10px 30px rgba(0,0,0,.35);--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";--radius: 14px;--body-bg: radial-gradient(900px 600px at 10% 0%, rgba(94, 234, 212, 0.15), transparent 60%), radial-gradient(900px 600px at 90% 0%, rgba(251, 191, 36, 0.10), transparent 60%), var(--bg)}:root[data-theme=classic]{--bg: #f5f5f5;--panel: #ffffff;--panel2: #eeeeee;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.55);--text: rgba(0, 0, 0, 0.88);--accent: #2c6fad;--danger: #d9534f;--ok: #2e7d32;--warn: #e65100;--shadow: 0 10px 30px rgba(0,0,0,.10);--body-bg: var(--bg)}:root[data-theme=cobalt]{--bg: #001f3d;--panel: #002b52;--panel2: #002040;--border: rgba(255, 255, 255, 0.12);--muted: rgba(255, 255, 255, 0.60);--text: #e8f4fd;--accent: #ffd700;--danger: #ff6b6b;--ok: #32d48a;--warn: #ffba08;--shadow: 0 10px 30px rgba(0,0,0,.50);--body-bg: radial-gradient(800px 500px at 20% 0%, rgba(0, 120, 212, 0.20), transparent 60%), radial-gradient(800px 500px at 80% 100%, rgba(255, 215, 0, 0.08), transparent 60%), var(--bg)}:root[data-theme=monokai]{--bg: #272822;--panel: #3e3d32;--panel2: #2d2c27;--border: rgba(255, 255, 255, 0.10);--muted: rgba(248, 248, 242, 0.55);--text: #f8f8f2;--accent: #a6e22e;--danger: #f92672;--ok: #a6e22e;--warn: #e6db74;--shadow: 0 10px 30px rgba(0,0,0,.45);--body-bg: radial-gradient(700px 400px at 0% 0%, rgba(166, 226, 46, 0.08), transparent 60%), radial-gradient(700px 400px at 100% 100%, rgba(249, 38, 114, 0.08), transparent 60%), var(--bg)}:root[data-theme=office]{--bg: #f0f0f0;--panel: #ffffff;--panel2: #f7f7f7;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.50);--text: rgba(0, 0, 0, 0.85);--accent: #0078d4;--danger: #c4314b;--ok: #107c10;--warn: #c19c00;--shadow: 0 10px 30px rgba(0,0,0,.08);--body-bg: var(--bg)}:root[data-theme=twilight]{--bg: #141414;--panel: #1e1e1e;--panel2: #191919;--border: rgba(255, 255, 255, 0.10);--muted: rgba(247, 247, 247, 0.55);--text: #f5f5f5;--accent: #d4a96a;--danger: #cf6679;--ok: #7cbf8e;--warn: #c9a84c;--shadow: 0 10px 30px rgba(0,0,0,.50);--body-bg: radial-gradient(700px 400px at 15% 0%, rgba(212, 169, 106, 0.10), transparent 60%), radial-gradient(700px 400px at 85% 100%, rgba(124, 191, 142, 0.07), transparent 60%), var(--bg)}:root[data-theme=xcode]{--bg: #f9f9f9;--panel: #ffffff;--panel2: #f2f2f7;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.50);--text: rgba(0, 0, 0, 0.88);--accent: #0070c1;--danger: #b22222;--ok: #006400;--warn: #7d4700;--shadow: 0 10px 30px rgba(0,0,0,.08);--body-bg: var(--bg)}*{box-sizing:border-box}html,body{height:100%}body{margin:0;font-family:var(--sans);background:var(--body-bg);color:var(--text)}.app{height:100%;display:grid;grid-template-rows:auto 1fr;grid-template-columns:320px 1fr}.app.sidebar-collapsed{grid-template-columns:64px 1fr}.app>.menubar{grid-column:1/-1}.card{background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;min-height:0}.sidebar{display:flex;flex-direction:column;min-height:0;position:relative;border-radius:0}.sidebarToggle{display:inline-flex;margin:10px;top:10px;right:10px;z-index:2;padding:8px 10px;font-size:12px}.sidebarPanel{display:flex;flex-direction:column;min-height:0;height:100%}.sidebar.collapsed .sidebarPanel{display:none}.sidebar.collapsed{background:rgba(0,0,0,0);border-color:rgba(0,0,0,0);box-shadow:none}.sidebar.collapsed .sidebarToggle{display:flex;align-items:center;justify-content:center;writing-mode:vertical-lr;text-orientation:upright;letter-spacing:1px;background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);border:1px solid var(--border);color:var(--text)}.sidebar header{padding:14px 14px 10px;border-bottom:1px solid var(--border);display:block;align-items:center;gap:10px}.row{display:flex;gap:10px;align-items:center;margin-top:7px}.row.space{justify-content:space-between}.row .row{margin-top:0}#searchInput{margin-top:7px}.title{font-size:14px;font-weight:700;letter-spacing:.2px;display:flex;align-items:center;gap:10px}.pill{font-size:12px;padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:hsla(0,0%,100%,.04);color:var(--muted);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pill.warning{border-color:rgba(251,191,36,.45);background:rgba(251,191,36,.12);color:rgba(251,191,36,.95)}button{appearance:none;border:1px solid var(--border);background:hsla(0,0%,100%,.06);color:var(--text);padding:10px 12px;border-radius:12px;font-weight:650;cursor:pointer;transition:transform .05s ease,background .15s ease,border-color .15s ease;user-select:none;white-space:nowrap}button:hover{background:hsla(0,0%,100%,.1);border-color:hsla(0,0%,100%,.18)}button:active{transform:translateY(1px)}button.primary{border-color:rgba(94,234,212,.45);background:rgba(94,234,212,.14)}button.primary:hover{background:rgba(94,234,212,.2);border-color:rgba(94,234,212,.7)}button.ghost{background:rgba(0,0,0,0)}button:disabled{opacity:.55;cursor:not-allowed}.input{width:100%;border-radius:12px;border:1px solid var(--border);background:hsla(0,0%,100%,.05);color:var(--text);padding:10px 12px;outline:none;font-size:14px}.input::placeholder{color:hsla(0,0%,100%,.55)}@media(prefers-color-scheme: light){.input::placeholder{color:rgba(2,6,23,.4)}}.small{font-size:12px;color:var(--muted)}.treeWrap{padding:10px 10px 14px;overflow:auto;min-height:0}.tree{font-size:13px;line-height:1.35;padding:4px 0;user-select:none}.tree .node{display:flex;align-items:center;gap:6px;padding:6px 8px;border-radius:10px;cursor:pointer;transition:background .12s ease;border:1px solid rgba(0,0,0,0)}.tree .node:hover{background:hsla(0,0%,100%,.06)}.tree .node.active{background:rgba(94,234,212,.14);background:rgba(94,234,212,.4)}.tree .indent{margin-left:14px}.icon{width:16px;height:16px;display:inline-block;vertical-align:middle;flex-shrink:0;color:inherit;pointer-events:none;opacity:.85}.node .name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node .meta{font-size:11px;color:var(--muted);flex:0 0 auto}.tagrow{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px;padding:0 2px 2px}.tag{font-size:11px;padding:3px 7px;border-radius:999px;border:1px solid var(--border);color:var(--muted);background:hsla(0,0%,100%,.04);cursor:pointer}.tag.active{border-color:rgba(94,234,212,.45);color:var(--text);background:rgba(94,234,212,.12)}.main{display:flex;flex-direction:column;min-height:0;border-radius:0}.main header{padding:12px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;min-height:54px}.viewModePicker{display:inline-flex;align-items:center;gap:2px;padding:3px;border:1px solid var(--border);border-radius:999px;background:hsla(0,0%,100%,.04);flex:0 0 auto}.viewModeBtn{border:0;background:rgba(0,0,0,0);color:var(--muted);border-radius:999px;padding:6px 10px;font-size:12px;line-height:1;font-weight:700;white-space:nowrap}.viewModeBtn:hover{background:hsla(0,0%,100%,.06);color:var(--text)}.viewModeBtn.active{background:rgba(94,234,212,.16);color:var(--text);box-shadow:inset 0 0 0 1px rgba(94,234,212,.35)}.filename{font-weight:800;letter-spacing:.2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:55vw}.dirtyDot{width:8px;height:8px;border-radius:50%;background:var(--danger);box-shadow:0 0 0 3px rgba(251,113,113,.18);display:none;flex:0 0 auto}.dirtyDot.show{display:inline-block}.status{margin-left:auto;font-size:12px;color:var(--muted);display:flex;align-items:center;gap:8px;min-width:220px;justify-content:flex-end}.status .badge{padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:hsla(0,0%,100%,.04);color:var(--muted);max-width:60ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status .badge.ok{border-color:rgba(52,211,153,.35);color:rgba(52,211,153,.95);background:rgba(52,211,153,.1)}.status .badge.warn{border-color:rgba(251,191,36,.35);color:rgba(251,191,36,.95);background:rgba(251,191,36,.1)}.status .badge.err{border-color:rgba(251,113,133,.4);color:rgba(251,113,133,.95);background:rgba(251,113,133,.1)}.split{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:1fr 1fr}.split.view-source,.split.view-preview{grid-template-columns:1fr}.split.view-source.with-cogito,.split.view-source.with-document-linter,.split.view-preview.with-cogito,.split.view-preview.with-document-linter{grid-template-columns:1fr 320px}.split.view-split.with-cogito,.split.view-split.with-document-linter{grid-template-columns:1fr 1fr 320px}.editorPane,.previewPane{min-height:0;min-width:0;overflow:hidden;display:flex;flex-direction:column}.editorPane{border-right:1px solid var(--border);background:var(--panel2)}.split.view-source .editorPane{border-right:0}.split.view-preview .previewPane{border-left:0}.editorPane[hidden],.previewPane[hidden],.cogitoPanel[hidden],.documentLinterPanel[hidden]{display:none !important}textarea{width:100%;height:100%;resize:none;border:none;outline:none;padding:14px 14px 18px;font-family:var(--mono);font-size:14px;line-height:1.5;background:rgba(0,0,0,0);color:var(--text);overflow:auto;white-space:pre-wrap;word-break:break-word;tab-size:2}.preview{padding:14px 16px 18px;min-width:0;min-height:0;overflow-y:auto}.cogitoPanel{min-height:0;border-left:1px solid var(--border);background:var(--panel2);padding:12px;display:flex;flex-direction:column;gap:10px;overflow:auto}.cogitoPanel[hidden]{display:none}.documentLinterPanel{min-height:0;border-left:1px solid var(--border);background:var(--panel2);padding:12px;display:flex;flex-direction:column;gap:10px;overflow:auto}.documentLinterPanel[hidden]{display:none}.documentLinterHeader{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0;padding:0;border:0;min-height:0}.documentLinterAutoRun{display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--muted);user-select:none}.documentLinterAutoRun input{margin:0}.documentLinterResults{flex:1 1 auto;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding-bottom:12px}.documentLinterSummary{display:grid;gap:10px;margin-bottom:10px}.documentLinterSummaryBlock{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03)}.documentLinterSummaryBlock h3{margin:0 0 8px;font-size:13px;font-weight:700}.documentLinterSummaryBlock ul,.documentLinterSummaryBlock ol{margin:0;padding-left:18px;display:grid;gap:6px}.documentLinterCategory{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);margin:0}.documentLinterCategoryHeader{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid var(--border)}.documentLinterCategoryHeader h3{margin:0;font-size:13px;font-weight:600}.documentLinterScore{font-size:14px;font-weight:700;padding:2px 6px;border-radius:4px}.documentLinterSuggestions{display:flex;flex-direction:column;gap:4px}.documentLinterSuggestion{font-size:12px;line-height:1.4}.documentLinterLineButton{border:0;background:rgba(0,0,0,0);color:var(--accent);padding:0;font:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.documentLinterLineButton:hover,.documentLinterLineButton:focus-visible{color:var(--text);outline:none}.documentLinterSectionAnalysis{display:grid;gap:10px;margin-top:2px;padding-top:8px}.documentLinterSectionAnalysis h3{margin:0;font-size:13px;font-weight:700}.documentLinterSectionList{display:grid;gap:10px}.documentLinterSectionCard{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);display:grid;gap:8px}.documentLinterSectionHeader{display:flex;justify-content:space-between;gap:10px;align-items:baseline;padding-bottom:8px;border-bottom:1px solid var(--border)}.documentLinterSectionHeader h4{margin:0;font-size:12px;font-weight:700}.documentLinterSectionHeader span{font-size:11px;color:var(--muted);white-space:nowrap}.documentLinterSectionNotes{display:grid;gap:4px}.cogitoHeader{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0;padding:0;border:0;min-height:0}.cogitoModelPicker{display:flex;gap:4px}.cogitoModelBtn{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;font-size:11px;font-weight:600;border-radius:8px;opacity:.55}.cogitoModelBtn.active{opacity:1;border-color:rgba(94,234,212,.5);background:rgba(94,234,212,.14)}.cogitoQuestionList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.cogitoQuestionItem{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);display:flex;flex-direction:column;gap:8px}.cogitoQuestionText{margin:0;font-size:13px;line-height:1.4}.cogitoInsertBtn{align-self:flex-end;padding:6px 10px;font-size:12px;border-radius:10px}.preview :is(h1,h2,h3){line-height:1.25;margin:1em 0 .5em}.preview h1{font-size:1.6rem}.preview h2{font-size:1.35rem}.preview h3{font-size:1.15rem}.preview p{color:var(--text);opacity:.92}.preview a{color:var(--accent)}.preview code{font-family:var(--mono);font-size:.95em;padding:.15em .35em;border-radius:8px;background:hsla(0,0%,100%,.06);border:1px solid var(--border)}.preview pre{max-width:100%;padding:12px;border-radius:12px;background:rgba(94,234,212,.06);border:1px solid var(--border);white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.preview pre code{padding:0;border:none;background:rgba(0,0,0,0)}.preview blockquote{border-left:4px solid rgba(94,234,212,.45);padding:8px 12px;margin:12px 0;background:rgba(94,234,212,.08);border-radius:10px;color:var(--muted)}.preview table{width:100%;border-collapse:collapse;border:1px solid var(--border);border-radius:12px;overflow:hidden}.preview th,.preview td{border-bottom:1px solid var(--border);padding:8px 10px;text-align:left;vertical-align:top}.preview th{background:hsla(0,0%,100%,.05);font-weight:800}.menubar{display:flex;align-items:center;padding:0 8px;height:38px;border-radius:0;border-bottom:1px solid var(--border);background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);flex-shrink:0;overflow:visible}.menubar-spacer{flex:1 1 auto}.menu-action-btn{display:inline-flex;align-items:center;gap:6px;height:30px;padding:4px 10px;border-radius:10px;font-size:12px}.menu-action-btn+.menu-action-btn{margin-left:8px}.menu-action-label{font-weight:700}.menu-action-btn[aria-expanded=true]{border-color:rgba(94,234,212,.6);background:rgba(94,234,212,.16)}.glyphicon{display:inline-flex;width:16px;justify-content:center;align-items:center;line-height:1;font-size:14px}.glyphicon-education::before{content:"🧠"}.menu-item{position:relative;display:flex;align-items:center;gap:4px;padding:6px 12px;cursor:pointer;border-radius:8px;transition:background .12s ease;user-select:none}.menu-item:hover,.menu-item:focus,.menu-item[aria-expanded=true]{background:hsla(0,0%,100%,.08);outline:none}.menu-text{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:500;color:var(--text)}.menu-arrow{font-size:8px;color:var(--muted);margin-left:2px}.dropdown{position:absolute;top:100%;left:0;min-width:200px;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:4px 0;display:none;z-index:100;margin-top:2px}.menu-item[aria-expanded=true] .dropdown{display:block}.dropdown li{display:flex;align-items:center;justify-content:space-between;padding:8px 14px;cursor:pointer;transition:background .08s ease}.dropdown li:hover,.dropdown li:focus{background:rgba(94,234,212,.12);outline:none}.dropdown li[role=separator]{height:1px;padding:0;margin:4px 8px;background:var(--border);cursor:default}.dropdown li[role=separator]:hover,.dropdown li[role=separator]:focus{background:var(--border)}.menu-label{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--text)}.menu-label-text{min-width:0}.menu-shortcut{font-size:11px;color:var(--muted);margin-left:20px}.menu-theme-check{font-size:11px;color:var(--accent);margin-left:20px;opacity:0}.menu-theme-check.active{opacity:1}.submenu-parent{position:relative}.submenu-parent .dropdown.submenu{position:absolute;left:100%;top:0;margin-left:4px;display:none}.submenu-parent:hover>.dropdown.submenu{display:block}.submenu-parent>.menu-arrow{font-size:6px;margin-left:6px}.toast{position:fixed;left:50%;bottom:14px;transform:translateX(-50%);background:rgba(0,0,0,.7);color:#fff;padding:10px 12px;border-radius:12px;border:1px solid hsla(0,0%,100%,.18);max-width:min(760px,100vw - 28px);box-shadow:0 14px 40px rgba(0,0,0,.35);display:none;z-index:1000;font-size:13px;backdrop-filter:blur(10px)}.toast.show{display:block}.toast .trow{display:flex;gap:10px;align-items:flex-start}.toast .trow .tmsg{flex:1 1 auto}.toast .trow button{padding:6px 8px;border-radius:10px;font-size:12px}.webmcpModal{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px;z-index:1050;opacity:0;pointer-events:none;visibility:hidden}.webmcpModal.show{opacity:1;pointer-events:auto;visibility:visible}.webmcpModalBackdrop{position:absolute;inset:0;background:rgba(0,0,0,.56);backdrop-filter:blur(8px)}.webmcpModalCard{position:relative;width:min(680px,100vw - 40px);max-height:calc(100vh - 40px);overflow:auto;padding:18px;border-radius:18px;border:1px solid var(--border);background:linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 100%),var(--panel);box-shadow:var(--shadow)}.webmcpModalHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:14px}#webmcpNoteForm{display:grid;gap:10px}#webmcpNoteForm textarea{min-height:160px;resize:vertical}.webmcpModalActions{justify-content:flex-end;margin-top:4px}@media(max-width: 980px){.app{grid-template-columns:1fr;grid-template-rows:auto auto 1fr;height:100%}.app.sidebar-collapsed{grid-template-columns:1fr}.split,.split.view-source,.split.view-preview,.split.view-split.with-cogito,.split.view-split.with-document-linter,.split.view-source.with-cogito,.split.view-source.with-document-linter,.split.view-preview.with-cogito,.split.view-preview.with-document-linter{grid-template-columns:1fr}.editorPane{border-right:none;border-bottom:1px solid var(--border);min-height:40vh}.previewPane{min-height:40vh}.cogitoPanel{border-left:none;border-top:1px solid var(--border);min-height:30vh}.documentLinterPanel{border-left:none;border-top:1px solid var(--border);min-height:30vh}.filename{max-width:70vw}.sidebarToggle{position:static;margin:10px;width:calc(100% - 20px);height:auto;transform:none;writing-mode:horizontal-tb;text-orientation:mixed;letter-spacing:normal}.sidebar.collapsed .sidebarPanel{display:none}.sidebar.collapsed .sidebarToggle{display:flex;width:calc(100% - 20px);writing-mode:horizontal-tb;text-orientation:mixed;letter-spacing:normal}.sidebar.collapsed{height:auto;min-height:0}.webmcpModal{padding:12px;align-items:flex-end}.webmcpModalCard{width:100%;max-height:min(80vh,720px);border-radius:18px 18px 0 0}textarea{font-size:16px}} \ No newline at end of file +:root{--bg: #0b0f14;--panel: #0f1620;--panel2: #0c121a;--border: rgba(255, 255, 255, 0.10);--muted: rgba(255, 255, 255, 0.70);--text: rgba(255, 255, 255, 0.92);--accent: #5eead4;--danger: #fb7185;--ok: #34d399;--warn: #fbbf24;--shadow: 0 10px 30px rgba(0,0,0,.35);--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";--radius: 14px;--body-bg: radial-gradient(900px 600px at 10% 0%, rgba(94, 234, 212, 0.15), transparent 60%), radial-gradient(900px 600px at 90% 0%, rgba(251, 191, 36, 0.10), transparent 60%), var(--bg)}:root[data-theme=classic]{--bg: #f5f5f5;--panel: #ffffff;--panel2: #eeeeee;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.55);--text: rgba(0, 0, 0, 0.88);--accent: #2c6fad;--danger: #d9534f;--ok: #2e7d32;--warn: #e65100;--shadow: 0 10px 30px rgba(0,0,0,.10);--body-bg: var(--bg)}:root[data-theme=cobalt]{--bg: #001f3d;--panel: #002b52;--panel2: #002040;--border: rgba(255, 255, 255, 0.12);--muted: rgba(255, 255, 255, 0.60);--text: #e8f4fd;--accent: #ffd700;--danger: #ff6b6b;--ok: #32d48a;--warn: #ffba08;--shadow: 0 10px 30px rgba(0,0,0,.50);--body-bg: radial-gradient(800px 500px at 20% 0%, rgba(0, 120, 212, 0.20), transparent 60%), radial-gradient(800px 500px at 80% 100%, rgba(255, 215, 0, 0.08), transparent 60%), var(--bg)}:root[data-theme=monokai]{--bg: #272822;--panel: #3e3d32;--panel2: #2d2c27;--border: rgba(255, 255, 255, 0.10);--muted: rgba(248, 248, 242, 0.55);--text: #f8f8f2;--accent: #a6e22e;--danger: #f92672;--ok: #a6e22e;--warn: #e6db74;--shadow: 0 10px 30px rgba(0,0,0,.45);--body-bg: radial-gradient(700px 400px at 0% 0%, rgba(166, 226, 46, 0.08), transparent 60%), radial-gradient(700px 400px at 100% 100%, rgba(249, 38, 114, 0.08), transparent 60%), var(--bg)}:root[data-theme=office]{--bg: #f0f0f0;--panel: #ffffff;--panel2: #f7f7f7;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.50);--text: rgba(0, 0, 0, 0.85);--accent: #0078d4;--danger: #c4314b;--ok: #107c10;--warn: #c19c00;--shadow: 0 10px 30px rgba(0,0,0,.08);--body-bg: var(--bg)}:root[data-theme=twilight]{--bg: #141414;--panel: #1e1e1e;--panel2: #191919;--border: rgba(255, 255, 255, 0.10);--muted: rgba(247, 247, 247, 0.55);--text: #f5f5f5;--accent: #d4a96a;--danger: #cf6679;--ok: #7cbf8e;--warn: #c9a84c;--shadow: 0 10px 30px rgba(0,0,0,.50);--body-bg: radial-gradient(700px 400px at 15% 0%, rgba(212, 169, 106, 0.10), transparent 60%), radial-gradient(700px 400px at 85% 100%, rgba(124, 191, 142, 0.07), transparent 60%), var(--bg)}:root[data-theme=xcode]{--bg: #f9f9f9;--panel: #ffffff;--panel2: #f2f2f7;--border: rgba(0, 0, 0, 0.12);--muted: rgba(0, 0, 0, 0.50);--text: rgba(0, 0, 0, 0.88);--accent: #0070c1;--danger: #b22222;--ok: #006400;--warn: #7d4700;--shadow: 0 10px 30px rgba(0,0,0,.08);--body-bg: var(--bg)}*{box-sizing:border-box}html,body{height:100%}body{margin:0;font-family:var(--sans);background:var(--body-bg);color:var(--text)}.app{height:100%;display:grid;grid-template-rows:auto 1fr;grid-template-columns:320px 1fr}.app.sidebar-collapsed{grid-template-columns:64px 1fr}.app>.menubar{grid-column:1/-1}.card{background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;min-height:0}.sidebar{display:flex;flex-direction:column;min-height:0;position:relative;border-radius:0}.sidebarToggle{display:inline-flex;margin:10px;top:10px;right:10px;z-index:2;padding:8px 10px;font-size:12px}.sidebarPanel{display:flex;flex-direction:column;min-height:0;height:100%}.sidebar.collapsed .sidebarPanel{display:none}.sidebar.collapsed{background:rgba(0,0,0,0);border-color:rgba(0,0,0,0);box-shadow:none}.sidebar.collapsed .sidebarToggle{display:flex;align-items:center;justify-content:center;writing-mode:vertical-lr;text-orientation:upright;letter-spacing:1px;background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);border:1px solid var(--border);color:var(--text)}.sidebar header{padding:14px 14px 10px;border-bottom:1px solid var(--border);display:block;align-items:center;gap:10px}.row{display:flex;gap:10px;align-items:center;margin-top:7px}.row.space{justify-content:space-between}.row .row{margin-top:0}#searchInput{margin-top:7px}.title{font-size:14px;font-weight:700;letter-spacing:.2px;display:flex;align-items:center;gap:10px}.pill{font-size:12px;padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:hsla(0,0%,100%,.04);color:var(--muted);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pill.warning{border-color:rgba(251,191,36,.45);background:rgba(251,191,36,.12);color:rgba(251,191,36,.95)}button{appearance:none;border:1px solid var(--border);background:hsla(0,0%,100%,.06);color:var(--text);padding:10px 12px;border-radius:12px;font-weight:650;cursor:pointer;transition:transform .05s ease,background .15s ease,border-color .15s ease;user-select:none;white-space:nowrap}button:hover{background:hsla(0,0%,100%,.1);border-color:hsla(0,0%,100%,.18)}button:active{transform:translateY(1px)}button.primary{border-color:rgba(94,234,212,.45);background:rgba(94,234,212,.14)}button.primary:hover{background:rgba(94,234,212,.2);border-color:rgba(94,234,212,.7)}button.ghost{background:rgba(0,0,0,0)}button:disabled{opacity:.55;cursor:not-allowed}.input{width:100%;border-radius:12px;border:1px solid var(--border);background:hsla(0,0%,100%,.05);color:var(--text);padding:10px 12px;outline:none;font-size:14px}.input::placeholder{color:hsla(0,0%,100%,.55)}@media(prefers-color-scheme: light){.input::placeholder{color:rgba(2,6,23,.4)}}.small{font-size:12px;color:var(--muted)}.treeWrap{padding:10px 10px 14px;overflow:auto;min-height:0}.tree{font-size:13px;line-height:1.35;padding:4px 0;user-select:none}.tree .node{display:flex;align-items:center;gap:6px;padding:6px 8px;border-radius:10px;cursor:pointer;transition:background .12s ease;border:1px solid rgba(0,0,0,0)}.tree .node:hover{background:hsla(0,0%,100%,.06)}.tree .node.active{background:rgba(94,234,212,.14);background:rgba(94,234,212,.4)}.tree .indent{margin-left:14px}.icon{width:16px;height:16px;display:inline-block;vertical-align:middle;flex-shrink:0;color:inherit;pointer-events:none;opacity:.85}.node .name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node .meta{font-size:11px;color:var(--muted);flex:0 0 auto}.tagrow{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px;padding:0 2px 2px}.tag{font-size:11px;padding:3px 7px;border-radius:999px;border:1px solid var(--border);color:var(--muted);background:hsla(0,0%,100%,.04);cursor:pointer}.tag.active{border-color:rgba(94,234,212,.45);color:var(--text);background:rgba(94,234,212,.12)}.main{display:flex;flex-direction:column;min-height:0;border-radius:0}.main header{padding:12px 14px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;min-height:54px}.viewModePicker{display:inline-flex;align-items:center;gap:2px;padding:3px;border:1px solid var(--border);border-radius:999px;background:hsla(0,0%,100%,.04);flex:0 0 auto}.viewModeBtn{border:0;background:rgba(0,0,0,0);color:var(--muted);border-radius:999px;padding:6px 10px;font-size:12px;line-height:1;font-weight:700;white-space:nowrap}.viewModeBtn:hover{background:hsla(0,0%,100%,.06);color:var(--text)}.viewModeBtn.active{background:rgba(94,234,212,.16);color:var(--text);box-shadow:inset 0 0 0 1px rgba(94,234,212,.35)}.filename{font-weight:800;letter-spacing:.2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:55vw}.dirtyDot{width:8px;height:8px;border-radius:50%;background:var(--danger);box-shadow:0 0 0 3px rgba(251,113,113,.18);display:none;flex:0 0 auto}.dirtyDot.show{display:inline-block}.status{margin-left:auto;font-size:12px;color:var(--muted);display:flex;align-items:center;gap:8px;min-width:220px;justify-content:flex-end}.status .badge{padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:hsla(0,0%,100%,.04);color:var(--muted);max-width:60ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.status .badge.ok{border-color:rgba(52,211,153,.35);color:rgba(52,211,153,.95);background:rgba(52,211,153,.1)}.status .badge.warn{border-color:rgba(251,191,36,.35);color:rgba(251,191,36,.95);background:rgba(251,191,36,.1)}.status .badge.err{border-color:rgba(251,113,133,.4);color:rgba(251,113,133,.95);background:rgba(251,113,133,.1)}.split{flex:1 1 auto;min-height:0;display:grid;grid-template-columns:1fr 1fr}.split.view-source,.split.view-preview{grid-template-columns:1fr}.split.view-source.with-cogito,.split.view-preview.with-cogito{grid-template-columns:1fr minmax(320px, 380px)}.split.view-split.with-cogito{grid-template-columns:1fr 1fr minmax(320px, 380px)}.editorPane,.previewPane{min-height:0;min-width:0;overflow:hidden;display:flex;flex-direction:column}.editorPane{border-right:1px solid var(--border);background:var(--panel2)}.split.view-source .editorPane{border-right:0}.split.view-preview .previewPane{border-left:0}.editorPane[hidden],.previewPane[hidden],.cogitoPanel[hidden]{display:none !important}textarea{width:100%;height:100%;resize:none;border:none;outline:none;padding:14px 14px 18px;font-family:var(--mono);font-size:14px;line-height:1.5;background:rgba(0,0,0,0);color:var(--text);overflow:auto;white-space:pre-wrap;word-break:break-word;tab-size:2}.preview{padding:14px 16px 18px;min-width:0;min-height:0;overflow-y:auto}.cogitoPanel{min-height:0;border-left:1px solid var(--border);background:var(--panel2);padding:12px;display:flex;flex-direction:column;gap:12px;overflow:auto}.cogitoPanel[hidden]{display:none}.cogitoPanelHeader p,.cogitoSectionHeader p{margin:4px 0 0}.cogitoSection{display:grid;gap:10px;border:1px solid var(--border);border-radius:14px;padding:12px;background:hsla(0,0%,100%,.02)}.cogitoSection+.cogitoSection{margin-top:2px}.cogitoSectionHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}.cogitoSectionHeader h2{margin:0;font-size:14px}.cogitoSectionActions{display:flex;gap:6px}.cogitoSectionActions button,.cogitoSectionHeader>button{padding:7px 9px;font-size:12px}.documentLinterAutoRun{display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--muted);user-select:none}.documentLinterAutoRun input{margin:0}.documentLinterResults{display:flex;flex-direction:column;gap:12px;padding-bottom:12px}.documentLinterSummary{display:grid;gap:10px;margin-bottom:10px}.documentLinterSummaryBlock{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03)}.documentLinterSummaryBlock h3{margin:0 0 8px;font-size:13px;font-weight:700}.documentLinterSummaryBlock ul,.documentLinterSummaryBlock ol{margin:0;padding-left:18px;display:grid;gap:6px}.documentLinterCategory{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);margin:0}.documentLinterCategoryHeader{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;padding-bottom:8px;border-bottom:1px solid var(--border)}.documentLinterCategoryHeader h3{margin:0;font-size:13px;font-weight:600}.documentLinterScore{font-size:14px;font-weight:700;padding:2px 6px;border-radius:4px}.documentLinterSuggestions{display:flex;flex-direction:column;gap:4px}.documentLinterSuggestion{font-size:12px;line-height:1.4}.documentLinterLineButton{border:0;background:rgba(0,0,0,0);color:var(--accent);padding:0;font:inherit;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.documentLinterLineButton:hover,.documentLinterLineButton:focus-visible{color:var(--text);outline:none}.documentLinterSectionAnalysis{display:grid;gap:10px;margin-top:2px;padding-top:8px}.documentLinterSectionAnalysis h3{margin:0;font-size:13px;font-weight:700}.documentLinterSectionList{display:grid;gap:10px}.documentLinterSectionCard{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);display:grid;gap:8px}.documentLinterSectionHeader{display:flex;justify-content:space-between;gap:10px;align-items:baseline;padding-bottom:8px;border-bottom:1px solid var(--border)}.documentLinterSectionHeader h4{margin:0;font-size:12px;font-weight:700}.documentLinterSectionHeader span{font-size:11px;color:var(--muted);white-space:nowrap}.documentLinterSectionNotes{display:grid;gap:4px}.cogitoModelPicker{display:flex;gap:4px}.cogitoModelBtn{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;font-size:11px;font-weight:600;border-radius:8px;opacity:.55}.cogitoModelBtn.active{opacity:1;border-color:rgba(94,234,212,.5);background:rgba(94,234,212,.14)}.cogitoQuestionList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.cogitoQuestionItem{border:1px solid var(--border);border-radius:12px;padding:10px;background:hsla(0,0%,100%,.03);display:flex;flex-direction:column;gap:8px}.cogitoQuestionText{margin:0;font-size:13px;line-height:1.4}.cogitoInsertBtn{align-self:flex-end;padding:6px 10px;font-size:12px;border-radius:10px}.preview :is(h1,h2,h3){line-height:1.25;margin:1em 0 .5em}.preview h1{font-size:1.6rem}.preview h2{font-size:1.35rem}.preview h3{font-size:1.15rem}.preview p{color:var(--text);opacity:.92}.preview a{color:var(--accent)}.preview code{font-family:var(--mono);font-size:.95em;padding:.15em .35em;border-radius:8px;background:hsla(0,0%,100%,.06);border:1px solid var(--border)}.preview pre{max-width:100%;padding:12px;border-radius:12px;background:rgba(94,234,212,.06);border:1px solid var(--border);white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}.preview pre code{padding:0;border:none;background:rgba(0,0,0,0)}.preview blockquote{border-left:4px solid rgba(94,234,212,.45);padding:8px 12px;margin:12px 0;background:rgba(94,234,212,.08);border-radius:10px;color:var(--muted)}.preview table{width:100%;border-collapse:collapse;border:1px solid var(--border);border-radius:12px;overflow:hidden}.preview th,.preview td{border-bottom:1px solid var(--border);padding:8px 10px;text-align:left;vertical-align:top}.preview th{background:hsla(0,0%,100%,.05);font-weight:800}.menubar{display:flex;align-items:center;padding:0 8px;height:38px;border-radius:0;border-bottom:1px solid var(--border);background:linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 80%),var(--panel);flex-shrink:0;overflow:visible}.menubar-spacer{flex:1 1 auto}.menu-action-btn{display:inline-flex;align-items:center;gap:6px;height:30px;padding:4px 10px;border-radius:10px;font-size:12px}.menu-action-btn+.menu-action-btn{margin-left:8px}.menu-action-label{font-weight:700}.menu-action-btn[aria-expanded=true]{border-color:rgba(94,234,212,.6);background:rgba(94,234,212,.16)}.glyphicon{display:inline-flex;width:16px;justify-content:center;align-items:center;line-height:1;font-size:14px}.glyphicon-education::before{content:"🧠"}.menu-item{position:relative;display:flex;align-items:center;gap:4px;padding:6px 12px;cursor:pointer;border-radius:8px;transition:background .12s ease;user-select:none}.menu-item:hover,.menu-item:focus,.menu-item[aria-expanded=true]{background:hsla(0,0%,100%,.08);outline:none}.menu-text{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:500;color:var(--text)}.menu-arrow{font-size:8px;color:var(--muted);margin-left:2px}.dropdown{position:absolute;top:100%;left:0;min-width:200px;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);padding:4px 0;display:none;z-index:100;margin-top:2px}.menu-item[aria-expanded=true] .dropdown{display:block}.dropdown li{display:flex;align-items:center;justify-content:space-between;padding:8px 14px;cursor:pointer;transition:background .08s ease}.dropdown li:hover,.dropdown li:focus{background:rgba(94,234,212,.12);outline:none}.dropdown li[role=separator]{height:1px;padding:0;margin:4px 8px;background:var(--border);cursor:default}.dropdown li[role=separator]:hover,.dropdown li[role=separator]:focus{background:var(--border)}.menu-label{display:inline-flex;align-items:center;gap:8px;font-size:13px;color:var(--text)}.menu-label-text{min-width:0}.menu-shortcut{font-size:11px;color:var(--muted);margin-left:20px}.menu-theme-check{font-size:11px;color:var(--accent);margin-left:20px;opacity:0}.menu-theme-check.active{opacity:1}.submenu-parent{position:relative}.submenu-parent .dropdown.submenu{position:absolute;left:100%;top:0;margin-left:4px;display:none}.submenu-parent:hover>.dropdown.submenu{display:block}.submenu-parent>.menu-arrow{font-size:6px;margin-left:6px}.toast{position:fixed;left:50%;bottom:14px;transform:translateX(-50%);background:rgba(0,0,0,.7);color:#fff;padding:10px 12px;border-radius:12px;border:1px solid hsla(0,0%,100%,.18);max-width:min(760px,100vw - 28px);box-shadow:0 14px 40px rgba(0,0,0,.35);display:none;z-index:1000;font-size:13px;backdrop-filter:blur(10px)}.toast.show{display:block}.toast .trow{display:flex;gap:10px;align-items:flex-start}.toast .trow .tmsg{flex:1 1 auto}.toast .trow button{padding:6px 8px;border-radius:10px;font-size:12px}.webmcpModal{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px;z-index:1050;opacity:0;pointer-events:none;visibility:hidden}.webmcpModal.show{opacity:1;pointer-events:auto;visibility:visible}.webmcpModalBackdrop{position:absolute;inset:0;background:rgba(0,0,0,.56);backdrop-filter:blur(8px)}.webmcpModalCard{position:relative;width:min(680px,100vw - 40px);max-height:calc(100vh - 40px);overflow:auto;padding:18px;border-radius:18px;border:1px solid var(--border);background:linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 100%),var(--panel);box-shadow:var(--shadow)}.webmcpModalHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:14px}#webmcpNoteForm{display:grid;gap:10px}#webmcpNoteForm textarea{min-height:160px;resize:vertical}.webmcpModalActions{justify-content:flex-end;margin-top:4px}@media(max-width: 980px){.app{grid-template-columns:1fr;grid-template-rows:auto auto 1fr;height:100%}.app.sidebar-collapsed{grid-template-columns:1fr}.split,.split.view-source,.split.view-preview,.split.view-split.with-cogito,.split.view-source.with-cogito,.split.view-preview.with-cogito{grid-template-columns:1fr}.editorPane{border-right:none;border-bottom:1px solid var(--border);min-height:40vh}.previewPane{min-height:40vh}.cogitoPanel{border-left:none;border-top:1px solid var(--border);min-height:30vh}.filename{max-width:70vw}.sidebarToggle{position:static;margin:10px;width:calc(100% - 20px);height:auto;transform:none;writing-mode:horizontal-tb;text-orientation:mixed;letter-spacing:normal}.sidebar.collapsed .sidebarPanel{display:none}.sidebar.collapsed .sidebarToggle{display:flex;width:calc(100% - 20px);writing-mode:horizontal-tb;text-orientation:mixed;letter-spacing:normal}.sidebar.collapsed{height:auto;min-height:0}.webmcpModal{padding:12px;align-items:flex-end}.webmcpModalCard{width:100%;max-height:min(80vh,720px);border-radius:18px 18px 0 0}textarea{font-size:16px}} \ No newline at end of file diff --git a/dist/test/auto-refresh.js b/dist/test/auto-refresh.js new file mode 100644 index 0000000..2e49c18 --- /dev/null +++ b/dist/test/auto-refresh.js @@ -0,0 +1,56 @@ +// src/app/auto-refresh.ts +var MINIMUM_IDLE_MS = 5e3; +function createAutoRefresh({ + state, + ensurePermission, + rescanWorkspace, + showToast, + setStatus +}) { + function startAutoRefresh() { + stopAutoRefresh(); + state.autoRefreshTimer = setInterval(() => { + runAutoRefresh().catch((error) => { + showToast(`Auto-refresh failed: ${String(error)}`, { persist: true }); + setStatus("Auto-refresh failed", "err"); + }); + }, state.autoRefreshMs); + } + async function runAutoRefresh() { + if (!state.workspaceHandle) { + return; + } + if (state.isDirty) { + return; + } + if (Date.now() - state.lastWorkspaceInteractionAt < MINIMUM_IDLE_MS) { + return; + } + try { + const permissionGranted = await ensurePermission(state.workspaceHandle, "read"); + if (!permissionGranted) { + throw new Error("Folder permission revoked."); + } + } catch { + stopAutoRefresh(); + showToast("Auto-refresh stopped: folder permission revoked.", { persist: true }); + setStatus("Permission revoked", "err"); + return; + } + await rescanWorkspace({ silent: true }); + } + function stopAutoRefresh() { + if (state.autoRefreshTimer) { + clearInterval(state.autoRefreshTimer); + state.autoRefreshTimer = null; + } + } + return { + startAutoRefresh, + stopAutoRefresh, + runAutoRefresh + }; +} +export { + createAutoRefresh +}; diff --git a/dist/test/cogito.js b/dist/test/cogito.js index 0f7e0dc..b77572d 100644 --- a/dist/test/cogito.js +++ b/dist/test/cogito.js @@ -6,12 +6,65 @@ Rules: - Do NOT suggest sentences. - Ask exactly 3 questions. - Questions must be grounded in the user's last sentence. +- Use document analysis only to focus what the questions explore. - Output JSON only in this format: { "questions": ["...", "...", "..."] }`; var LITE_MODEL = "Llama-3.2-1B-Instruct-q4f32_1-MLC"; var DEEP_MODEL = "Qwen3-8B-q4f16_1-MLC"; +var MAX_LAST_SENTENCE_CONTEXT_LENGTH = 1200; +var MAX_ANALYSIS_LINE_LENGTH = 240; +var MODEL_LOAD_RETRY_DELAY_MS = 750; +function compactContextLine(value, maximumLength, keepEnd = false) { + const normalized = value.replace(/\s+/g, " ").trim(); + if (normalized.length <= maximumLength) { + return normalized; + } + if (keepEnd) { + return `\u2026${normalized.slice(-(maximumLength - 1))}`; + } + return `${normalized.slice(0, maximumLength - 1)}\u2026`; +} +function isRecoverableModelLoadError(error) { + const message = error instanceof Error ? error.message : String(error); + return /cache|network|fetch|failed to execute ['"]?add|load failed|connection/i.test(message); +} +function wait(milliseconds) { + return new Promise((resolve) => { + setTimeout(resolve, milliseconds); + }); +} +function buildCogitoAnalysisContext(analysis) { + if (!analysis) { + return null; + } + return { + overallScore: analysis.overallScore, + priorities: analysis.overview.priorities.slice(0, 3).map((line) => compactContextLine(line, MAX_ANALYSIS_LINE_LENGTH)), + strengths: analysis.overview.strengths.slice(0, 2).map((line) => compactContextLine(line, MAX_ANALYSIS_LINE_LENGTH)) + }; +} +function buildCogitoUserPrompt(lastSentence, analysisContext) { + const compactLastSentence = compactContextLine( + lastSentence, + MAX_LAST_SENTENCE_CONTEXT_LENGTH, + true + ); + const lines = [`Last sentence: ${compactLastSentence}`]; + if (!analysisContext) { + lines.push("Document analysis: unavailable. Focus only on the last sentence."); + return lines.join("\n"); + } + const compactPriorities = analysisContext.priorities.slice(0, 3).map((line) => compactContextLine(line, MAX_ANALYSIS_LINE_LENGTH)); + const compactStrengths = analysisContext.strengths.slice(0, 2).map((line) => compactContextLine(line, MAX_ANALYSIS_LINE_LENGTH)); + lines.push( + `Document strength: ${analysisContext.overallScore}/100`, + `Highest-priority improvements: ${compactPriorities.join(" | ") || "No major fixes identified."}`, + `Current strengths: ${compactStrengths.join(" | ") || "No clear strengths identified yet."}` + ); + return lines.join("\n"); +} function extractLastSentence(text) { const normalized = text.replace(/\s+/g, " ").trim(); if (!normalized) { @@ -56,11 +109,14 @@ function createCogitoController({ getEditorText, onEditorContentReplaced, showToast, - setStatus + setStatus, + getDocumentAnalysis, + getAnalysisRevision }) { let isPanelOpen = false; let generatedQuestions = []; let selectedModel = "lite"; + let generatedAnalysisRevision = null; const engineCache = {}; async function loadWebLlmModule() { const testModule = globalThis.__INK_TEST_WEBLLM__; @@ -104,27 +160,66 @@ function createCogitoController({ function setCogitoStatus(text) { els.cogitoStatus.textContent = text; } - async function getOrCreateEngine() { - const modelId = selectedModel === "deep" ? DEEP_MODEL : LITE_MODEL; - if (engineCache[selectedModel]) { - return engineCache[selectedModel]; - } - engineCache[selectedModel] = (async () => { - setCogitoStatus(`Loading ${selectedModel === "deep" ? "Deep (Qwen3 8B)" : "Lite (Llama 1B)"} model...`); - const webllm = await loadWebLlmModule(); - const engine = await webllm.CreateMLCEngine(modelId, { + function getModelId(model) { + return model === "deep" ? DEEP_MODEL : LITE_MODEL; + } + function getModelLabel(model) { + return model === "deep" ? "Deep (Qwen3 8B)" : "Lite (Llama 1B)"; + } + async function createEngineWithRecovery(model) { + const modelId = getModelId(model); + const webllm = await loadWebLlmModule(); + const appConfig = { + ...webllm.prebuiltAppConfig, + cacheBackend: "indexeddb" + }; + async function createEngine() { + return webllm.CreateMLCEngine(modelId, { + appConfig, initProgressCallback: (progress) => { if (progress?.text) { setCogitoStatus(progress.text); } } }); - return engine; - })().catch((error) => { - delete engineCache[selectedModel]; + } + try { + return await createEngine(); + } catch (error) { + if (!isRecoverableModelLoadError(error)) { + throw error; + } + setCogitoStatus(`Repairing ${getModelLabel(model)} cache and retrying...`); + try { + await webllm.deleteModelAllInfoInCache?.(modelId, appConfig); + } catch { + } + await wait(MODEL_LOAD_RETRY_DELAY_MS); + return createEngine(); + } + } + async function getOrCreateEngine(model = selectedModel) { + if (engineCache[model]) { + return engineCache[model]; + } + setCogitoStatus(`Loading ${getModelLabel(model)} model...`); + engineCache[model] = createEngineWithRecovery(model).catch((error) => { + delete engineCache[model]; throw error; }); - return engineCache[selectedModel]; + return engineCache[model]; + } + async function getEngineWithFallback() { + try { + return await getOrCreateEngine(selectedModel); + } catch (error) { + if (selectedModel !== "deep" || !isRecoverableModelLoadError(error)) { + throw error; + } + setCogitoStatus("Deep model download failed. Falling back to Lite..."); + selectModel("lite"); + return getOrCreateEngine("lite"); + } } async function generateQuestions() { const lastSentence = extractLastSentence(getEditorText()); @@ -136,11 +231,13 @@ function createCogitoController({ try { els.cogitoGenerateBtn.disabled = true; setCogitoStatus("Generating 3 questions..."); - const engine = await getOrCreateEngine(); + const engine = await getEngineWithFallback(); + const analysisContext = buildCogitoAnalysisContext(getDocumentAnalysis()); + const analysisRevision = getAnalysisRevision(); const completion = await engine.chat.completions.create({ messages: [ { role: "system", content: COGITO_PROMPT }, - { role: "user", content: `Last sentence: ${lastSentence}` } + { role: "user", content: buildCogitoUserPrompt(lastSentence, analysisContext) } ], temperature: 0.2 }); @@ -150,16 +247,20 @@ function createCogitoController({ throw new Error("Cogito returned an empty response."); } generatedQuestions = parseCogitoQuestionPayload(textContent); + generatedAnalysisRevision = analysisContext ? analysisRevision : null; updateQuestionList(generatedQuestions); - setCogitoStatus("Questions ready. Insert one into your markdown when useful."); + setCogitoStatus( + analysisContext ? "Questions ready and focused by the current document analysis." : "Questions ready. Analyze the document for more focused coaching." + ); setStatus("Cogito questions ready", "ok"); } catch (error) { generatedQuestions = []; updateQuestionList(generatedQuestions); const message = error instanceof Error ? error.message : String(error); - setCogitoStatus(`Cogito error: ${message}`); + const friendlyMessage = isRecoverableModelLoadError(error) ? "The local model could not finish downloading. Check the connection and available browser storage, then retry." : message; + setCogitoStatus(`Cogito error: ${friendlyMessage}`); setStatus("Cogito unavailable", "warn"); - showToast(`Cogito failed: ${message}`, { persist: true }); + showToast(`Cogito failed: ${friendlyMessage}`, { persist: true }); } finally { els.cogitoGenerateBtn.disabled = false; } @@ -175,18 +276,28 @@ function createCogitoController({ onEditorContentReplaced(els.editor.value); setStatus("Inserted AI question", "ok"); } + function markAnalysisChanged(revision) { + if (generatedQuestions.length === 0 || generatedAnalysisRevision === revision) { + return; + } + setCogitoStatus("Document analysis changed. Regenerate to focus these questions on the latest findings."); + } setPanelVisibility(false); return { togglePanel: () => { setPanelVisibility(!isPanelOpen); if (isPanelOpen) { - setCogitoStatus("Cogito Mode enabled. Generate questions from your last sentence."); + setCogitoStatus( + getDocumentAnalysis() ? "Generate questions focused by the current document analysis." : "Analyze first for more focused coaching, or generate from your latest sentence now." + ); } }, setPanelOpen: (nextIsOpen) => { setPanelVisibility(nextIsOpen); if (nextIsOpen) { - setCogitoStatus("Cogito Mode enabled. Generate questions from your last sentence."); + setCogitoStatus( + getDocumentAnalysis() ? "Generate questions focused by the current document analysis." : "Analyze first for more focused coaching, or generate from your latest sentence now." + ); } }, isPanelOpen: () => isPanelOpen, @@ -195,16 +306,20 @@ function createCogitoController({ }, selectModel, generateQuestions, - insertQuestionAtIndex + insertQuestionAtIndex, + markAnalysisChanged }; } export { COGITO_PROMPT, DEEP_MODEL, LITE_MODEL, + buildCogitoAnalysisContext, + buildCogitoUserPrompt, createCogitoController, extractLastSentence, formatCogitoQuestionBlock, insertTextAtCursor, + isRecoverableModelLoadError, parseCogitoQuestionPayload }; diff --git a/dist/test/document-linter.js b/dist/test/document-linter.js index 8d5ea58..60faae8 100644 --- a/dist/test/document-linter.js +++ b/dist/test/document-linter.js @@ -108,6 +108,7 @@ var CATEGORY_META = [ { id: "style", title: "Style", color: "#9C27B0" }, { id: "structure", title: "Structure", color: "#607D8B" } ]; +var AUTO_RUN_DEBOUNCE_MS = 300; function clampScore(score) { return Math.max(0, Math.min(100, score)); } @@ -931,16 +932,32 @@ function createDocumentLinterController({ getEditorText, onEditorContentReplaced: _onEditorContentReplaced, showToast, - setStatus + setStatus, + onAnalysisUpdated }) { - let isPanelOpen = false; + let isActive = false; let isAnalyzing = false; let autoRunEnabled = false; let lastAnalysis = null; let lastTextSnapshot = ""; + let analysisRevision = 0; + let autoRunTimer = null; + let autoRunResolvers = []; async function runAnalysis(textSnapshot = getEditorText()) { return Promise.resolve(analyzeDocumentText(textSnapshot)); } + function resolveScheduledAutoRun() { + const resolvers = autoRunResolvers; + autoRunResolvers = []; + resolvers.forEach((resolve) => resolve()); + } + function cancelScheduledAutoRun() { + if (autoRunTimer !== null) { + clearTimeout(autoRunTimer); + autoRunTimer = null; + } + resolveScheduledAutoRun(); + } function scrollEditorToLine(lineNumber) { const lines = els.editor.value.split("\n"); const clampedLine = Math.max(1, Math.min(lineNumber, lines.length || 1)); @@ -954,15 +971,6 @@ function createDocumentLinterController({ const lineHeight = Number.isFinite(computedLineHeight) ? computedLineHeight : 20; els.editor.scrollTop = Math.max(0, (clampedLine - 1) * lineHeight); } - function setPanelVisibility(isOpen) { - isPanelOpen = isOpen; - els.documentLinterPanel.hidden = !isOpen; - els.documentLinterToggleBtn.setAttribute("aria-expanded", String(isOpen)); - const split = els.documentLinterPanel.closest(".split"); - if (split) { - split.classList.toggle("with-document-linter", isOpen); - } - } function updateResultsPanel(analysis) { const resultsContainer = els.documentLinterResults; const previousScrollTop = resultsContainer.scrollTop; @@ -1067,8 +1075,10 @@ function createDocumentLinterController({ try { const analysis = await runAnalysis(text); lastAnalysis = analysis; + analysisRevision += 1; updateResultsPanel(analysis); els.documentLinterStatus.textContent = "Analysis complete"; + onAnalysisUpdated?.(analysis, analysisRevision); showToast("Document analysis completed"); setStatus("Analysis complete", "ok"); } catch (error) { @@ -1096,6 +1106,11 @@ function createDocumentLinterController({ const analysis = needsFreshAnalysis ? await runAnalysis(text) : lastAnalysis; lastAnalysis = analysis; lastTextSnapshot = text; + if (needsFreshAnalysis) { + analysisRevision += 1; + updateResultsPanel(analysis); + onAnalysisUpdated?.(analysis, analysisRevision); + } const report = buildDocumentLinterReport(text, analysis); downloadMarkdownReport(report); showToast("Exported linter review as Markdown."); @@ -1106,8 +1121,9 @@ function createDocumentLinterController({ } } async function handleEditorChanged(textSnapshot = getEditorText()) { - if (!isPanelOpen || !autoRunEnabled || isAnalyzing || !textSnapshot.trim()) { - if (!textSnapshot.trim() && autoRunEnabled && isPanelOpen) { + if (!isActive || !autoRunEnabled || isAnalyzing || !textSnapshot.trim()) { + cancelScheduledAutoRun(); + if (!textSnapshot.trim() && autoRunEnabled && isActive) { lastAnalysis = null; lastTextSnapshot = textSnapshot; els.documentLinterStatus.textContent = "No content to analyze"; @@ -1117,11 +1133,26 @@ function createDocumentLinterController({ if (textSnapshot === lastTextSnapshot) { return Promise.resolve(); } - return analyzeDocumentAction(); + if (autoRunTimer !== null) { + clearTimeout(autoRunTimer); + } + els.documentLinterStatus.textContent = "Waiting for typing to pause..."; + return new Promise((resolve) => { + autoRunResolvers.push(resolve); + autoRunTimer = setTimeout(() => { + autoRunTimer = null; + const shouldAnalyze = isActive && autoRunEnabled && textSnapshot !== lastTextSnapshot; + const analysisPromise = shouldAnalyze ? analyzeDocumentAction() : Promise.resolve(); + analysisPromise.finally(resolveScheduledAutoRun); + }, AUTO_RUN_DEBOUNCE_MS); + }); } els.documentLinterAutoRunToggle.checked = autoRunEnabled; els.documentLinterAutoRunToggle.addEventListener("change", () => { autoRunEnabled = els.documentLinterAutoRunToggle.checked; + if (!autoRunEnabled) { + cancelScheduledAutoRun(); + } els.documentLinterStatus.textContent = autoRunEnabled ? "Rerun on change enabled" : "Rerun on change disabled"; }); els.documentLinterResults.addEventListener("click", (event) => { @@ -1136,27 +1167,21 @@ function createDocumentLinterController({ } scrollEditorToLine(lineNumber); }); - setPanelVisibility(false); return { - togglePanel: () => { - setPanelVisibility(!isPanelOpen); - if (isPanelOpen) { - els.documentLinterStatus.textContent = "Ready to analyze document"; + setActive: (nextIsActive) => { + isActive = nextIsActive; + if (!nextIsActive) { + cancelScheduledAutoRun(); } - }, - setPanelOpen: (nextIsOpen) => { - setPanelVisibility(nextIsOpen); - if (nextIsOpen) { + if (nextIsActive && !lastAnalysis) { els.documentLinterStatus.textContent = "Ready to analyze document"; } }, - isPanelOpen: () => isPanelOpen, - closePanel: () => { - setPanelVisibility(false); - }, handleEditorChanged, analyzeDocument: analyzeDocumentAction, - exportSuggestions: exportSuggestionsAction + exportSuggestions: exportSuggestionsAction, + getLatestAnalysis: () => lastAnalysis, + getAnalysisRevision: () => analysisRevision }; } export { diff --git a/ink-app.html b/ink-app.html index e8d6ec9..257b9d6 100644 --- a/ink-app.html +++ b/ink-app.html @@ -8,7 +8,7 @@ @@ -185,26 +185,14 @@ id="cogitoToggleBtn" class="menu-action-btn ghost" type="button" - title="Toggle Cogito Mode" - aria-label="Toggle Cogito Mode" + title="Toggle document strength and Cogito coaching" + aria-label="Toggle document strength and Cogito coaching" aria-expanded="false" aria-controls="cogitoPanel" > Cogito - @@ -243,7 +231,7 @@
-
Auto-refresh: On
+
Auto-refresh: Idle
@@ -287,9 +275,41 @@
- - +

Analyze first for more focused coaching, or generate from your latest sentence now.

+
    + + @@ -391,48 +397,48 @@ diff --git a/ink.template.html b/ink.template.html index 41c3703..d73935a 100644 --- a/ink.template.html +++ b/ink.template.html @@ -185,26 +185,14 @@ id="cogitoToggleBtn" class="menu-action-btn ghost" type="button" - title="Toggle Cogito Mode" - aria-label="Toggle Cogito Mode" + title="Toggle document strength and Cogito coaching" + aria-label="Toggle document strength and Cogito coaching" aria-expanded="false" aria-controls="cogitoPanel" > Cogito - @@ -243,7 +231,7 @@ -
    Auto-refresh: On
    +
    Auto-refresh: Idle
    @@ -287,9 +275,41 @@
    - - +

    Analyze first for more focused coaching, or generate from your latest sentence now.

    +
      + + diff --git a/openspec/changes/integrate-document-linter-into-cogito/design.md b/openspec/changes/integrate-document-linter-into-cogito/design.md new file mode 100644 index 0000000..37d3de8 --- /dev/null +++ b/openspec/changes/integrate-document-linter-into-cogito/design.md @@ -0,0 +1,60 @@ +## Context +Ink exposes Cogito and the Document Linter as mutually exclusive side panels. The linter explains what is strong or weak in the whole document, while Cogito asks questions about the latest sentence. Their separate entrypoints make related feedback feel like separate products and prevent Cogito from using the linter's deterministic findings to focus its coaching. + +The existing analysis engine, report builder, question parser, model selection, and markdown insertion behavior are useful boundaries and should remain independently testable. This change unifies their presentation and orchestration rather than merging all logic into one large module. + +## Goals / Non-Goals +- Goals: + - Provide one top-right Cogito entrypoint for document assessment and coaching. + - Show a clear, understandable document-strength summary before or alongside AI coaching. + - Let Cogito focus its three questions using deterministic linter findings. + - Preserve editing, line navigation, rerun-on-change, report export, and question insertion. + - Keep linting usable when WebLLM is unavailable or still loading. +- Non-Goals: + - Automatically rewriting the user's prose. + - Automatically applying linter suggestions. + - Replacing the deterministic linter with an LLM-based score. + - Changing the linter scoring rules or report schema except where presentation context requires it. + - Loading a language model merely to display document strength. + +## Decisions +- Decision: Use the existing Cogito menu bar button as the only writing-assistance entrypoint. + - Rationale: Cogito is the broader concept and can naturally contain both diagnosis and coaching. +- Decision: Render one side panel with two explicit areas: **Document strength** and **Improve with Cogito**. + - Rationale: a linear assess-then-improve flow is clearer than two competing panels while keeping each responsibility visible. +- Decision: Keep `analyzeDocumentText()` and `buildDocumentLinterReport()` as deterministic linter contracts, and keep Cogito generation in its dedicated module. + - Rationale: the unified experience should not create a monolithic controller or make analysis dependent on AI availability. +- Decision: Supply Cogito with a compact, structured summary of the latest linter analysis plus the latest sentence. + - Rationale: coaching should target observable weaknesses while preserving the current local context that makes questions specific. +- Decision: Preserve the strict JSON response contract containing exactly three questions and the explicit insert action. + - Rationale: the change should improve relevance without turning Cogito into an auto-writer. +- Decision: Run document analysis on explicit user action by default and preserve the existing rerun-on-change option. + - Rationale: this retains predictable cost and timing while allowing users who want live feedback to opt in. +- Decision: Debounce rerun-on-change by 300 ms and bound model context to the latest 1,200 sentence characters plus compact linter lines. + - Rationale: analysis should run once after a typing burst, and unusually long unpunctuated drafts should not inflate WebLLM input. +- Decision: If no current analysis exists, Cogito question generation may run with latest-sentence context alone and the UI should invite the user to analyze for more focused coaching. + - Rationale: analysis improves Cogito but should not become a hard dependency that blocks the existing coaching flow. + +## Risks / Trade-offs +- The unified panel may become visually dense. + - Mitigation: use clear section headings, compact score summaries, and progressive disclosure for detailed category and section feedback. +- Linter output may consume too much model context. + - Mitigation: pass only structured high-priority findings and strengths, not the rendered report or full analysis object. +- Rerun-on-change may update findings while generated questions are still visible. + - Mitigation: mark coaching as based on an earlier analysis snapshot and require explicit regeneration rather than silently replacing questions. +- Removing the standalone button may surprise existing users. + - Mitigation: keep all linter functions visible under the Cogito panel and use document-strength language in Cogito's accessible description and empty state. +- A combined controller could increase coupling. + - Mitigation: retain separate analysis and generation modules, with app-level orchestration passing only typed summaries between them. + +## Migration Plan +1. Restructure the template and styles around one Cogito panel containing document-strength and coaching sections. +2. Remove the standalone Linter button and its independent panel visibility state. +3. Rewire Document Linter controls and rendering into the Cogito panel while preserving analyzer/report contracts. +4. Add a compact analysis-to-Cogito context mapper and extend the fixed prompt input without changing the three-question output schema. +5. Update DOM types, controller orchestration, tests, documentation, and generated single-file output. +6. Verify the unified panel in Source, Split, Preview, and responsive layouts. + +## Resolved Questions +- Detailed linter categories and section notes remain expanded in the initial implementation so all existing feedback stays directly accessible. +- Opening Cogito retains an explicit first-run Analyze action. The coaching flow remains available without analysis and explains that analysis will make its questions more focused. diff --git a/openspec/changes/integrate-document-linter-into-cogito/proposal.md b/openspec/changes/integrate-document-linter-into-cogito/proposal.md new file mode 100644 index 0000000..d7e043b --- /dev/null +++ b/openspec/changes/integrate-document-linter-into-cogito/proposal.md @@ -0,0 +1,20 @@ +# Change: Integrate Document Linter into Cogito + +## Why +Cogito and the Document Linter currently compete as separate top-right actions and separate side panels even though they serve one writing workflow: understand the document's quality, then improve it. Combining them gives writers one clear place to assess document strength and receive focused coaching without choosing between overlapping tools. + +## What Changes +- Remove the standalone **Linter** menu bar button and keep **Cogito** as the single writing-assistance entrypoint. +- Replace the separate Cogito and Document Linter panels with one unified Cogito panel. +- Add a **Document strength** area inside Cogito that presents the linter's overall score, strengths, prioritized issues, category scores, and section-level feedback. +- Keep document analysis deterministic and available without loading the Cogito language model. +- Keep linter controls such as manual analysis, rerun-on-change, line navigation, and markdown report export within the unified panel. +- Add an **Improve with Cogito** area that generates exactly three question-only coaching prompts informed by the current document analysis and the writer's latest sentence. +- Preserve the existing Cogito model selection, recoverable model-loading states, explicit question insertion, and canonical AI markdown block format. +- Update unit and end-to-end coverage around the unified entrypoint, analysis flow, coaching flow, and removal of the standalone Linter UI. + +## Impact +- Affected specs: `cogito-mode`, `document-linter` +- Affected code: `ink.template.html`, panel layout styles, DOM references/types, app-controller orchestration, UI event wiring, Cogito prompt context, Document Linter controller, QUnit tests, Cypress tests, and user/agent guidance +- Runtime dependency: no new dependency; deterministic linting remains client-side and Cogito continues to load WebLLM on demand +- Breaking changes: the standalone Linter button and independently toggled Document Linter panel are removed diff --git a/openspec/changes/integrate-document-linter-into-cogito/specs/cogito-mode/spec.md b/openspec/changes/integrate-document-linter-into-cogito/specs/cogito-mode/spec.md new file mode 100644 index 0000000..4a66fe1 --- /dev/null +++ b/openspec/changes/integrate-document-linter-into-cogito/specs/cogito-mode/spec.md @@ -0,0 +1,65 @@ +## MODIFIED Requirements + +### Requirement: Cogito Mode Menu Bar Button +The system SHALL provide Cogito as the single writing-assistance button in the menu bar's top-right action area. + +#### Scenario: User sees the unified writing-assistance entrypoint +- **WHEN** the editor UI is visible +- **THEN** a Cogito button SHALL appear in the top-right menu bar actions +- **AND** the button SHALL expose an accessible label or description identifying document assessment and coaching +- **AND** no standalone Linter button SHALL appear + +#### Scenario: User toggles the unified Cogito panel +- **WHEN** the user clicks the Cogito top-right button +- **THEN** the system SHALL open or close the unified Cogito side panel +- **AND** the toggle action SHALL not interrupt normal editing flow + +### Requirement: Cogito Mode Side Panel +The system SHALL provide one Cogito side panel where users can assess document strength and generate writing-coach questions while editing markdown. + +#### Scenario: User opens Cogito +- **WHEN** the user enables Cogito +- **THEN** the editor SHALL show a Document strength area +- **AND** the editor SHALL show an Improve with Cogito area +- **AND** the panel SHALL not prevent normal markdown editing + +#### Scenario: Language model is unavailable +- **WHEN** WebLLM cannot initialize or generate questions +- **THEN** the Document strength area SHALL remain usable +- **AND** the system SHALL present a clear non-blocking coaching error state + +### Requirement: Fixed Coaching Prompt Contract +The system SHALL generate questions using a fixed writing-coach instruction that accepts the user's latest sentence and, when available, compact document-analysis context, and outputs JSON only with exactly three questions. + +#### Scenario: Prompt uses current analysis +- **WHEN** Cogito requests questions after a document analysis +- **THEN** the system SHALL include a compact representation of the current document strengths and highest-priority improvements +- **AND** the system SHALL include the user's latest sentence +- **AND** the system SHALL instruct the model not to write prose or suggest sentences +- **AND** the system SHALL require exactly three questions as `{ "questions": ["...", "...", "..."] }` +- **AND** no additional non-JSON content SHALL be accepted as valid output + +#### Scenario: Prompt runs without analysis +- **WHEN** Cogito requests questions before a document analysis exists +- **THEN** the system SHALL generate questions from the user's latest sentence +- **AND** the UI SHALL indicate that analyzing the document can make coaching more focused + +### Requirement: Last-Sentence Grounding +The system SHALL ground generated questions in the user's most recent sentence and use document-analysis context only to focus the coaching goal. + +#### Scenario: User has at least one sentence +- **WHEN** the user triggers question generation +- **THEN** the system SHALL extract the latest sentence from the current document +- **AND** each generated question SHALL remain relevant to that sentence +- **AND** available linter findings SHALL guide which weaknesses or strengths the questions explore + +## ADDED Requirements + +### Requirement: Analysis Snapshot Awareness +The system SHALL make it clear when displayed Cogito questions no longer match the latest document analysis. + +#### Scenario: Document analysis changes after question generation +- **WHEN** a new analysis completes after Cogito questions were generated +- **THEN** the existing questions SHALL remain visible +- **AND** the panel SHALL mark them as based on an earlier analysis snapshot +- **AND** the system SHALL require explicit regeneration before replacing them diff --git a/openspec/changes/integrate-document-linter-into-cogito/specs/document-linter/spec.md b/openspec/changes/integrate-document-linter-into-cogito/specs/document-linter/spec.md new file mode 100644 index 0000000..f13a4d7 --- /dev/null +++ b/openspec/changes/integrate-document-linter-into-cogito/specs/document-linter/spec.md @@ -0,0 +1,43 @@ +## MODIFIED Requirements + +### Requirement: Document Linter +The system SHALL provide deterministic markdown document analysis as the Document strength area within the unified Cogito panel. + +#### Scenario: Analyze current document from Cogito +- **WHEN** the user requests analysis in the Document strength area +- **THEN** the system SHALL parse the currently open document +- **AND** the system SHALL show an overall strength score +- **AND** the system SHALL return suggestions grouped by clarity, flow, scannability, and engagement +- **AND** the report SHALL highlight the highest-priority fixes and current strengths + +#### Scenario: Analysis remains independent from AI +- **WHEN** the Cogito language model is unavailable, loading, or disabled +- **THEN** document analysis SHALL remain available +- **AND** analysis results SHALL remain deterministic for the same document content + +#### Scenario: Generate section-specific suggestions +- **WHEN** the system analyzes a document +- **THEN** it SHALL return actionable suggestions tied to exact lines or sections +- **AND** line references SHALL navigate the editor to the relevant content +- **AND** markdown bullets, headings, quoted callouts, and code SHALL not be misclassified as generic prose + +#### Scenario: Provide document-strength metrics +- **WHEN** the system analyzes document prose and structure +- **THEN** it SHALL calculate category scores and an aggregate overall score +- **AND** it SHALL evaluate readability, headings, bullets, paragraph density, scan-friendly structure, and engagement signals + +#### Scenario: Rerun analysis after edits +- **WHEN** the user enables rerun-on-change and edits the document +- **THEN** the Document strength results SHALL refresh from the current content +- **AND** any existing Cogito questions SHALL not be silently regenerated + +#### Scenario: Export suggestions as markdown +- **WHEN** the user requests export from the Document strength area +- **THEN** the system SHALL output the suggestions in markdown format +- **AND** the export SHALL include a concise summary, prioritized fixes, strengths, scores, and section-level detail + +#### Scenario: Standalone linter entrypoint is removed +- **WHEN** the editor UI is visible +- **THEN** the system SHALL not show a standalone Linter menu bar button +- **AND** the system SHALL not expose an independently toggled Document Linter side panel +- **AND** all retained linter actions SHALL be available from the unified Cogito panel diff --git a/openspec/changes/integrate-document-linter-into-cogito/tasks.md b/openspec/changes/integrate-document-linter-into-cogito/tasks.md new file mode 100644 index 0000000..24653f3 --- /dev/null +++ b/openspec/changes/integrate-document-linter-into-cogito/tasks.md @@ -0,0 +1,27 @@ +## 1. Unified panel structure +- [x] 1.1 Remove the standalone Linter menu bar button and retain Cogito as the single writing-assistance entrypoint. +- [x] 1.2 Replace the separate Cogito and Document Linter sidebars with one Cogito panel containing Document strength and Improve with Cogito sections. +- [x] 1.3 Update panel styles and responsive layouts so the unified panel works in Source, Split, and Preview modes. +- [x] 1.4 Update accessible labels, descriptions, status copy, and focus behavior for the combined experience. + +## 2. Document strength integration +- [x] 2.1 Render the existing overall score, strengths, priorities, category scores, and section feedback in the Document strength area. +- [x] 2.2 Preserve manual Analyze, rerun-on-change, clickable line navigation, and markdown report export inside Cogito. +- [x] 2.3 Keep `analyzeDocumentText()` and `buildDocumentLinterReport()` stable and independent from WebLLM availability. +- [x] 2.4 Remove obsolete standalone panel visibility state, CSS classes, DOM references, and toggle event wiring. + +## 3. Cogito coaching integration +- [x] 3.1 Add a typed mapper that reduces the latest linter analysis to compact strengths and prioritized improvement context. +- [x] 3.2 Extend Cogito generation input with the compact analysis context while preserving latest-sentence grounding. +- [x] 3.3 Preserve the strict JSON-only response with exactly three questions, model selection, error states, and explicit markdown insertion. +- [x] 3.4 Indicate when generated questions use an older analysis snapshot and require explicit regeneration after analysis changes. + +## 4. Validation +- [x] 4.1 Update QUnit tests for DOM/controller behavior, analysis context mapping, prompt construction, and retained report contracts. +- [x] 4.2 Replace separate Cogito and Document Linter Cypress flows with coverage for the unified entrypoint and full assess-then-improve workflow. +- [x] 4.3 Add assertions that no standalone Linter button or independently toggled linter panel remains. +- [x] 4.4 Run lint, build, QUnit, and Cypress checks and manually smoke-test responsive panel behavior. + +## 5. Documentation +- [x] 5.1 Update user-facing documentation to explain document strength and Cogito coaching as one workflow. +- [x] 5.2 Update repository agent guidance to describe the unified panel boundaries and preserved analyzer/generation contracts. diff --git a/repomix-output.xml b/repomix-output.xml index e1f5bad..ddaa3f3 100644 --- a/repomix-output.xml +++ b/repomix-output.xml @@ -395,6 +395,13 @@ openspec/ design.md proposal.md tasks.md + add-progressive-workspace-loading/ + specs/ + workspace-loading/ + spec.md + design.md + proposal.md + tasks.md add-webmcp-notes/ proposal.md spec.md @@ -411,6 +418,12 @@ openspec/ spec.md proposal.md tasks.md + fix-mobile-textarea-zoom/ + specs/ + mobile-support/ + spec.md + proposal.md + tasks.md harden-document-linter/ proposal.md tasks.md @@ -420,6 +433,15 @@ openspec/ spec.md proposal.md tasks.md + integrate-document-linter-into-cogito/ + specs/ + cogito-mode/ + spec.md + document-linter/ + spec.md + design.md + proposal.md + tasks.md migrate-marked-to-npm-dependency/ proposal.md tasks.md @@ -509,6 +531,7 @@ src/ tests/ qunit/ auth.test.js + auto-refresh.test.js cogito.test.js document-linter.test.js editor-preview.test.js @@ -27875,6 +27898,190 @@ The current left menu is fixed, which reduces usable editor space on smaller scr - [x] 4.4 Add a Cypress end-to-end test that validates left menu collapse/expand behavior and resulting layout changes. + +## ADDED Requirements + +### Requirement: CSS Custom Property Theme System +The application SHALL implement color themes using CSS custom properties, with each theme defined as a set of variable overrides on `:root[data-theme=""]`. + +#### Scenario: Default theme at initial load +- **WHEN** the application loads with no saved theme preference +- **THEN** no `data-theme` attribute is present on `` +- **AND** the default dark teal palette is applied via `:root` base variables + +#### Scenario: Named theme activation +- **WHEN** a named theme (e.g. `monokai`) is applied +- **THEN** `data-theme="monokai"` is set on `` +- **AND** the corresponding CSS variable overrides take effect immediately across all elements +- **AND** no page reload is required + +#### Scenario: Returning to default +- **WHEN** the user selects Default (Dark) after a named theme is active +- **THEN** the `data-theme` attribute is removed from `` +- **AND** the base `:root` variable definitions are restored + +### Requirement: Theme Persistence +The application SHALL persist the user's theme choice across sessions using `localStorage`. + +#### Scenario: Theme saved on selection +- **WHEN** the user selects a theme from the View menu +- **THEN** the theme identifier is written to `localStorage` under the key `ink-theme` + +#### Scenario: Theme restored on load +- **WHEN** the application initialises and a valid theme identifier exists in `localStorage` +- **THEN** that theme is applied before the first render +- **AND** the user sees their previously chosen colours immediately, without a flash of the default theme + +#### Scenario: Invalid or missing stored value +- **WHEN** the application initialises and `localStorage` contains no `ink-theme` key or an unrecognised value +- **THEN** the default dark theme is applied +- **AND** no error is shown to the user + +### Requirement: Available Colour Styles +The application SHALL provide the following seven colour styles, modeled on RStudio's built-in themes. + +| Theme | Character | +|-------|-----------| +| Default (Dark) | Dark teal, original ink palette | +| Classic | Light neutral white/grey, blue accent | +| Cobalt | Dark ocean blue, gold accent | +| Monokai | Dark charcoal, green/pink highlights | +| Office | Clean professional light, Microsoft blue | +| Twilight | Warm dark grey, amber accent | +| Xcode | Crisp Apple-style light, Xcode blue | + +#### Scenario: Each theme covers all required variables +- **WHEN** any named theme is active +- **THEN** all CSS custom properties (`--bg`, `--panel`, `--panel2`, `--border`, `--muted`, `--text`, `--accent`, `--danger`, `--ok`, `--warn`, `--shadow`, `--body-bg`) are defined +- **AND** no variable falls back to an unintended value from another theme + + + +## ADDED Requirements + +### Requirement: View Top-Level Menu +The application menu bar SHALL include a View top-level menu positioned between the Edit menu and the Import/Export menu. + +#### Scenario: View menu is visible in the menu bar +- **WHEN** the application loads +- **THEN** a "View" menu item is present in the menu bar +- **AND** it appears between "Edit" and "Import/Export" + +#### Scenario: View menu opens a dropdown +- **WHEN** the user clicks the View menu item +- **THEN** a dropdown appears listing all available colour styles +- **AND** the dropdown follows the same interaction model as File and Edit menus + +### Requirement: Colour Style Selection +The View menu dropdown SHALL list all available colour styles and allow the user to switch between them with a single click. + +#### Scenario: All themes listed +- **WHEN** the View dropdown is open +- **THEN** the following items are present in order: Default (Dark), [separator], Classic, Cobalt, Monokai, Office, Twilight, Xcode + +#### Scenario: Selecting a theme +- **WHEN** the user clicks a theme name in the View dropdown +- **THEN** the corresponding theme is applied immediately +- **AND** the dropdown closes +- **AND** the application retains full functionality under the new colour scheme + +### Requirement: Active Theme Indicator +The View menu SHALL display a checkmark (✓) next to the currently active theme. + +#### Scenario: Checkmark on active theme +- **WHEN** the View dropdown is open +- **THEN** a ✓ indicator is visible next to the currently active theme name +- **AND** all other theme entries show no checkmark + +#### Scenario: Checkmark updates on selection +- **WHEN** the user selects a different theme +- **THEN** the checkmark moves to the newly selected theme +- **AND** the previous theme entry no longer shows a checkmark + +### Requirement: Backward Compatibility +Adding the View menu SHALL not affect any existing menu, button, or keyboard shortcut behaviour. + +#### Scenario: Existing menus unaffected +- **WHEN** the View menu is present +- **THEN** File, Edit, and Import/Export menus continue to operate identically to their prior behaviour + +#### Scenario: Existing buttons unaffected +- **WHEN** a colour theme is active +- **THEN** all sidebar buttons, Save, JSON, and MD buttons continue to function correctly +- **AND** their visual style adapts to the active theme via CSS custom properties + + + +# Change: Add Color Style Themes + +## Why + +Ink currently uses a single fixed dark color scheme. Users have different preferences and work in different lighting environments, so a fixed style limits comfort and accessibility. Providing a set of well-known color themes — modeled on those available in RStudio — gives users immediate visual choices without requiring any configuration beyond a single menu click. + +## What Changes + +- Add a **View** top-level menu to the existing office-style menu bar, placed between Edit and Import/Export. +- The View menu lists seven color styles: Default (Dark), Classic, Cobalt, Monokai, Office, Twilight, and Xcode. +- Selecting a style applies it immediately by setting a `data-theme` attribute on the `` element. +- Each theme is defined as a set of CSS custom property overrides in `src/styles.scss` using `:root[data-theme=""]` selectors. +- The active theme is indicated by a checkmark (✓) next to the selected item in the menu. +- The selected theme is persisted to `localStorage` under the key `ink-theme` and restored on next load. +- The body background gradient is theme-aware via a `--body-bg` CSS custom property. + +## Impact + +- Affected specs: `theming`, `view-menu` +- Affected code: + - `ink.template.html` — View menu added + - `src/styles.scss` — theme CSS custom properties added, `--body-bg` variable introduced + - `src/app/bootstrap.ts` — `applyTheme()` and `loadTheme()` methods added; new `theme-*` action cases in `handleMenuAction()` + - `dist/app.min.js` (rebuilt) + - `dist/styles.min.css` (rebuilt) + - `ink-app.html` (rebuilt) + + + +## 1. CSS Theme System + +- [x] 1.1 Introduce `--body-bg` CSS custom property to allow per-theme control of the body background gradient. +- [x] 1.2 Define `Default (Dark)` base theme variables in `:root` (existing dark teal palette, refactored to use `--body-bg`). +- [x] 1.3 Define `Classic` theme — light neutral white/grey, blue accent (`#2c6fad`). +- [x] 1.4 Define `Cobalt` theme — dark ocean blue (`#001f3d`) with gold accent (`#ffd700`) and a blue radial gradient. +- [x] 1.5 Define `Monokai` theme — dark (`#272822`) with green accent (`#a6e22e`) and pink/green radial gradients. +- [x] 1.6 Define `Office` theme — clean professional light, Microsoft blue accent (`#0078d4`). +- [x] 1.7 Define `Twilight` theme — warm dark grey (`#141414`), amber accent (`#d4a96a`). +- [x] 1.8 Define `Xcode` theme — crisp light (`#f9f9f9`), Apple blue accent (`#0070c1`). +- [x] 1.9 Add `.menu-theme-check` CSS class for the active-theme checkmark indicator in the dropdown. + +## 2. View Menu (HTML Template) + +- [x] 2.1 Add a `View` menu item to `ink.template.html` between Edit and Import/Export. +- [x] 2.2 Add dropdown list items for all seven themes, each with `data-action="theme-"`. +- [x] 2.3 Add `` to each theme item for active-state indication. +- [x] 2.4 Include a separator between Default (Dark) and the six named themes. + +## 3. Theme Logic (TypeScript) + +- [x] 3.1 Add `VALID_THEMES` constant array listing all accepted theme identifiers. +- [x] 3.2 Implement `applyTheme(theme: string)` method that sets/removes the `data-theme` attribute on `document.documentElement`, saves to `localStorage`, and updates the checkmark indicator. +- [x] 3.3 Implement `loadTheme()` method that reads `localStorage` on startup and calls `applyTheme()` with the saved value (defaulting to `"default"` if absent or invalid). +- [x] 3.4 Call `loadTheme()` from `initialize()` before other UI setup. +- [x] 3.5 Add `theme-default`, `theme-classic`, `theme-cobalt`, `theme-monokai`, `theme-office`, `theme-twilight`, and `theme-xcode` cases to `handleMenuAction()`. + +## 4. Build and Verification + +- [x] 4.1 Run `npm run build` to recompile TypeScript, SCSS, and assemble `ink-app.html`. +- [x] 4.2 Verify the View menu appears in the menu bar between Edit and Import/Export. +- [x] 4.3 Verify each theme applies immediately on selection with correct colours. +- [x] 4.4 Verify the checkmark moves to the newly selected theme. +- [x] 4.5 Verify the selected theme persists across page reloads via `localStorage`. + +## 5. Documentation + +- [x] 5.1 Update `replit.md` with a Color Themes section describing each theme and the persistence mechanism. +- [x] 5.2 Create this openspec change request. + + ## ADDED Requirements ### Requirement: Componentized Source Inputs @@ -28087,6 +28294,7 @@ Ink needs a repeatable way to show first-time users the core authoring flow with - [x] 1.3 Add npm scripts for recording and GIF generation. - [x] 1.4 Document demo asset generation in README and agent guidance. - [x] 1.5 Run focused validation for the demo workflow and project checks. +- [x] 1.6 Add Makefile targets and help entries for demo generation and source line counting. @@ -29021,6 +29229,608 @@ The ink markdown note-taking application currently lacks a proper office-style m - [x] 1.8.5 Code review and cleanup + +## ADDED Requirements + +### Requirement: Progressive Workspace Discovery +The system SHALL publish discovered folders and Markdown filenames during initial workspace loading before all file metadata and tags have been hydrated. + +#### Scenario: Large vault begins loading +- **WHEN** the user selects a workspace containing nested directories and many Markdown files +- **THEN** the workspace tree SHALL show discovered folders and files in batches while scanning continues +- **AND** the user SHALL not need to wait for the complete vault scan before seeing the first discovered content + +#### Scenario: Discovered file is not yet hydrated +- **WHEN** a Markdown file has been discovered but its metadata and tags are still loading +- **THEN** the file SHALL appear in the workspace tree +- **AND** the user SHALL be able to open it +- **AND** missing metadata SHALL use safe temporary defaults until hydration completes + +### Requirement: Bounded Parallel Scanning +The system SHALL scan independent directories and hydrate discovered Markdown files concurrently using separate bounded worker queues. + +#### Scenario: Vault contains multiple directories +- **WHEN** independent directories are waiting to be enumerated +- **THEN** the system SHALL enumerate more than one directory concurrently +- **AND** directory concurrency SHALL not exceed the configured limit + +#### Scenario: Many Markdown files require hydration +- **WHEN** discovered files require metadata and tag extraction +- **THEN** the system SHALL hydrate multiple files concurrently +- **AND** file-read concurrency SHALL not exceed the configured limit +- **AND** each file SHALL be opened at most once during one scan + +### Requirement: Batched Progressive Rendering +The system SHALL coalesce progressive scan updates into bounded UI batches. + +#### Scenario: Many items are discovered rapidly +- **WHEN** multiple folders or files are discovered within one batch window +- **THEN** the system SHALL publish them together +- **AND** the system SHALL not rebuild the workspace tree once per discovered item + +#### Scenario: Discovery is slow +- **WHEN** filesystem entries arrive slowly and the item threshold is not reached +- **THEN** the system SHALL publish available progress after the configured time window + +### Requirement: Workspace Loading Progress +The system SHALL expose meaningful progress throughout initial workspace loading. + +#### Scenario: Workspace is loading +- **WHEN** directory discovery or file hydration remains incomplete +- **THEN** the status UI SHALL indicate the current loading phase and available progress counts + +#### Scenario: Workspace finishes loading +- **WHEN** all discovery and hydration work completes +- **THEN** the status UI SHALL report that the workspace is ready +- **AND** the displayed note count SHALL match the successfully loaded Markdown files + +### Requirement: Stale Scan Cancellation +The system SHALL prevent obsolete workspace scans from mutating current application state. + +#### Scenario: User closes workspace during loading +- **WHEN** the user closes the workspace while a progressive scan is running +- **THEN** later results from that scan SHALL be ignored +- **AND** the closed workspace UI SHALL remain cleared + +#### Scenario: User selects another workspace during loading +- **WHEN** a newer workspace scan starts before an older scan finishes +- **THEN** only the newer scan SHALL publish state and UI updates + +### Requirement: Atomic Workspace Refresh +The system SHALL preserve the existing rendered workspace while manual or automatic refresh scans run. + +#### Scenario: Existing workspace refreshes +- **WHEN** a loaded workspace is rescanned +- **THEN** the current tree SHALL remain available until the new scan snapshot is complete +- **AND** the completed snapshot SHALL replace the previous workspace state in one commit + + + +## Context +The current scanner has two phases: recursively enumerate the entire directory tree, then read every Markdown file to collect metadata and tags. Although file reads now use a bounded worker pool, directory traversal remains depth-first and the UI receives no tree until both phases finish. + +An Obsidian vault makes this especially visible because it often has many nested folders and cloud-backed files. Ink needs a fast time-to-first-tree without creating uncontrolled filesystem concurrency or continuously rebuilding the DOM. + +## Goals / Non-Goals +- Goals: + - Show useful folders and filenames before the full vault has loaded. + - Parallelize independent directory enumeration and file hydration with explicit concurrency limits. + - Keep the main thread responsive by batching state and DOM updates. + - Keep discovered files usable while metadata and tags are still loading. + - Prevent stale scans from updating a closed or replaced workspace. + - Preserve stable background refresh behavior. +- Non-Goals: + - Indexing full note content during initial workspace open. + - Persisting a cross-session search index. + - Watching filesystem changes through a new external dependency. + - Rendering every discovered item immediately as an individual DOM mutation. + - Parsing Obsidian-specific links, canvases, or plugin metadata. + +## Decisions +- Decision: Split scanning into directory discovery and file hydration queues. + - Directory discovery adds folder nodes and lightweight file records containing path/handle information. + - File hydration fills size, modification time, and tags from a single bounded file read. + - Rationale: filenames become visible without waiting for file content reads. +- Decision: Use separate bounded concurrency limits. + - Directory enumeration starts with a conservative limit of 4 workers. + - File hydration uses a limit of 4 workers so background vault reads do not starve foreground note opens. + - Rationale: directory iteration and cloud-backed file reads have different costs, and unbounded parallelism can overwhelm browser or filesystem resources. +- Decision: Publish initial-open updates in batches. + - Flush when either a small item threshold is reached or a short time window elapses. + - Sort only changed directory children before each batch commit. + - Rationale: users see steady progress without a full render for every discovered file. +- Decision: Use a scan generation token. + - Every open/close operation invalidates previous work. + - Queue workers and UI flushes check the token before mutating state or DOM. + - Rationale: File System Access operations do not provide a universal abort signal. +- Decision: Make files navigable before hydration completes. + - Lightweight note records use explicit hydration state and safe default metadata. + - Opening a note reads the file normally from its handle. + - Rationale: metadata is not required to open or edit a discovered note. +- Decision: Keep refresh scans atomic. + - Manual and automatic refresh build a new snapshot and swap it in after completion. + - Only initial workspace opening uses progressive publication. + - Rationale: progressive refresh would repeatedly move or flicker an existing tree. +- Decision: Defer tag filtering accuracy until hydration catches up. + - Filename/path browsing works immediately. + - Tag controls update in batches as tags become available. + - The UI exposes loading progress so incomplete tag results are understandable. + +## Risks / Trade-offs +- Frequent progressive updates could make large trees expensive to render. + - Mitigation: batch updates and coalesce render requests. +- Parallel directory iteration may put pressure on cloud-backed providers. + - Mitigation: use a conservative directory worker limit and keep file hydration separately bounded. +- Sorting partially loaded directories can move rows while scanning. + - Mitigation: sort per batch and avoid reordering more frequently than the batch cadence. +- Users may filter by a tag before hydration completes. + - Mitigation: update tag controls progressively and label the workspace as still loading. +- A stale asynchronous operation may finish after a new workspace opens. + - Mitigation: generation checks before all state/UI commits. + +## Migration Plan +1. Add explicit discovery/hydration state to workspace note records. +2. Extract a bounded directory queue and reuse the bounded file hydration queue. +3. Add progressive batch publication for initial workspace opening. +4. Add scan-generation invalidation to open and close flows. +5. Keep refresh paths on the atomic scanner mode. +6. Add delayed multi-directory Cypress fixtures and verify time-to-first-tree, bounded concurrency, final counts, and cancellation. + +## Open Questions +- The initial batch thresholds should be tuned against a real large Obsidian vault after implementation; proposed defaults are 40 discovered items or 75 ms, whichever comes first. + + + +# Change: Add Progressive Workspace Loading + +## Why +Large Obsidian vaults can contain thousands of Markdown files spread across many directories. Ink currently waits for complete directory discovery and metadata/tag hydration before rendering the workspace tree, so the application appears stuck even when useful folders and filenames have already been found. + +## What Changes +- Render discovered folders and Markdown filenames incrementally while the initial workspace scan continues. +- Traverse independent directories through a bounded worker queue instead of recursively waiting for each directory in sequence. +- Hydrate file metadata and tags through a separate bounded worker queue while keeping discovered files immediately navigable. +- Commit discovery and hydration updates to the UI in batches so large vaults do not trigger a full tree render for every file. +- Show live progress for directory discovery, file discovery, and metadata hydration. +- Cancel stale scan work when the user closes the workspace or opens a different folder. +- Prioritize foreground note opens by cancelling obsolete background hydration work and only scheduling automatic refresh after an idle interval. +- Keep automatic and manual refreshes atomic by default so an already-rendered tree does not flicker during background rescans. +- Add performance-focused Cypress coverage using a delayed, multi-directory synthetic Obsidian-style vault. + +## Impact +- Affected specs: `workspace-loading` (new capability) +- Affected code: workspace scanner, workspace state/types, tree renderer, search/tag behavior during hydration, auto-refresh coordination, and workspace Cypress coverage +- Runtime dependency: none +- Breaking changes: none; workspace contents and supported File System Access API behavior remain the same + + + +## 1. Scanner architecture +- [ ] 1.1 Add explicit discovered/hydrated state to workspace note records. +- [ ] 1.2 Implement a bounded directory discovery queue with a conservative worker limit. +- [ ] 1.3 Keep file metadata/tag hydration in a separate bounded worker queue with one file read per note. +- [ ] 1.4 Add scan-generation invalidation for workspace open, replacement, and close. + +## 2. Progressive initial open +- [ ] 2.1 Publish discovered folders and filenames before metadata hydration completes. +- [ ] 2.2 Batch state/tree commits by item threshold or elapsed time. +- [ ] 2.3 Coalesce tree and tag renders so batches do not overlap or queue redundant DOM work. +- [ ] 2.4 Show live discovery and hydration progress and finish with the authoritative note count. + +## 3. Behavior during loading +- [ ] 3.1 Keep discovered files openable before metadata/tag hydration completes. +- [ ] 3.2 Keep filename/path browsing accurate during loading. +- [ ] 3.3 Update tag controls incrementally as hydrated tags become available. +- [ ] 3.4 Preserve atomic behavior for manual and automatic refresh scans. + +## 4. Validation +- [ ] 4.1 Add Cypress coverage for time-to-first-tree with delayed nested directory enumeration. +- [ ] 4.2 Assert bounded directory and file-read concurrency. +- [ ] 4.3 Assert incremental counts/tree updates and final complete state. +- [ ] 4.4 Assert stale scan cancellation when a workspace is closed or replaced. +- [ ] 4.5 Run lint, build, QUnit, full Cypress, and a manual large-vault smoke test. + +## 5. Documentation +- [ ] 5.1 Document progressive loading behavior and partial tag availability. +- [ ] 5.2 Update repository guidance with scanner concurrency, batching, and cancellation invariants. + + + +# Spec: Mobile Layout + +## Sidebar Toggle Always Visible on Small Screens + +The sidebar toggle button (`#sidebarToggleBtn`) SHALL be visible and interactive at all viewport widths, including those ≤ 980 px. + +#### Scenario: App loads on a mobile device — sidebar expanded + +- **GIVEN** a viewport width of 390 px +- **WHEN** the app finishes loading +- **THEN** the sidebar toggle button is rendered as a full-width horizontal button inside the sidebar +- **AND** the button label reads "▶ Collapse" with `aria-label="Collapse sidebar"` + +#### Scenario: App loads on a mobile device — sidebar collapsed + +- **GIVEN** a viewport width of 390 px and the sidebar state persisted as collapsed +- **WHEN** the app finishes loading +- **THEN** the sidebar is shown as a thin horizontal strip +- **AND** the toggle button is visible within that strip, reading "▼ Expand" with `aria-label="Expand sidebar"` + +--- + +## Sidebar Collapses and Expands Vertically on Mobile + +On small screens the sidebar SHALL collapse and expand vertically (row height), not horizontally (column width). + +#### Scenario: User collapses sidebar on mobile + +- **GIVEN** the sidebar is expanded on a ≤ 980 px viewport +- **WHEN** the user taps the sidebar toggle button +- **THEN** the sidebar shrinks to a horizontal strip showing only the toggle button +- **AND** the editor and preview area expand vertically to occupy the freed space +- **AND** the layout remains a single full-width column (no narrow sidebar column appears alongside the editor) + +#### Scenario: User expands sidebar on mobile + +- **GIVEN** the sidebar is collapsed on a ≤ 980 px viewport +- **WHEN** the user taps the toggle strip +- **THEN** the sidebar expands to show the full workspace panel (workspace name, search, file tree, tags) +- **AND** the editor area returns to its position below the sidebar + +--- + +## Editor and Preview Fill Available Vertical Space + +The editor (`