From 2988ef474fc2554d9201f22afa1e7e9f9221ede1 Mon Sep 17 00:00:00 2001 From: Mitchell Hansen Date: Fri, 8 May 2026 21:50:01 -0700 Subject: [PATCH] =?UTF-8?q?delete=20text=20source=20+=20contours=20tab;=20?= =?UTF-8?q?rename=20Detection=20=E2=86=92=20Pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per separated-pipelines decision: text source is gone entirely. The image pipeline (kernels → hulls → fills → gcode) stands alone. Removed: - src/text.rs (Hershey font parser, render_text, rasterize_blocks) - src-frontend/.../components/TextEditOverlay.jsx - App.jsx: sourceMode, textBlocks state, text-source toggle button, rasterise-on-edit useEffect, TextEditOverlay branch, "Clear all blocks" UI - lib.rs: pub mod text, struct TextBlock, AppState.text_blocks, TextBlockPayload, set_text_blocks, TextPreview, get_text_preview, the two tauri handler entries - useTauri.js: setTextBlocks - project.{js,test.js}: sourceMode + textBlocks fields in serialize/deserialize (existing project files keep loading; the fields just get ignored) Renamed: - VIEW_MODES: 'detection' + 'contours' → single 'pipeline' tab (the contours-SVG mode was the only thing in get_pass_viz, so the entire command + its frontend wrapper get_pass_viz/getPassViz also went). The pipeline tab still shows the NodeGraph; if the user wants contours back, it's a fill-strategy choice now (outline). - Accent colors map updated for the renamed tab; deleted 'paint' entry too. Verified: cargo build, cargo test --lib (74 pass), npm run build, npm test (72 pass — pre-existing hershey-fixture failure unchanged). --- src-frontend/src/App.jsx | 107 +----- .../src/components/TextEditOverlay.jsx | 319 ------------------ src-frontend/src/hooks/useTauri.js | 23 -- src-frontend/src/project.js | 6 +- src-frontend/src/project.test.js | 1 - src/lib.rs | 162 +-------- src/text.rs | 241 ------------- 7 files changed, 7 insertions(+), 852 deletions(-) delete mode 100644 src-frontend/src/components/TextEditOverlay.jsx delete mode 100644 src/text.rs diff --git a/src-frontend/src/App.jsx b/src-frontend/src/App.jsx index c8e89249..472f6aaa 100644 --- a/src-frontend/src/App.jsx +++ b/src-frontend/src/App.jsx @@ -4,7 +4,6 @@ import PrinterPanel from './components/PrinterPanel.jsx' import TuningPanel from './components/TuningPanel.jsx' import CalibrationButtons from './components/CalibrationButtons.jsx' import CalibrationAxis from './components/CalibrationAxis.jsx' -import TextEditOverlay from './components/TextEditOverlay.jsx' import NodeGraph from './components/NodeGraph.jsx' import PassPanel from './components/PassPanel.jsx' import PerfPanel from './components/PerfPanel.jsx' @@ -14,7 +13,7 @@ import * as tauri from './hooks/useTauri.js' import { serialize, deserialize } from './project.js' import { useFps } from './hooks/useFps.js' -const VIEW_MODES = ['source', 'detection', 'contours', 'gcode', 'printer', 'tuning'] +const VIEW_MODES = ['source', 'pipeline', 'gcode', 'printer', 'tuning'] export default function App() { const [image, setImage] = useState(null) @@ -35,45 +34,10 @@ export default function App() { const [nodeWidth, setNodeWidth] = useState(450) const [dpi, setDpi] = useState(150) const [projectPath, setProjectPath] = useState(null) // null = unsaved - const [sourceMode, setSourceMode] = useState('image') // 'image' | 'text' - const [textBlocks, setTextBlocks] = useState([ - // Sensible defaults for #10 envelope addressing. - { text: 'Your Name\n123 Your St\nYour City, ST 12345', - font_size_mm: 3, line_spacing_mm: 5, x_mm: 8, y_mm: 8 }, - { text: 'Recipient Name\n456 Their St\nTheir City, ST 67890', - font_size_mm: 5, line_spacing_mm: 8, x_mm: 35, y_mm: 95 }, - ]) const resizing = useRef(false) - // True when the project has something to plot. In text mode, "ready" = - // pipeline has produced strokes; just having text doesn't guarantee the - // graph processed it yet. Same check as image mode. const hasOutput = passes.some(p => p.strokeCount > 0) - // When in text mode, rasterise blocks into a paper-sized image source - // (debounced) and trigger pipeline processing on the new image. The - // image flows through Source → Kernel → Hull → Fill like any other. - useEffect(() => { - if (sourceMode !== 'text') return - const t = setTimeout(async () => { - try { - const info = await tauri.setTextBlocks( - textBlocks, - gcodeConfig.paper_w_mm, - gcodeConfig.paper_h_mm, - dpi, - /* strokeThicknessPx */ Math.max(2, Math.round(dpi / 50)), - ) - setImage(info) - setStrokes(null) - scheduleProcessRef.current?.() - } catch (e) { - setGlobalStatus(`Text render error: ${e.message ?? e}`) - } - }, 350) - return () => clearTimeout(t) - }, [sourceMode, textBlocks, gcodeConfig.paper_w_mm, gcodeConfig.paper_h_mm, dpi]) - // Ctrl+S / Ctrl+Shift+S — ref pattern keeps listener stable across renders const saveProjectRef = useRef(null) saveProjectRef.current = saveProject @@ -145,24 +109,9 @@ export default function App() { case 'source': setDisplayB64(image.preview_b64) break - case 'detection': + case 'pipeline': setDisplayB64(passes[0]?.vizB64 ?? null) break - case 'contours': - if (passes[0]?.hullCount > 0) { - try { - const tv = performance.now() - const b64 = await tauri.getPassViz(0, viewMode) - setPerfData(pd => ({ ...(pd ?? {}), js_viz: Math.round(performance.now() - tv) })) - setDisplayB64(b64) - } catch (e) { - setGlobalStatus(`Viz error: ${e}`) - setDisplayB64(null) - } - } else { - setDisplayB64(null) - } - break case 'gcode': if (passes.some(p => p.strokeCount > 0)) { try { @@ -282,8 +231,6 @@ export default function App() { nodeWidth, graph: passes[0].graph, gcodeConfig, - sourceMode, - textBlocks, }) await tauri.writeProjectFile(path, json) setProjectPath(path) @@ -306,8 +253,6 @@ export default function App() { if (restored.gcodeConfig) setGcodeConfig(restored.gcodeConfig) if (restored.dpi) setDpi(restored.dpi) if (restored.nodeWidth) setNodeWidth(restored.nodeWidth) - if (restored.sourceMode) setSourceMode(restored.sourceMode) - if (Array.isArray(restored.textBlocks)) setTextBlocks(restored.textBlocks) // Replace the pass graph if (restored.graph) { @@ -412,41 +357,6 @@ export default function App() {
- {/* Source mode */} -
-

