[{"data":1,"prerenderedAt":635},["ShallowReactive",2],{"$fAL98rdR3gbs6rkT-tKp-p57P7II5U91cEpY6kaRJJvY":3},{"newThing":4,"project":5,"cycle":8,"sections":21,"threads":612,"otherDocs":613,"documents":615,"images":617,"archiveItems":633,"links":634},null,{"id":6,"label":7,"liveBase":4},"qpixel","Qpixel",{"id":9,"num":10,"dir":11,"name":12,"title":13,"focus":4,"status":14,"statusNote":4,"created":15,"completed":4,"mode":4,"report":4,"recordless":16,"thumbnail":17,"imageCount":18,"artifactCount":19,"lastActivity":15,"hasPickup":16,"hasInitialPrompt":20},"c4",4,"c4.svg-export","svg-export","SVG Export","active","2026-08-15",false,"\u002Fcycles\u002Fqpixel\u002Fc4-checkerboard.png",7,1,true,[22,45,55,116,165,182,531,583],{"key":23,"title":24,"empty":16,"ast":25},"why","Why",{"type":26,"children":27},"root",[28],{"type":29,"tag":30,"props":31,"children":32},"element","p",{},[33,36,43],{"type":34,"value":35},"text","Qpixel can currently export ANSI ",{"type":29,"tag":37,"props":38,"children":40},"code",{"className":39},[],[41],{"type":34,"value":42},".txt",{"type":34,"value":44},", but there is no vector export path for web, docs, or design workflows. We also now support more than block pixels: the canvas can contain block elements, line-drawing glyphs, arrows, and free text, so SVG export needs to be defined against the real editor model rather than the original block-art-only assumption.",{"key":46,"title":47,"empty":16,"ast":48},"objective","Objective",{"type":26,"children":49},[50],{"type":29,"tag":30,"props":51,"children":52},{},[53],{"type":34,"value":54},"Ship a reliable SVG export flow that preserves crisp pixel-aligned output by default, keeps ANSI export unchanged, and creates a clear path toward smarter shape reduction without blocking the first usable release.",{"key":56,"title":57,"empty":16,"ast":58},"scope","Scope",{"type":26,"children":59},[60],{"type":29,"tag":61,"props":62,"children":63},"ul",{},[64,70,83,94,99,111],{"type":29,"tag":65,"props":66,"children":67},"li",{},[68],{"type":34,"value":69},"Add SVG export as a first-class export option in the editor UI",{"type":29,"tag":65,"props":71,"children":72},{},[73,75,81],{"type":34,"value":74},"Ship ",{"type":29,"tag":76,"props":77,"children":78},"strong",{},[79],{"type":34,"value":80},"block export",{"type":34,"value":82}," first for block-based characters",{"type":29,"tag":65,"props":84,"children":85},{},[86,88],{"type":34,"value":87},"Define fallback behavior for non-block glyphs introduced in ",{"type":29,"tag":37,"props":89,"children":91},{"className":90},[],[92],{"type":34,"value":93},"c2",{"type":29,"tag":65,"props":95,"children":96},{},[97],{"type":34,"value":98},"Add a repeatable verification workflow for crisp, non-antialiased output",{"type":29,"tag":65,"props":100,"children":101},{},[102,104,109],{"type":34,"value":103},"Timebox ",{"type":29,"tag":76,"props":105,"children":106},{},[107],{"type":34,"value":108},"shape export",{"type":34,"value":110}," as a research\u002Fprototype track rather than a release blocker",{"type":29,"tag":65,"props":112,"children":113},{},[114],{"type":34,"value":115},"Defer PNG export unless it falls out naturally from the SVG pipeline",{"key":117,"title":118,"empty":16,"ast":119},"key-changes","Deliverables",{"type":26,"children":120},[121],{"type":29,"tag":61,"props":122,"children":123},{},[124,129,140,145,150,155,160],{"type":29,"tag":65,"props":125,"children":126},{},[127],{"type":34,"value":128},"Toolbar\u002FUI support for SVG export",{"type":29,"tag":65,"props":130,"children":131},{},[132,138],{"type":29,"tag":37,"props":133,"children":135},{"className":134},[],[136],{"type":34,"value":137},"app\u002Futils\u002Fsvg.ts",{"type":34,"value":139}," or equivalent export module",{"type":29,"tag":65,"props":141,"children":142},{},[143],{"type":34,"value":144},"Character classification for block-geometry vs text\u002Fglyph fallback",{"type":29,"tag":65,"props":146,"children":147},{},[148],{"type":34,"value":149},"Block SVG export with default crisp sizing",{"type":29,"tag":65,"props":151,"children":152},{},[153],{"type":34,"value":154},"Rectangle-merging optimization for obvious same-color runs",{"type":29,"tag":65,"props":156,"children":157},{},[158],{"type":34,"value":159},"Fixture sheet and repeatable QA flow",{"type":29,"tag":65,"props":161,"children":162},{},[163],{"type":34,"value":164},"Short write-up on full shape export feasibility and recommended next step",{"key":166,"title":167,"empty":16,"ast":168},"outcome","Outcome Target",{"type":26,"children":169},[170],{"type":29,"tag":30,"props":171,"children":172},{},[173,175,180],{"type":34,"value":174},"By the end of ",{"type":29,"tag":37,"props":176,"children":178},{"className":177},[],[179],{"type":34,"value":9},{"type":34,"value":181},", Qpixel should have a production-usable SVG export path with crisp block rendering, safe handling of mixed-content canvases, and enough research completed to choose the right shape-merging strategy without guessing.",{"key":183,"title":184,"empty":16,"ast":185},"extra","Execution Plan",{"type":26,"children":186},[187,194,286,292,315,321,360,366,394,399,405,430,435,441,502,507,513],{"type":29,"tag":188,"props":189,"children":191},"h3",{"id":190},"_1-define-the-export-contract",[192],{"type":34,"value":193},"1. Define the export contract",{"type":29,"tag":61,"props":195,"children":196},{},[197,202,207,212,246,281],{"type":29,"tag":65,"props":198,"children":199},{},[200],{"type":34,"value":201},"Keep the current ANSI copy\u002Fexport flow exactly as-is",{"type":29,"tag":65,"props":203,"children":204},{},[205],{"type":34,"value":206},"Introduce a dedicated SVG export utility, separate from ANSI serialization",{"type":29,"tag":65,"props":208,"children":209},{},[210],{"type":34,"value":211},"Standardize export sizing around SVG\u002FCSS pixels, not physical DPI",{"type":29,"tag":65,"props":213,"children":214},{},[215,217,223,225,231,233,239,240],{"type":34,"value":216},"Default to ",{"type":29,"tag":37,"props":218,"children":220},{"className":219},[],[221],{"type":34,"value":222},"1",{"type":34,"value":224}," SVG unit per editor cell and expose scale presets like ",{"type":29,"tag":37,"props":226,"children":228},{"className":227},[],[229],{"type":34,"value":230},"1x",{"type":34,"value":232},", ",{"type":29,"tag":37,"props":234,"children":236},{"className":235},[],[237],{"type":34,"value":238},"2x",{"type":34,"value":232},{"type":29,"tag":37,"props":241,"children":243},{"className":242},[],[244],{"type":34,"value":245},"4x",{"type":29,"tag":65,"props":247,"children":248},{},[249,251,257,258,264,265,271,273,279],{"type":34,"value":250},"Export with explicit ",{"type":29,"tag":37,"props":252,"children":254},{"className":253},[],[255],{"type":34,"value":256},"viewBox",{"type":34,"value":232},{"type":29,"tag":37,"props":259,"children":261},{"className":260},[],[262],{"type":34,"value":263},"width",{"type":34,"value":232},{"type":29,"tag":37,"props":266,"children":268},{"className":267},[],[269],{"type":34,"value":270},"height",{"type":34,"value":272},", and ",{"type":29,"tag":37,"props":274,"children":276},{"className":275},[],[277],{"type":34,"value":278},"shape-rendering=\"crispEdges\"",{"type":34,"value":280}," where applicable",{"type":29,"tag":65,"props":282,"children":283},{},[284],{"type":34,"value":285},"Decide background behavior up front: transparent by default, optional solid background later if needed",{"type":29,"tag":188,"props":287,"children":289},{"id":288},"_2-split-canvas-content-into-exportable-categories",[290],{"type":34,"value":291},"2. Split canvas content into exportable categories",{"type":29,"tag":61,"props":293,"children":294},{},[295,300,305,310],{"type":29,"tag":65,"props":296,"children":297},{},[298],{"type":34,"value":299},"Treat block-style characters as geometry-driven cells that can become vector shapes",{"type":29,"tag":65,"props":301,"children":302},{},[303],{"type":34,"value":304},"Treat non-block glyphs as glyph\u002Ftext output, not rectangles",{"type":29,"tag":65,"props":306,"children":307},{},[308],{"type":34,"value":309},"Build a small classifier layer so export logic can branch by character family instead of hardcoding per feature",{"type":29,"tag":65,"props":311,"children":312},{},[313],{"type":34,"value":314},"Fail safely: if a character cannot be converted to geometry, export it as positioned text rather than dropping it",{"type":29,"tag":188,"props":316,"children":318},{"id":317},"_3-ship-block-export-mvp",[319],{"type":34,"value":320},"3. Ship block export MVP",{"type":29,"tag":61,"props":322,"children":323},{},[324,335,340,345,350,355],{"type":29,"tag":65,"props":325,"children":326},{},[327,329],{"type":34,"value":328},"Add an export surface in the toolbar for ",{"type":29,"tag":37,"props":330,"children":332},{"className":331},[],[333],{"type":34,"value":334},".svg",{"type":29,"tag":65,"props":336,"children":337},{},[338],{"type":34,"value":339},"Serialize each block-capable cell into SVG primitives at cell-aligned coordinates",{"type":29,"tag":65,"props":341,"children":342},{},[343],{"type":34,"value":344},"Start with per-cell output for correctness and simplicity",{"type":29,"tag":65,"props":346,"children":347},{},[348],{"type":34,"value":349},"Support foreground color per cell",{"type":29,"tag":65,"props":351,"children":352},{},[353],{"type":34,"value":354},"Preserve empty cells as transparent space",{"type":29,"tag":65,"props":356,"children":357},{},[358],{"type":34,"value":359},"Verify that the generated SVG opens cleanly in browsers and design tools",{"type":29,"tag":188,"props":361,"children":363},{"id":362},"_4-add-a-smarter-middle-step-before-full-shape-export",[364],{"type":34,"value":365},"4. Add a smarter middle step before full shape export",{"type":29,"tag":61,"props":367,"children":368},{},[369,374,379,384],{"type":29,"tag":65,"props":370,"children":371},{},[372],{"type":34,"value":373},"Implement rectangle merging for adjacent same-color full-cell blocks as the first optimization pass",{"type":29,"tag":65,"props":375,"children":376},{},[377],{"type":34,"value":378},"Keep this optimization optional and lossless",{"type":29,"tag":65,"props":380,"children":381},{},[382],{"type":34,"value":383},"Use it to reduce SVG size without taking on full polygon union complexity",{"type":29,"tag":65,"props":385,"children":386},{},[387,389],{"type":34,"value":388},"Treat this as the likely default optimization for ",{"type":29,"tag":37,"props":390,"children":392},{"className":391},[],[393],{"type":34,"value":9},{"type":29,"tag":30,"props":395,"children":396},{},[397],{"type":34,"value":398},"This is the main additional strategy worth considering: we do not need to jump directly from \"one rect per cell\" to \"perfect merged polygons.\" A rectangle-merging pass gives most of the benefit with far less algorithmic risk.",{"type":29,"tag":188,"props":400,"children":402},{"id":401},"_5-timebox-shape-export-research",[403],{"type":34,"value":404},"5. Timebox shape export research",{"type":29,"tag":61,"props":406,"children":407},{},[408,413,418],{"type":29,"tag":65,"props":409,"children":410},{},[411],{"type":34,"value":412},"Research\u002Fcompare approaches for converting merged cell regions into simple orthogonal paths",{"type":29,"tag":65,"props":414,"children":415},{},[416],{"type":34,"value":417},"Evaluate at least these options: edge tracing around occupied cells by color, union of cell rectangles followed by contour extraction, and run-length\u002Fscanline merging as a preprocessing stage",{"type":29,"tag":65,"props":419,"children":420},{},[421,423,428],{"type":34,"value":422},"Produce a recommendation on whether full shape export belongs in ",{"type":29,"tag":37,"props":424,"children":426},{"className":425},[],[427],{"type":34,"value":9},{"type":34,"value":429}," or a follow-up cycle",{"type":29,"tag":30,"props":431,"children":432},{},[433],{"type":34,"value":434},"Success for this track is a recommendation and a small prototype, not necessarily a production-ready second export mode.",{"type":29,"tag":188,"props":436,"children":438},{"id":437},"_6-build-qa-and-verification-tooling",[439],{"type":34,"value":440},"6. Build QA and verification tooling",{"type":29,"tag":61,"props":442,"children":443},{},[444,478,497],{"type":29,"tag":65,"props":445,"children":446},{},[447,449,455,456,462,463,469,470,476],{"type":34,"value":448},"Create a small fixture sheet covering full blocks, partial blocks (",{"type":29,"tag":37,"props":450,"children":452},{"className":451},[],[453],{"type":34,"value":454},"▀",{"type":34,"value":232},{"type":29,"tag":37,"props":457,"children":459},{"className":458},[],[460],{"type":34,"value":461},"▄",{"type":34,"value":232},{"type":29,"tag":37,"props":464,"children":466},{"className":465},[],[467],{"type":34,"value":468},"▌",{"type":34,"value":232},{"type":29,"tag":37,"props":471,"children":473},{"className":472},[],[474],{"type":34,"value":475},"▐",{"type":34,"value":477},"), quadrants\u002Fdiagonals, mixed colors, and mixed content with non-block glyph fallback",{"type":29,"tag":65,"props":479,"children":480},{},[481,483,488,490,495],{"type":34,"value":482},"Add a local QA page or dev-only panel that renders the live editor preview, exported SVG inline, rasterized SVG at ",{"type":29,"tag":37,"props":484,"children":486},{"className":485},[],[487],{"type":34,"value":230},{"type":34,"value":489}," and ",{"type":29,"tag":37,"props":491,"children":493},{"className":492},[],[494],{"type":34,"value":238},{"type":34,"value":496},", and offset\u002Fzoom views for spotting blur or half-pixel alignment",{"type":29,"tag":65,"props":498,"children":499},{},[500],{"type":34,"value":501},"Add lightweight pixel checks where possible by rasterizing the SVG to canvas and asserting there are no unexpected semi-transparent edge pixels in crisp modes",{"type":29,"tag":30,"props":503,"children":504},{},[505],{"type":34,"value":506},"Do not block on AI-based visual introspection. First ship a deterministic QA sheet that makes failures obvious. If we still need an AI-assisted review step after that, it can be layered on top of saved screenshots rather than embedded in the core implementation.",{"type":29,"tag":188,"props":508,"children":510},{"id":509},"_7-handle-png-as-a-stretch-goal",[511],{"type":34,"value":512},"7. Handle PNG as a stretch goal",{"type":29,"tag":61,"props":514,"children":515},{},[516,521],{"type":29,"tag":65,"props":517,"children":518},{},[519],{"type":34,"value":520},"If block SVG export is stable, add PNG export by rendering the generated SVG to canvas and downloading a raster image",{"type":29,"tag":65,"props":522,"children":523},{},[524,526],{"type":34,"value":525},"Keep this out of the critical path for ",{"type":29,"tag":37,"props":527,"children":529},{"className":528},[],[530],{"type":34,"value":9},{"key":183,"title":532,"empty":16,"ast":533},"Success Criteria",{"type":26,"children":534},[535],{"type":29,"tag":61,"props":536,"children":537},{},[538,543,555,573,578],{"type":29,"tag":65,"props":539,"children":540},{},[541],{"type":34,"value":542},"ANSI export still behaves exactly as before",{"type":29,"tag":65,"props":544,"children":545},{},[546,548,553],{"type":34,"value":547},"A user can export ",{"type":29,"tag":37,"props":549,"children":551},{"className":550},[],[552],{"type":34,"value":334},{"type":34,"value":554}," from the editor without manual post-processing",{"type":29,"tag":65,"props":556,"children":557},{},[558,560,565,566,571],{"type":34,"value":559},"Default SVG output renders crisply at ",{"type":29,"tag":37,"props":561,"children":563},{"className":562},[],[564],{"type":34,"value":230},{"type":34,"value":489},{"type":29,"tag":37,"props":567,"children":569},{"className":568},[],[570],{"type":34,"value":238},{"type":34,"value":572}," on the QA sheet",{"type":29,"tag":65,"props":574,"children":575},{},[576],{"type":34,"value":577},"Non-block characters are preserved via an explicit fallback path",{"type":29,"tag":65,"props":579,"children":580},{},[581],{"type":34,"value":582},"We have a documented decision on whether full shape export ships now or moves to the next cycle",{"key":183,"title":584,"empty":16,"ast":585},"Open Questions",{"type":26,"children":586},[587],{"type":29,"tag":61,"props":588,"children":589},{},[590,595,607],{"type":29,"tag":65,"props":591,"children":592},{},[593],{"type":34,"value":594},"Should SVG text\u002Fglyph fallback use the same Menlo-style preview stack, or should it be intentionally limited to broadly available monospace fonts?",{"type":29,"tag":65,"props":596,"children":597},{},[598,600,605],{"type":34,"value":599},"Do we want an optional solid background color in the export, or is transparent-first enough for ",{"type":29,"tag":37,"props":601,"children":603},{"className":602},[],[604],{"type":34,"value":9},{"type":34,"value":606},"?",{"type":29,"tag":65,"props":608,"children":609},{},[610],{"type":34,"value":611},"Is \"shape export\" required for cycle completion, or is a validated recommendation sufficient once block export is solid?",[],[614],"initial-prompt.md",[616],{"file":614,"title":13},[618,621,623,625,627,629,631],{"src":17,"kind":619,"order":620},"hero",4999,{"src":622,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-empty-canvas.png",{"src":624,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-full-block-single-color.png",{"src":626,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-half-blocks.png",{"src":628,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-mixed-block-text.png",{"src":630,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-pixel-snap.png",{"src":632,"kind":619,"order":620},"\u002Fcycles\u002Fqpixel\u002Fc4-quadrant-blocks.png",[],[],1794599359008]