[{"data":1,"prerenderedAt":1223},["ShallowReactive",2],{"navigation_docs":3,"-guides-index-pg":412,"-guides-index-pg-surround":1218},[4,38,84,326,386],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":37},"Getting started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20,24,28,32],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","1.getting-started\u002F1.introduction","i-lucide-book-open",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":6},"Quick start: Nuxt","\u002Fgetting-started\u002Fquick-start-nuxt","1.getting-started\u002F3.quick-start-nuxt",{"title":25,"path":26,"stem":27,"icon":6},"Quick start: plain Vue","\u002Fgetting-started\u002Fquick-start-vue","1.getting-started\u002F4.quick-start-vue",{"title":29,"path":30,"stem":31,"icon":6},"Quick start: React","\u002Fgetting-started\u002Fquick-start-react","1.getting-started\u002F5.quick-start-react",{"title":33,"path":34,"stem":35,"icon":36},"Glossary","\u002Fgetting-started\u002Fglossary","1.getting-started\u002F6.glossary","i-lucide-book-a",false,{"title":39,"icon":40,"path":41,"stem":42,"children":43,"page":37},"Tutorial","i-lucide-graduation-cap","\u002Ftutorial","2.tutorial",[44,49,54,59,64,69,74,79],{"title":45,"path":46,"stem":47,"icon":48},"Tutorial: overview","\u002Ftutorial\u002Foverview","2.tutorial\u002F1.overview","i-lucide-map",{"title":50,"path":51,"stem":52,"icon":53},"Chapter 2: A simple editor","\u002Ftutorial\u002Fsimple-editor","2.tutorial\u002F2.simple-editor","i-lucide-pencil",{"title":55,"path":56,"stem":57,"icon":58},"Chapter 3: Compose features","\u002Ftutorial\u002Fcompose-features","2.tutorial\u002F3.compose-features","i-lucide-blocks",{"title":60,"path":61,"stem":62,"icon":63},"Chapter 4: Your own block, path A","\u002Ftutorial\u002Fown-block-path-a","2.tutorial\u002F4.own-block-path-a","i-lucide-square-plus",{"title":65,"path":66,"stem":67,"icon":68},"Chapter 5: Your own feature, path B","\u002Ftutorial\u002Fown-feature-path-b","2.tutorial\u002F5.own-feature-path-b","i-lucide-puzzle",{"title":70,"path":71,"stem":72,"icon":73},"Chapter 6: Backend and collaboration","\u002Ftutorial\u002Fbackend-and-collaboration","2.tutorial\u002F6.backend-and-collaboration","i-lucide-users",{"title":75,"path":76,"stem":77,"icon":78},"Chapter 7: AI","\u002Ftutorial\u002Fai","2.tutorial\u002F7.ai","i-lucide-sparkles",{"title":80,"path":81,"stem":82,"icon":83},"Chapter 8: The React surface","\u002Ftutorial\u002Freact-surface","2.tutorial\u002F8.react-surface","i-lucide-atom",{"title":85,"icon":14,"path":86,"stem":87,"children":88,"page":37},"Guides","\u002Fguides","3.guides",[89,93,98,103,108,112,116,121,126,130,134,139,144,149,154,159,164,169,174,179,184,189,194,199,204,209,213,218,223,228,233,237,242,247,251,256,261,266,271,276,281,286,291,295,302,307,311,316,321],{"title":90,"path":91,"stem":92,"icon":58},"Feature composition and entries","\u002Fguides\u002Fcomposition","3.guides\u002F1.composition",{"title":94,"path":95,"stem":96,"icon":97},"Media, mentions and transcript features","\u002Fguides\u002Fmedia-mentions-transcript","3.guides\u002F10.media-mentions-transcript","i-lucide-image",{"title":99,"path":100,"stem":101,"icon":102},"Authors and comments","\u002Fguides\u002Fauthors-and-comments","3.guides\u002F11.authors-and-comments","i-lucide-message-square-text",{"title":104,"path":105,"stem":106,"icon":107},"Themes (Obsidian)","\u002Fguides\u002Fthemes","3.guides\u002F12.themes","i-lucide-palette",{"title":109,"path":110,"stem":111,"icon":58},"Composites and the settings UI","\u002Fguides\u002Fcomposites","3.guides\u002F13.composites",{"title":113,"path":114,"stem":115,"icon":78},"AI","\u002Fguides\u002Fai","3.guides\u002F14.ai",{"title":117,"path":118,"stem":119,"icon":120},"Document tree","\u002Fguides\u002Fdocument-tree","3.guides\u002F15.document-tree","i-lucide-folder-tree",{"title":122,"path":123,"stem":124,"icon":125},"Markdown and code","\u002Fguides\u002Fmarkdown-and-code","3.guides\u002F16.markdown-and-code","i-lucide-file-code",{"title":127,"path":128,"stem":129,"icon":83},"Advanced editor in React","\u002Fguides\u002Fadvanced-react","3.guides\u002F17.advanced-react",{"title":131,"path":132,"stem":133,"icon":6},"Kitchen sink: learning path","\u002Fguides\u002Fadvanced-nuxt-example","3.guides\u002F18.advanced-nuxt-example",{"title":135,"path":136,"stem":137,"icon":138},"Production","\u002Fguides\u002Fproduction","3.guides\u002F19.production","i-lucide-server",{"title":140,"path":141,"stem":142,"icon":143},"Register the Nuxt module","\u002Fguides\u002Fnuxt-module","3.guides\u002F2.nuxt-module","i-lucide-package",{"title":145,"path":146,"stem":147,"icon":148},"Storage adapters, comment stores, Nitro host","\u002Fguides\u002Fstorage-adapters","3.guides\u002F20.storage-adapters","i-lucide-database",{"title":150,"path":151,"stem":152,"icon":153},"Migrating from #tiptap-editor","\u002Fguides\u002Fmigration","3.guides\u002F21.migration","i-lucide-arrow-right-left",{"title":155,"path":156,"stem":157,"icon":158},"Content tree mechanics","\u002Fguides\u002Fcontent-tree","3.guides\u002F22.content-tree","i-lucide-git-fork",{"title":160,"path":161,"stem":162,"icon":163},"Block capabilities, abilities and actions","\u002Fguides\u002Fblock-capabilities","3.guides\u002F23.block-capabilities","i-lucide-shield-check",{"title":165,"path":166,"stem":167,"icon":168},"Block types in code and JSON","\u002Fguides\u002Fblock-types","3.guides\u002F24.block-types","i-lucide-shapes",{"title":170,"path":171,"stem":172,"icon":173},"Derive methods and the Derivation index","\u002Fguides\u002Fderive-methods","3.guides\u002F25.derive-methods","i-lucide-git-branch-plus",{"title":175,"path":176,"stem":177,"icon":178},"Block properties and Property definitions","\u002Fguides\u002Fblock-properties","3.guides\u002F26.block-properties","i-lucide-sliders-horizontal",{"title":180,"path":181,"stem":182,"icon":183},"Image studio","\u002Fguides\u002Fimage-studio","3.guides\u002F27.image-studio","i-lucide-image-plus",{"title":185,"path":186,"stem":187,"icon":188},"Prüfung and Freigabestatus","\u002Fguides\u002Fpruefung","3.guides\u002F28.pruefung","i-lucide-stamp",{"title":190,"path":191,"stem":192,"icon":193},"Workspace: find, read, reference and embed Blocks across Documents","\u002Fguides\u002Fworkspace","3.guides\u002F29.workspace","i-lucide-network",{"title":195,"path":196,"stem":197,"icon":198},"EditorPorts: auth and storage","\u002Fguides\u002Feditor-ports","3.guides\u002F3.editor-ports","i-lucide-plug",{"title":200,"path":201,"stem":202,"icon":203},"Collections: an ordered list of Blocks","\u002Fguides\u002Fcollections","3.guides\u002F30.collections","i-lucide-list-checks",{"title":205,"path":206,"stem":207,"icon":208},"Data views: a query shown as table, list or board","\u002Fguides\u002Fdata-views","3.guides\u002F31.data-views","i-lucide-table-properties",{"title":210,"path":211,"stem":212,"icon":193},"Explore: backlinks, related Blocks and the trail","\u002Fguides\u002Fexplore","3.guides\u002F32.explore",{"title":214,"path":215,"stem":216,"icon":217},"Render: export a Block or a Document to a file","\u002Fguides\u002Frender","3.guides\u002F33.render","i-lucide-file-output",{"title":219,"path":220,"stem":221,"icon":222},"Importers: what a dropped file becomes","\u002Fguides\u002Fimporters","3.guides\u002F34.importers","i-lucide-file-input",{"title":224,"path":225,"stem":226,"icon":227},"Templates: start a Document from a Dokumentvorlage","\u002Fguides\u002Ftemplates","3.guides\u002F35.templates","i-lucide-file-stack",{"title":229,"path":230,"stem":231,"icon":232},"Citations","\u002Fguides\u002Fcitations","3.guides\u002F36.citations","i-lucide-quote",{"title":234,"path":235,"stem":236,"icon":163},"AI proposals and the export check","\u002Fguides\u002Fexport-check","3.guides\u002F37.export-check",{"title":238,"path":239,"stem":240,"icon":241},"Query: filter, sort, group and total Blocks","\u002Fguides\u002Fquery","3.guides\u002F38.query","i-lucide-list-filter",{"title":243,"path":244,"stem":245,"icon":246},"Edges: backlinks and authored connections","\u002Fguides\u002Fedges","3.guides\u002F39.edges","i-lucide-link-2",{"title":248,"path":249,"stem":250,"icon":163},"Server routes and authentication","\u002Fguides\u002Froutes-and-auth","3.guides\u002F4.routes-and-auth",{"title":252,"path":253,"stem":254,"icon":255},"Speech: text to audio, and subtitle files","\u002Fguides\u002Fspeech","3.guides\u002F41.speech","i-lucide-audio-lines",{"title":257,"path":258,"stem":259,"icon":260},"Graph: the Edges around a Block","\u002Fguides\u002Fgraph","3.guides\u002F42.graph","i-lucide-waypoints",{"title":262,"path":263,"stem":264,"icon":265},"PDF viewer: passages, anchors and quotes","\u002Fguides\u002Fpdf-viewer","3.guides\u002F43.pdf-viewer","i-lucide-file-search",{"title":267,"path":268,"stem":269,"icon":270},"Obsidian: import and export a vault","\u002Fguides\u002Fobsidian","3.guides\u002F44.obsidian","i-lucide-folder-input",{"title":272,"path":273,"stem":274,"icon":275},"Typst: render a Document as a PDF with citations","\u002Fguides\u002Ftypst","3.guides\u002F45.typst","i-lucide-file-text",{"title":277,"path":278,"stem":279,"icon":280},"Audio: read aloud, mixdown and ElevenLabs","\u002Fguides\u002Faudio","3.guides\u002F46.audio","i-lucide-audio-waveform",{"title":282,"path":283,"stem":284,"icon":285},"Slides: decks, slide layouts and exports","\u002Fguides\u002Fslides","3.guides\u002F47.slides","i-lucide-presentation",{"title":287,"path":288,"stem":289,"icon":290},"Knowledge graph: sources and concepts beside the Documents","\u002Fguides\u002Fknowledge-graph","3.guides\u002F49.knowledge-graph","i-lucide-share-2",{"title":292,"path":293,"stem":294,"icon":78},"AI configuration and secrets","\u002Fguides\u002Fai-configuration","3.guides\u002F5.ai-configuration",{"title":296,"path":297,"stem":298,"children":299,"icon":301},"Index on Postgres: typos, meaning and SQL pushdown","\u002Fguides\u002Findex-pg","3.guides\u002F50.index-pg",[300],{"title":296,"path":297,"stem":298,"icon":301},"i-lucide-database-zap",{"title":303,"path":304,"stem":305,"icon":306},"Charts: bar, line, pie and scatter over a query","\u002Fguides\u002Fcharts","3.guides\u002F51.charts","i-lucide-chart-column",{"title":308,"path":309,"stem":310,"icon":73},"Collaboration on the client","\u002Fguides\u002Fcollaboration","3.guides\u002F6.collaboration",{"title":312,"path":313,"stem":314,"icon":315},"Block UI (drag handle, hover actions)","\u002Fguides\u002Fblock-ui","3.guides\u002F7.block-ui","i-lucide-grip-vertical",{"title":317,"path":318,"stem":319,"icon":320},"Settings and app lifetime","\u002Fguides\u002Fsettings","3.guides\u002F8.settings","i-lucide-settings",{"title":322,"path":323,"stem":324,"icon":325},"Language","\u002Fguides\u002Flanguage","3.guides\u002F9.language","i-lucide-languages",{"title":327,"icon":14,"path":328,"stem":329,"children":330,"page":37},"Reference","\u002Freference","4.reference",[331,335,339,344,348,352,356,360,364,368,373,378,382],{"title":332,"path":333,"stem":334,"icon":143},"Public exports","\u002Freference\u002Fexports","4.reference\u002F1.exports",{"title":336,"path":337,"stem":338,"icon":173},"Derive method contract","\u002Freference\u002Fderive-methods","4.reference\u002F10.derive-methods",{"title":340,"path":341,"stem":342,"icon":343},"Changelog","\u002Freference\u002Fchangelog","4.reference\u002F11.changelog","i-lucide-history",{"title":345,"path":346,"stem":347,"icon":217},"Renderer port","\u002Freference\u002Frenderer-port","4.reference\u002F12.renderer-port",{"title":349,"path":350,"stem":351,"icon":255},"Speech port","\u002Freference\u002Fspeech-port","4.reference\u002F13.speech-port",{"title":353,"path":354,"stem":355,"icon":58},"Presets and features","\u002Freference\u002Fpresets-and-features","4.reference\u002F2.presets-and-features",{"title":357,"path":358,"stem":359,"icon":68},"Feature contract","\u002Freference\u002Ffeature-contract","4.reference\u002F3.feature-contract",{"title":361,"path":362,"stem":363,"icon":107},"CSS and UI-provider requirements","\u002Freference\u002Fcss-and-ui","4.reference\u002F4.css-and-ui",{"title":365,"path":366,"stem":367,"icon":148},"Vue-free advanced schema","\u002Freference\u002Fadvanced-schema","4.reference\u002F5.advanced-schema",{"title":369,"path":370,"stem":371,"icon":372},"Dependency rules","\u002Freference\u002Fdependency-rules","4.reference\u002F6.dependency-rules","i-lucide-git-branch",{"title":374,"path":375,"stem":376,"icon":377},"Commands","\u002Freference\u002Fcommands","4.reference\u002F7.commands","i-lucide-terminal",{"title":379,"path":380,"stem":381,"icon":163},"Block capability contract","\u002Freference\u002Fblock-capabilities","4.reference\u002F8.block-capabilities",{"title":383,"path":384,"stem":385,"icon":168},"Block type contract","\u002Freference\u002Fblock-types","4.reference\u002F9.block-types",{"title":387,"path":388,"stem":389,"children":390,"icon":393},"AI agents","\u002Fai-agents","5.ai-agents\u002F1.index",[391,394,398,402,407],{"title":392,"path":388,"stem":389,"icon":393},"Overview","i-lucide-bot",{"title":395,"path":396,"stem":397,"icon":198},"Integrate the editor","\u002Fai-agents\u002Fintegrate","5.ai-agents\u002F2.integrate",{"title":399,"path":400,"stem":401,"icon":68},"Add a block or feature","\u002Fai-agents\u002Fextend","5.ai-agents\u002F3.extend",{"title":403,"path":404,"stem":405,"icon":406},"Pitfalls and invariants","\u002Fai-agents\u002Finvariants","5.ai-agents\u002F4.invariants","i-lucide-shield-alert",{"title":408,"path":409,"stem":410,"icon":411},"Verify","\u002Fai-agents\u002Fverify","5.ai-agents\u002F5.verify","i-lucide-check-check",{"id":413,"title":296,"body":414,"description":1211,"extension":1212,"links":1213,"meta":1214,"navigation":1215,"path":297,"seo":1216,"stem":298,"__hash__":1217},"docs\u002F3.guides\u002F50.index-pg.md",{"type":415,"value":416,"toc":1201},"minimark",[417,443,562,569,587,708,730,733,758,772,776,990,1058,1065,1078,1096,1099,1158,1162,1197],[418,419,420,421,425,426,430,431,434,435,438,439,442],"p",{},"The library's drizzle ",[422,423,424],"a",{"href":196},"Block index"," already runs ",[427,428,429],"code",{},"search"," in Postgres on ",[427,432,433],{},"tsvector","s. ",[427,436,437],{},"@skaile\u002Fforge-editor-index-pg"," wraps it (",[427,440,441],{},"createPgBlockIndex",") and adds three things a plain drizzle index lacks. Nothing changes for a host that does not use it.",[444,445,446,462],"table",{},[447,448,449],"thead",{},[450,451,452,456,459],"tr",{},[453,454,455],"th",{},"Piece",[453,457,458],{},"What it does",[453,460,461],{},"Where",[463,464,465,488,518,548],"tbody",{},[450,466,467,471,485],{},[468,469,470],"td",{},"Typo search",[468,472,473,475,476,480,481,484],{},[427,474,429],{}," finds a title that holds a word ",[477,478,479],"em",{},"similar"," to each query word (",[427,482,483],{},"pg_trgm","), after the hits for the words as written",[468,486,487],{},"add-on",[450,489,490,493,511],{},[468,491,492],{},"Vectors",[468,494,495,498,499,502,503,506,507,510],{},[427,496,497],{},"replace"," embeds the Blocks whose text changed into ",[427,500,501],{},"editor_block_vectors"," (pgvector); ",[427,504,505],{},"nearest"," and ",[427,508,509],{},"related"," answer by cosine similarity",[468,512,513,514,517],{},"add-on + ",[427,515,516],{},"Embedder"," port",[450,519,520,523,546],{},[468,521,522],{},"Query pushdown",[468,524,525,528,529,532,533,532,536,532,539,506,542,545],{},[427,526,527],{},"query"," filters ",[427,530,531],{},"eq",", ",[427,534,535],{},"ne",[427,537,538],{},"isEmpty",[427,540,541],{},"isNotEmpty",[427,543,544],{},"hasAny"," on Properties in SQL",[468,547,487],{},[450,549,550,553,559],{},[468,551,552],{},"Search by meaning",[468,554,555,556,558],{},"the search route fuses the keyword hits with ",[427,557,505],{}," (reciprocal rank fusion)",[468,560,561],{},"library",[563,564,566,567,517],"h2",{"id":565},"the-embedder-port","The ",[427,568,516],{},[418,570,571,574,575,578,579,582,583,586],{},[427,572,573],{},"EditorPorts.embeddings"," (",[427,576,577],{},"@skaile\u002Fforge-editor\u002Fserver",") turns text into vectors. Capability id ",[427,580,581],{},"embeddings","; the server lists it in ",[427,584,585],{},"GET \u002Fapi\u002Feditor\u002Fcapabilities",", a backend never has it (the client never embeds, the route does).",[588,589,594],"pre",{"className":590,"code":591,"language":592,"meta":593,"style":593},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","interface Embedder {\n  \u002F** Names the model; a vector is comparable only with vectors of the same model. *\u002F\n  model: string\n  \u002F** The length of its vectors. *\u002F\n  dim: number\n  embed(texts: string[], user?: object): Promise\u003Cnumber[][]>\n}\n","ts","",[427,595,596,613,620,633,639,650,702],{"__ignoreMap":593},[597,598,601,605,609],"span",{"class":599,"line":600},"line",1,[597,602,604],{"class":603},"spNyl","interface",[597,606,608],{"class":607},"sBMFI"," Embedder",[597,610,612],{"class":611},"sMK4o"," {\n",[597,614,616],{"class":599,"line":615},2,[597,617,619],{"class":618},"sHwdD","  \u002F** Names the model; a vector is comparable only with vectors of the same model. *\u002F\n",[597,621,623,627,630],{"class":599,"line":622},3,[597,624,626],{"class":625},"swJcz","  model",[597,628,629],{"class":611},":",[597,631,632],{"class":607}," string\n",[597,634,636],{"class":599,"line":635},4,[597,637,638],{"class":618},"  \u002F** The length of its vectors. *\u002F\n",[597,640,642,645,647],{"class":599,"line":641},5,[597,643,644],{"class":625},"  dim",[597,646,629],{"class":611},[597,648,649],{"class":607}," number\n",[597,651,653,656,659,663,665,668,672,675,678,681,684,687,690,693,696,699],{"class":599,"line":652},6,[597,654,655],{"class":625},"  embed",[597,657,658],{"class":611},"(",[597,660,662],{"class":661},"sHdIc","texts",[597,664,629],{"class":611},[597,666,667],{"class":607}," string",[597,669,671],{"class":670},"sTEyZ","[]",[597,673,674],{"class":611},",",[597,676,677],{"class":661}," user",[597,679,680],{"class":611},"?:",[597,682,683],{"class":607}," object",[597,685,686],{"class":611},"):",[597,688,689],{"class":607}," Promise",[597,691,692],{"class":611},"\u003C",[597,694,695],{"class":607},"number",[597,697,698],{"class":670},"[][]",[597,700,701],{"class":611},">\n",[597,703,705],{"class":599,"line":704},7,[597,706,707],{"class":611},"}\n",[418,709,710,713,714,717,718,721,722,725,726,729],{},[427,711,712],{},"createOpenAiEmbedder({ endpoint, model, dim, apiKey })"," from ",[427,715,716],{},"@skaile\u002Fforge-editor\u002Fadapters\u002Fembeddings"," calls an OpenAI-compatible ",[427,719,720],{},"POST \u003Cendpoint>\u002Fembeddings",". The call goes from the server, the key never reaches a page. ",[427,723,724],{},"OPENAI_EMBEDDING_DIMENSIONS"," lists the vector lengths of OpenAI's models; a model it does not list needs its ",[427,727,728],{},"dim"," given.",[563,731,552],{"id":732},"search-by-meaning",[418,734,735,736,739,740,532,742,745,746,749,750,753,754,757],{},"When the host passes ",[427,737,738],{},"ports.embeddings"," and the index has ",[427,741,505],{},[427,743,744],{},"GET \u002Fapi\u002Feditor\u002Fsearch?q="," runs the keyword search, embeds the query, asks the index for the nearest Blocks and fuses both lists with ",[427,747,748],{},"fuseRanks"," (reciprocal rank fusion: a hit at rank ",[427,751,752],{},"r"," adds ",[427,755,756],{},"weight \u002F (60 + r)","; keyword 0.7, vector 0.3). A note titled \"Hund\" then answers the query \"Haustier\", which shares no word with it. When the embedder fails the route logs it and answers the keyword hits: search never stops working with the endpoint.",[418,759,760,763,764,767,768,771],{},[427,761,762],{},"GET \u002Fapi\u002Feditor\u002Fblocks\u002F:documentId\u002F:blockId\u002Frelated"," (the ",[422,765,766],{"href":211},"Explore"," tab) prefers ",[427,769,770],{},"index.related"," when the index has it, the Blocks nearest to the Block's own vector, and answers the shared-tags heuristic otherwise, or when the Block has no vector yet.",[563,773,775],{"id":774},"set-up","Set up",[588,777,779],{"className":590,"code":778,"language":592,"meta":593,"style":593},"import { createOpenAiEmbedder } from '@skaile\u002Fforge-editor\u002Fadapters\u002Fembeddings'\nimport { bootstrapPgBlockIndex, createPgBlockIndex, pgBlockIndexTables } from '@skaile\u002Fforge-editor-index-pg'\n\nconst embedder = createOpenAiEmbedder({ endpoint: 'https:\u002F\u002Fapi.openai.com\u002Fv1', model: 'text-embedding-3-small', dim: 1536, apiKey })\nawait bootstrapPgBlockIndex(db, { dim: embedder.dim })\nconst index = createPgBlockIndex({ db, tables: pgBlockIndexTables(embedder.dim), embedder })\n\u002F\u002F installEditorServer({ ports: { index, embeddings: embedder, ... } })\n",[427,780,781,808,837,843,911,942,985],{"__ignoreMap":593},[597,782,783,787,790,793,796,799,802,805],{"class":599,"line":600},[597,784,786],{"class":785},"s7zQu","import",[597,788,789],{"class":611}," {",[597,791,792],{"class":670}," createOpenAiEmbedder",[597,794,795],{"class":611}," }",[597,797,798],{"class":785}," from",[597,800,801],{"class":611}," '",[597,803,716],{"class":804},"sfazB",[597,806,807],{"class":611},"'\n",[597,809,810,812,814,817,819,822,824,827,829,831,833,835],{"class":599,"line":615},[597,811,786],{"class":785},[597,813,789],{"class":611},[597,815,816],{"class":670}," bootstrapPgBlockIndex",[597,818,674],{"class":611},[597,820,821],{"class":670}," createPgBlockIndex",[597,823,674],{"class":611},[597,825,826],{"class":670}," pgBlockIndexTables",[597,828,795],{"class":611},[597,830,798],{"class":785},[597,832,801],{"class":611},[597,834,437],{"class":804},[597,836,807],{"class":611},[597,838,839],{"class":599,"line":622},[597,840,842],{"emptyLinePlaceholder":841},true,"\n",[597,844,845,848,851,854,857,859,862,865,867,869,872,875,877,880,882,884,887,889,891,894,896,900,902,905,908],{"class":599,"line":635},[597,846,847],{"class":603},"const",[597,849,850],{"class":670}," embedder ",[597,852,853],{"class":611},"=",[597,855,792],{"class":856},"s2Zo4",[597,858,658],{"class":670},[597,860,861],{"class":611},"{",[597,863,864],{"class":625}," endpoint",[597,866,629],{"class":611},[597,868,801],{"class":611},[597,870,871],{"class":804},"https:\u002F\u002Fapi.openai.com\u002Fv1",[597,873,874],{"class":611},"'",[597,876,674],{"class":611},[597,878,879],{"class":625}," model",[597,881,629],{"class":611},[597,883,801],{"class":611},[597,885,886],{"class":804},"text-embedding-3-small",[597,888,874],{"class":611},[597,890,674],{"class":611},[597,892,893],{"class":625}," dim",[597,895,629],{"class":611},[597,897,899],{"class":898},"sbssI"," 1536",[597,901,674],{"class":611},[597,903,904],{"class":670}," apiKey ",[597,906,907],{"class":611},"}",[597,909,910],{"class":670},")\n",[597,912,913,916,918,921,923,925,927,929,932,935,938,940],{"class":599,"line":641},[597,914,915],{"class":785},"await",[597,917,816],{"class":856},[597,919,920],{"class":670},"(db",[597,922,674],{"class":611},[597,924,789],{"class":611},[597,926,893],{"class":625},[597,928,629],{"class":611},[597,930,931],{"class":670}," embedder",[597,933,934],{"class":611},".",[597,936,937],{"class":670},"dim ",[597,939,907],{"class":611},[597,941,910],{"class":670},[597,943,944,946,949,951,953,955,957,960,962,965,967,969,972,974,977,979,981,983],{"class":599,"line":652},[597,945,847],{"class":603},[597,947,948],{"class":670}," index ",[597,950,853],{"class":611},[597,952,821],{"class":856},[597,954,658],{"class":670},[597,956,861],{"class":611},[597,958,959],{"class":670}," db",[597,961,674],{"class":611},[597,963,964],{"class":625}," tables",[597,966,629],{"class":611},[597,968,826],{"class":856},[597,970,971],{"class":670},"(embedder",[597,973,934],{"class":611},[597,975,976],{"class":670},"dim)",[597,978,674],{"class":611},[597,980,850],{"class":670},[597,982,907],{"class":611},[597,984,910],{"class":670},[597,986,987],{"class":599,"line":704},[597,988,989],{"class":618},"\u002F\u002F installEditorServer({ ports: { index, embeddings: embedder, ... } })\n",[991,992,993,1020,1035,1044],"ul",{},[994,995,996,999,1000,506,1002,1005,1006,1009,1010,713,1012,506,1015,713,1017,934],"li",{},[427,997,998],{},"db"," is any drizzle Postgres database whose server has ",[427,1001,483],{},[427,1003,1004],{},"vector",". On PGlite load them: ",[427,1007,1008],{},"new PGlite({ extensions: { pg_trgm, vector } })"," with ",[427,1011,483],{},[427,1013,1014],{},"@electric-sql\u002Fpglite\u002Fcontrib\u002Fpg_trgm",[427,1016,1004],{},[427,1018,1019],{},"@electric-sql\u002Fpglite-pgvector",[994,1021,1022,1023,1026,1027,1030,1031,1034],{},"A host with migrations exports ",[427,1024,1025],{},"pgBlockIndexTables(dim)"," from its drizzle schema and adds ",[427,1028,1029],{},"pgBlockIndexStatements(dim)"," (the extensions, the trigram index, the vector table) to a migration. ",[427,1032,1033],{},"bootstrapPgBlockIndex"," is the same without migrations.",[994,1036,1037,1038,1040,1041,1043],{},"The vector table has the length ",[427,1039,728],{}," of the embedder. Changing the model to another length needs a new table; changing it to one of the same length makes ",[427,1042,497],{}," embed everything again (the model is stored with each vector).",[994,1045,1046,1047,532,1050,1052,1053,1055,1056,934],{},"Without ",[427,1048,1049],{},"embedder",[427,1051,441],{}," still gives typo search and pushdown, and has no ",[427,1054,505],{}," or ",[427,1057,509],{},[563,1059,1061,1062,1064],{"id":1060},"what-replace-does-with-vectors","What ",[427,1063,497],{}," does with vectors",[418,1066,1067,1068,1070,1071,1074,1075,1077],{},"Embedding is a network call, so ",[427,1069,497],{}," returns before it is done and ",[427,1072,1073],{},"index.settled()"," waits for it. Per Document one job runs at a time, always over the latest rows; a second ",[427,1076,497],{}," meanwhile replaces the rows the job is yet to see.",[991,1079,1080,1083,1089],{},[994,1081,1082],{},"A Block whose title and text (cut at 6000 characters) hash like its stored vector, with the same model, costs nothing.",[994,1084,1085,1086,1088],{},"A changed Block is embedded in batches of 64, and each batch is stored when it returns, so a failing endpoint keeps what was made. The failure is logged; the rows stay; the next ",[427,1087,497],{}," of the Document, or the rebuild of the index at server start, makes the vectors that are missing.",[994,1090,1091,1092,1095],{},"A Block that vanished or has neither title nor text loses its vector; ",[427,1093,1094],{},"remove(documentId)"," drops the Document's vectors.",[563,1097,522],{"id":1098},"query-pushdown",[418,1100,1101,1103,1104,1107,1108,532,1110,532,1112,532,1114,506,1116,1118,1119,506,1121,1123,1124,1126,1127,532,1130,532,1133,532,1136,532,1139,532,1142,1145,1146,1149,1150,1153,1154,1157],{},[427,1102,527],{}," keeps the contract of the memory adapter: the same rows in the same order. The add-on narrows the candidate rows in SQL, as a pre-filter, and ",[427,1105,1106],{},"evaluateQuery"," runs over the rows that are left for the final answer (the filter again, sorting, grouping, aggregates, paging, issues), so SQL can only drop rows the filter would drop anyway. A condition is pushed when its SQL is the very test of the Property kind: ",[427,1109,531],{},[427,1111,535],{},[427,1113,538],{},[427,1115,541],{},[427,1117,544],{}," on text, textarea, url, select, status and reference Properties, ",[427,1120,531],{},[427,1122,535],{}," on checkboxes, ",[427,1125,544],{}," on multiselects, and the same on the pseudo-fields (",[427,1128,1129],{},"$title",[427,1131,1132],{},"$tags",[427,1134,1135],{},"$type",[427,1137,1138],{},"$id",[427,1140,1141],{},"$doc",[427,1143,1144],{},"$basket","). An ",[427,1147,1148],{},"and"," pushes the conditions it can and leaves the others to JS; an ",[427,1151,1152],{},"or"," is pushed only when every branch is. Dates, numbers, ",[427,1155,1156],{},"contains"," and the ordering operators, a query without a Block type, and a query that names a key no definition declares go through the drizzle adapter's JS path unchanged. A row whose JSON Postgres cannot read (U+0000) falls back the same way. The contract test runs every case against the memory adapter and the pg adapter on PGlite.",[563,1159,1161],{"id":1160},"in-the-host-app","In the host app",[418,1163,1164,1167,1168,1171,1172,1175,1176,1179,1180,1183,1184,1186,1187,1189,1190,1193,1194,1196],{},[427,1165,1166],{},"NUXT_BLOCK_INDEX=pg-vector"," starts the index on PGlite with the add-on (",[427,1169,1170],{},"server\u002Futils\u002Fblock-index.ts","). With ",[427,1173,1174],{},"OPENAI_API_KEY"," set the host also creates the embedder (",[427,1177,1178],{},"server\u002Futils\u002Faddons.ts",": ",[427,1181,1182],{},"OPENAI_EMBEDDING_MODEL",", default ",[427,1185,886],{},"; ",[427,1188,724],{}," for a model OpenAI does not list; ",[427,1191,1192],{},"OPENAI_BASE_URL"," for another OpenAI-compatible endpoint) and passes it to the index and to ",[427,1195,738],{},". Without the key the index has typo search only. PGlite lives in memory: the server rebuilds the index, and with it the vectors, from the Documents on every start, which costs one embedding call per 64 changed Blocks; a host with many Documents uses a Postgres server instead.",[1198,1199,1200],"style",{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":593,"searchDepth":615,"depth":615,"links":1202},[1203,1205,1206,1207,1209,1210],{"id":565,"depth":615,"text":1204},"The Embedder port",{"id":732,"depth":615,"text":552},{"id":774,"depth":615,"text":775},{"id":1060,"depth":615,"text":1208},"What replace does with vectors",{"id":1098,"depth":615,"text":522},{"id":1160,"depth":615,"text":1161},"The @skaile\u002Fforge-editor-index-pg add-on: trigram title search, a vector table kept current on replace, nearest and related Blocks, query pushdown, and the Embedder port with its OpenAI-compatible adapter.","md",null,{},{"icon":301},{"title":296,"description":1211},"qCN86nqkG06BnkUwnGdKTt_ybmmnmU635k-Ghz2Wv6A",[1219,1221],{"title":292,"path":293,"stem":294,"description":1220,"icon":78,"children":-1},"One configuration object for module options, server and browser, the Model presets and Model roles, and why API keys come from the environment only.",{"title":303,"path":304,"stem":305,"description":1222,"icon":306,"children":-1},"The add-on @skaile\u002Fforge-editor-charts: the Block type chart, its configuration and the query it derives, the pure Vega-Lite specification, the node view with a chart editor for Vue and React, and the render target chart-svg.",1793213910679]