Source

-
- - -
- - {sourceMode === 'text' && ( -
-

- Switch to the Source tab to place - text directly on the paper. Click empty paper to add a box, drag the - header to move, drag the SE corner to scale. -

-

- Blocks rasterise to a paper-sized image and run through the graph - (Detection → Hull → Fill) like any image source. -

- -
- )} -
- {/* Graph */}

Graph

@@ -565,7 +475,7 @@ export default function App() { {/* Top bar — accent colors match the section dots in the left panel */}
{VIEW_MODES.map(m => { - const accent = { detection: '#6366f1', contours: '#14b8a6', gcode: '#f59e0b', printer: '#10b981', tuning: '#a855f7' }[m] + const accent = { pipeline: '#6366f1', gcode: '#f59e0b', printer: '#10b981', tuning: '#a855f7' }[m] const label = m === 'gcode' ? 'G-code' : m.charAt(0).toUpperCase() + m.slice(1) return ( - )} -
- - {/* Body: SVG (visual) + textarea (caret) stacked at same coords. */} -
- {/* WYSIWYG Hershey rendering */} - - {strokes.map((pts, i) => ( - `${x},${y}`).join(' ')} - fill="none" stroke="black" strokeWidth={strokePx / mmToPx(1)} - strokeLinecap="round" strokeLinejoin="round" /> - ))} - - {/* Editable layer: caret-only, transparent text. Lining up the - character cells exactly with Hershey is impossible (Hershey is - proportional, system mono is not), but a monospace placeholder - keeps the caret tracking close enough for editing. */} -