LTM / WORKLOG.md
mlmihjaz
Rebrand to Sri Axis: remove AI + Three.js, rebuild 3D on Babylon.js
be189e0
|
Raw
History Blame Contribute Delete
22.4 kB
# DesignForge Pro — Work Log
> Continuation notes so any machine (or a fresh Claude session) can pick up where we left off.
> This file lives in the repo on purpose — `git clone` it on the new laptop and read this first.
## What this is
- **DesignForge Pro** — a Canva-style design editor. Single static `index.html` (~2000 lines) built on **Fabric.js 5.3.0** (CDN). No backend, no build step.
- **Hugging Face static Space:** `mlmihjaz/LTM`
- Repo / page: https://huggingface.co/spaces/mlmihjaz/LTM
- Live app (static host): **https://mlmihjaz-ltm.static.hf.space** (note: `.static.hf.space`, not `.hf.space`)
- Owner: Mihjaz · mlmihjaz@gmail.com · FB https://www.facebook.com/mlmihjaz · WhatsApp +94 777 869 736
- NOTE: the live Space welcome popup / promo were rebranded to **SriAI** (info@sriai.edu.lk, WhatsApp +41779412181) via the HF web editor — keep that branding when editing those sections.
## ▶ RESUME ON A NEW LAPTOP — read this first
1. **Clone:** `git clone https://huggingface.co/spaces/mlmihjaz/LTM` then `cd LTM`.
2. **Auth for pushing:** `git push` needs an HF write token — run `huggingface-cli login`, OR when git prompts use your HF **username** + a **write token** (from https://huggingface.co/settings/tokens) as the password. Credentials then cache in the OS keychain.
3. **It's a single static file** — no install/build. Just open `index.html`, or serve locally to test:
`python -m http.server 8077` → open `http://127.0.0.1:8077/index.html`.
4. **External libs are all CDN** (need internet): Fabric.js 5.3.0, ImageTracer.js (vector tracing), paper-core 0.12.17 (Pathfinder booleans). Pixabay key is inline (`PIXABAY_KEY`).
5. **Workflow:** edit `index.html` → test locally (hard-refresh **Ctrl+Shift+R**) → `git commit` + `git push origin main` to deploy. The live `.static.hf.space` site only updates AFTER a push.
6. **Validate JS after edits:** extract the inline `<script>` and run `node --check` (catches syntax errors before deploy).
## Current feature set (as of 2026-06-16)
Shapes/text/images/filters/crop/pen · templates(placeholder) · Pixabay photos · export(png/jpg/webp/svg)+copy · visitor counter · **snap guides · align/distribute · group/ungroup (top-bar icons + Ctrl+G/Shift+G) · save/open .dfp.json + autosave/restore + New · vector tracing (B&W/2/3/4/6/8/12/Full + Curvy/Sharp edge + detail) · My Library (localStorage, export/import) · deep color on groups/vectors · Pathfinder (unite/subtract/intersect/exclude) · middle-mouse pan · Alt-drag duplicate · right-click menu (copy/cut/paste/dup/group/ungroup/flip/delete) · rulers (px/mm/cm/in/m)**.
## How to deploy
Plain git over HTTPS to the HF remote (`origin` = the Space repo):
```
git add index.html
git commit -m "..."
git push origin main # pushes straight to the live Space; it rebuilds in seconds
```
Credentials were cached on the original laptop. **On a new laptop you'll need to auth**`huggingface-cli login` (or a write token as the git password) before `git push` works.
Verify a deploy landed:
```
curl -sL https://mlmihjaz-ltm.static.hf.space/ | grep -c <some-unique-string-you-just-added>
```
## Features implemented (June 2026 session)
- **Bug fixes:** stroke-only shapes (checkmark/cross/plus) were filled blobs → fixed; horizontal lines landed off-center → fixed; undo/redo pushed duplicate history states (needed 2 presses) → added dedupe in `saveHist`.
- **Shape editing:** corner-radius slider, border style (solid/dashed/dotted), gradient fill/mix (two colors + direction) for shapes & lines.
- **Pen tool:** click points → polygon; double-click/Enter to finish, Esc to cancel.
- **More shapes:** added "More Shapes" category (semicircle, quarter, chevron, plus, chat, ribbon, flag, pac).
- **UX:** left panel auto-switches to Text/Shapes on selection; `Ctrl+A` select-all; delete works on multi-selection.
- **Photos tab (Pixabay):** image search next to Templates. API key in `index.html` (`PIXABAY_KEY`). Both API and image CDN send CORS `*`, so canvas export stays clean.
- **Templates:** intentionally "coming soon" banner — clicking one only resizes the canvas. **TODO: real templates (text + shapes + bg).**
- **Branding/contact:** welcome popup (once per browser via localStorage `df_welcomed`), scrolling promo marquee in top bar, WhatsApp + Facebook buttons top-right, credit + social links in status bar.
- **Visitor counter** (status bar) — see below.
- **Load glitch fix:** canvas now fit-zooms on first paint (was 100% then snap via `setTimeout`).
## Visitor counter details
- Service: **counterapi.dev v1** (free, no auth, CORS `*`). No backend needed.
- Namespace/key: `designforge-ltm-mlmihjaz` / `visits`
- Increment: `GET https://api.counterapi.dev/v1/designforge-ltm-mlmihjaz/visits/up`
- Read (CDN-cached! always add a cache-buster): `.../visits/?cb=<ts>`
- Set: `.../visits/set?count=N` (cannot set 0 — Go `required` rejects zero)
- App display = `BASE + count`, with **BASE = 0** (so it shows the true server count).
- Counts once per session (sessionStorage `df_visit_counted`); caches last value in localStorage `df_visits_cache` for instant paint + offline fallback.
- Server value was seeded to ~1990; it has since climbed naturally (~1996 at last check). To re-seed: hit the `set?count=N` endpoint.
## Features implemented (2026-06-16 session — "Pro editing" batch)
- **Smart snap guides:** dragging an object snaps its edges/centers to the canvas (left/center/right, top/middle/bottom) and to other objects' edges/centers; pink dashed guide lines drawn on `contextContainer` in `after:render`. Threshold `7/zoom` (screen-constant). State in `snapLines`; handlers wired inside `initCanvas`. NOTE: zoom is CSS `scale()` on `#canvas-container`, so Fabric viewport is identity — guide coords are raw canvas coords (× `getRetinaScaling()` via `setTransform`).
- **Align & Distribute:** `alignObjects(mode)` — single object aligns to the canvas; multi-selection aligns within its bbox (discards the activeSelection so children are in absolute coords, then re-selects). Distribute needs 3+. UI = **⊞ Arrange ▾** topbar dropdown (`#arrange-menu`).
- **Group / Ungroup:** `groupSelection`/`ungroupSelection` (Fabric `toGroup`/`toActiveSelection`); **Ctrl+G** / **Ctrl+Shift+G** + Arrange menu.
- **Save / Load projects:** `.dfp.json` export (**💾 Save** / Ctrl+S) + **📂 Open** import; **autosave** to `localStorage df_autosave` (debounced 800ms via `scheduleAutosave` in `onCanvasChange`), **restored on load** (`restoreAutosave`); **✲ New** clears. Project shape: `{app,v,name,w,h,json}`.
- Verified: JS `node --check` clean, all new IDs/functions resolve, served page contains them. Browser-tested visuals = snap lines + arrange menu.
## Features implemented (2026-06-16 session #2 — vector tracing + library)
- **Vector tracing (raster→SVG):** uses **ImageTracer.js** (CDN: `cdn.jsdelivr.net/gh/jankovicsandras/imagetracerjs@master/imagetracer_v1.2.6.js`, global `ImageTracer`). New **Vector** left tab (`sec-trace`). Source = selected canvas image / paste / upload (`traceSourceEl`). Color modes: **B&W**, **1 color** (silhouette), **2–5 color** (`numberofcolors`). `runTrace(mode)`: downscale to ≤900px → `imagedataToSVG``fabric.loadSVGFromString``groupSVGElements` → add as `_type:'vector'` group (ungroup to edit each color). B&W/1-color pre-threshold the imagedata + use a black/white `pal`; 1-color also drops near-white paths. Detail slider maps to `ltres`/`pathomit`. Wrapped in `setTimeout` so the UI can paint a "Tracing…" status. Cross-origin/tainted images are caught + toasted.
- **My Library (persistent):** new **Library** tab (`sec-library`), `localStorage df_library`. `addToLibrary()` serializes the active object (auto-groups an activeSelection) via `toObject(['_name','_type'])` + a PNG thumbnail (`obj.toDataURL`). `renderLibrary()` grid (click=add via `fabric.util.enlivenObjects`, hover ×=delete). **Export/Import** the whole library as JSON ("for future versions"). Quota errors are caught.
- Verified: `node --check` clean, all new IDs/functions resolve, ImageTracer CDN 200. Browser-test the trace quality / tune detault detail.
## Features implemented (2026-06-16 session #3 — deep color + Pathfinder)
- **Deep color on groups/vectors:** `setStyleDeep(obj,prop,val)` recurses into `group`/`activeSelection` children so a fill applies to every path of a traced vector or grouped shapes (even while grouped). `updateActiveShape` routes groups/selections through it; `updateRightPanel` syncs `sh-fill` for groups (first child with a real fill); `onSelectionChange` opens the Shapes panel for `group` too. Colors survive ungroup→recolor→regroup.
- **Pathfinder boolean ops (paper.js):** added **paper-core** CDN (`paper@0.12.17`, global `paper`, `ensurePaper()` lazy-inits an offscreen project). `pathfinder(op)` for **unite/subtract/intersect/exclude** in the Arrange menu (`[data-pf]`). Must select 2+ objects; it **discards the activeSelection first** so children are in absolute canvas coords, converts each to a paper path via `obj.toSVG()``importSVG` (sub-paths united in `objToPaperPath`), runs the op (subtract = bottom minus the ones above, Illustrator "Minus Front"), then rebuilds one `fabric.Path` from `result.pathData` (fillRule evenodd, inherits bottom shape's style) and removes the originals.
- Verified: `node --check` clean, both CDNs 200, all fns/wiring resolve. Browser-test boolean edge cases (holes/winding) + tune if needed.
## Features implemented (2026-06-16 session #4 — editor UX + rulers)
- **Group/Ungroup as top-bar icon buttons** (`#btn-group`/`#btn-ungroup`, `.top-btn.ic`).
- **Trace replaces the source raster** even without clicking "Use selected" (runTrace derives `traceSourceObj` from the active image) so the original isn't left behind / copied.
- **Middle-mouse pan** (FINAL, commit 5ce943e): done in `setupPan()` with **Pointer Events** on `#canvas-wrap``pointerdown` (button 1) in the **capture phase** calls `preventDefault()` to kill Chrome's middle-click autoscroll (the reason earlier attempts failed), `setPointerCapture`, then `pointermove` updates `panX/panY` from clientX/Y deltas; `applyTransform` = `translate(panX,panY) scale(zoom)`. (Earlier fabric-`mouse:*` and window-listener versions did NOT work — autoscroll ate the moves.)
- **Alt-drag duplicate** reworked: armed on `mouse:down` (`altDragPending` with original left/top), clone dropped on the first `object:moving` (so a plain alt-click doesn't duplicate).
- **Right-click menu** expanded: Copy/Cut/Paste/Duplicate/Group/Ungroup/Flip/Delete. Object clipboard `copyActive`/`cutActive`/`pasteCopied` (reuses `copiedObject`); added **Ctrl+X**.
- **Ungroup freeze fix:** `updateLayers` caps at 80 rows (+ "…N more"); ungroup is deferred with a toast + confirm if >300 parts; snap-guide object loop skips when >60 objects.
- **Rulers** (`#ruler-h`/`#ruler-v`/`#ruler-corner` + `#ruler-unit` select) with **px/mm/cm/inch/m** (96px=1in mapping). `drawRulers()` uses `canvas-container.getBoundingClientRect()` for origin + `zoom` for scale, "nice" tick steps, pink cursor indicator; called from `applyTransform`, resize, and unit change.
- Verified: `node --check` clean, both CDNs ok, served file has all markers. Needs browser pass for ruler alignment + middle-pan feel.
- KNOWN: zoom is still CSS `scale()` on the container, so zooming past 100% rasterizes the whole canvas (vector edges pixelate). True crisp zoom = refactor to Fabric native `setZoom`/viewportTransform (touches applyZoom/fitZoom/snap-guides/rulers/pan) — deferred.
## Features implemented (2026-06-17 session #5 — zoom/selection polish, popup-first UX, Smart Draw)
- **Selection handles vs CSS zoom:** `syncControlScale()` (called from `applyZoom`) counter-scales `cornerSize`/`touchCornerSize`/`borderScaleFactor` by `1/zoom` so handles stay constant on-screen; Canva-style white circular handles w/ purple stroke set on `fabric.Object.prototype` in `initCanvas`. Snap-guide line width also `/zoom`. Ruler robustness: trim CSS-var colors, `ResizeObserver` on `#canvas-wrap`, rAF-throttled cursor redraw.
- **Live transform readout:** `object:moving/scaling/rotating``updateRightPanel` so X/Y/W/H update live.
- **Shapes scale to artboard:** `addShape` multiplies default sizes by `k = clamp(min(cw,ch)/600, 0.6, 4)` so shapes aren't specks on big pages.
- **Recent colors + shared color:** `recentColors` (localStorage `df_recent_colors`, 8 max). `applyColorToActive()` is the single entry (text fill / shape fill) used by recent swatches, the popup color button, side-panel inputs. Swatches render in the popup (`#ct-recent`).
- **Contextual selection popup** (`#canvas-toolbar`): `updateToolbarForType(obj)` shows image→`#ct-adjust`(⚙)/`#ct-crop`, text/shape→color button (`.ctx-color`); common = flip/rotate/layer/dup(`#ct-dup`)/opacity(`#obj-opacity` moved here)/recent/delete. Image **Adjustments+Filters** panel (`#rp-adj`) is **moved at init into `#ct-pop-adjust`** popover (toggled by ⚙); closes on outside-click / non-image select.
- **Single export path:** removed right-panel Quick Export; topbar **Download** opens the modal; Copy-to-clipboard moved into the modal.
- **Resize consolidated:** removed right-panel Canvas section; topbar **⊡ Resize** → size modal (presets + custom W/H). `updateSB`/`loadProject` now sync `custom-w`/`custom-h`.
- **Topbar regroup:** New/Open/Save/Undo/Redo are icon-only (`.top-btn.ic`), then Resize/Group/Ungroup/Arrange.
- **AI Designer panel** (`#rp-ai`): front-end-only chat UI (gradient ✦ header, 13px bubbles, suggestion chips, autosize textarea). **No backend yet**`submit()` just echoes a "coming soon" bot reply. Ready to wire a real model later.
- **UNITE rotation bug — FIXED (took 5 tries):** root cause = paper.js 0.12 boolean ops run on raw segment coords and IGNORE pending/unbaked matrices; also `discardActiveObject()` doesn't bake the active-selection's rotation back into children. FINAL fix in `objToPaperPath(obj, m)`: capture each object's `calcTransformMatrix()` **before** discard (`mats` in `pathfinder`), neutralize the object to read transform-free centre-origin local geometry via `toSVG`, import to paper, then **bake the matrix into the point coords** with `local.getPathData(paperMatrix, 4)` and rebuild a fresh identity-matrix `CompoundPath` from that string → unite gets already-absolute geometry. Switched CDN to **paper-full** (was paper-core). VERIFIED WORKING by user.
- **Smart Draw (AutoDraw-style)** in Shapes panel (`#btn-autodraw`, `toggleDraw/startDraw/stopDraw`, `drawMode`): drag to sketch → snaps to a clean **editable** shape. Live stroke painted in an `after:render` hook (NOT a canvas object, so no layer/history pollution). Recognition = **least-squares fitting**: `_resample`(64), Kåsa `_fitCircle`, PCA rotation-aware `_fitEllipse`, line fit, `_rdp` corners + `_polyResidual` (RMS pt→chord); decide by lowest fit error **biased ×1.4 toward round**. Outputs Line / Circle / Ellipse(rotated) / Rect(axis-aligned) / Polygon(triangle/penta/hexa/irregular via detected vertices) / open Polyline. Esc exits.
- RESOLVED in session #6 — replaced by the AutoDraw clip-art flow (TF.js DoodleNet + Iconify) and folded into the Drawing Toolset as "Magic Pencil".
## Features implemented (2026-06-17 session #6 — Drawing Toolset + AI AutoDraw)
All in the **Shapes** left panel under "Drawing Tools" (`.draw-tools` palette of `.dtool` buttons + `.draw-opts`: `#draw-color`, `#draw-size`, `#brush-type`). One tool active at a time via `activeTool` + `selectTool(name)` / `deactivateAllTools(except)` / `refreshToolBtns()`. State vars near `drawMode`: `activeTool, brushActive, lineOn/linePts/lineCur, penBezOn/bezAnchors/bezDrag/bezCur, nodeEditOn/editNodes/editClosed/editStyle/editDrag`. All tool pointer routing is added into the canvas `mouse:down/move/up/dblclick` handlers in `initCanvas`; live previews are drawn in `after:render` hooks (never as objects → no layer/history spam).
- **✨ Magic Pencil** (`t-magic`) — the AutoDraw-style AI tool (was "Smart Draw"). **TF.js + DoodleNet** (345 QuickDraw classes), lazy-loaded on first use from jsDelivr: model `cdn.jsdelivr.net/gh/yining1023/doodleNet@master/demo/DoodleClassifier_345/model/model.json` + sibling `class_names.txt` (CORS ok); `@tensorflow/tfjs@4.22.0`. Flow (`drawEnd``smartRecognize`… actually now): keep the rough stroke as an editable Polyline, classify top-5 (`topPredictions`, input rasterized to 28×28 centred, strokes=1/bg=0 per `_strokeToTensor`), then **Iconify search API** (`_iconifySearch`/`gatherIcons`, free, CORS, 200k+ icons) for the guesses → render a **suggestion bar** (`#ai-suggest`/`showSuggestions`) of clip-art; click one (`placeIcon`) → fetch SVG, `currentColor``#333`, `loadSVGFromString`→group, scaled to the sketch bbox, replaces the stroke (editable `_type:'vector'`). One-shot: pen deactivates on mouse release (`deactivateDraw`); suggestion bar persists (× / Esc / drawing again hides it). Graceful fallback keeps the sketch if AI/icons unavailable. NOTE: the old least-squares recognizer (`recognizeStroke`/`geomFromLabel`/`createRecognized`/`_fitCircle`/`_fitEllipse`/`_rdp`/`_polyResidual`) is still in the file but currently UNUSED by Magic Pencil — kept for reuse.
- **🖌️ Brush** (`t-brush`) — fabric free-drawing. `enableBrush()`/`disableBrush()`; types Pencil/Marker(2.2× width + 55% alpha via `_hexA`)/Spray/Dots(circle); size `#draw-size`, color `#draw-color`. `path:created` tags brush paths. Option changes re-apply via `refreshBrush()`.
- **📐 Line** (`t-line`) — dot-click straight open polyline. `lineClick` (Shift-constrain to 0/45/90° via `_constrain`), preview hook, `finishLine()` on double-click / Enter → `fabric.Polyline`. Stays active for the next line.
- **✒️ Pen (Bézier, Illustrator-style)** (`t-pen`) — `penDown`/`penMove(p,alt)`/`penUp`; click = corner, click-drag = smooth mirrored handles, **Alt-drag = break handle (cusp)** (skip mirror), click the first anchor (within 9px) to close. `_bezD(anchors,closed)` builds the `d` (M/L/C/Z); `finishBez(closed)` on Enter/dbl-click → `fabric.Path` (closed fills with draw color).
- **Node editing (Direct-Selection)** — double-click any `path`/`polyline`/`polygon``enterNodeEdit(obj)` parses commands to canvas-coord nodes (maps via `obj.calcTransformMatrix()` + `pathOffset`, `fabric.util.transformPoint`), removes the object, edits a node model, draws live in `after:render`. `nodeDown/Move/Up`: drag anchors (handles follow), drag handles (mirror unless Alt → break), **click a segment to insert an anchor** (`nodeInsert` — straight split or De Casteljau `_cubicPt`/`_lerp` Bézier split, closing segment too), **double-click an anchor to delete** (`nodeDblClick`), **Alt-click an anchor = corner↔smooth** (`toggleNode` — drop handles, or add symmetric handles along the neighbour tangent). `exitNodeEdit(true)` (Enter/Esc/switch tool) rebuilds a clean `fabric.Path`.
- Keyboard: Enter finishes Line/Pen; Enter/Esc commits node-edit; Esc exits the active tool. Verified: inline `<script>` parses clean (`new Function`) on every push.
## Known notes / next ideas
- Drawing toolset is the foundation for a "complete library": more brush textures/pressure/taper, dashed/calligraphy nibs, fill toggle for pen shapes, marquee multi-anchor select. The unused least-squares recognizer could power a "snap to clean shape" toggle.
- Templates are placeholders — biggest remaining "real Canva" feature.
- Counter relies on a 3rd-party free service; if it dies, swap for a tiny Gradio/Docker Space hosting a JSON counter.
- Possible next: save/load projects (localStorage + JSON import/export), alignment/snap guides, grouping, multi-page.
## Git history (recent)
Run `git log --oneline` for the full list. Latest session commits: shape/undo fixes → welcome/promo → sidebar spacing → visitor counter (+ stale-read fix, seed 1990) → load-glitch fix.
---
## 2026-06-18 — Major re-architecture: "Sri Axis" (3D-enabled 2D design app)
Rebranded DesignForge Pro → **Sri Axis**. Removed the Claude AI Designer + the Anthropic proxy (`app.py` is now a minimal FastAPI static server; `requirements.txt` = fastapi+uvicorn only) and removed **Three.js** entirely. 3D rebuilt on **Babylon.js** (CDN + earcut), woven into the 2D app as a transparent overlay synced to the Fabric artboard.
3D engine (Babylon): Extrude / Revolve / **Inflate** (puffy pillow on a uniform grid). Shapes AND text both convert through one accurate pipeline: object is rasterised (`toCanvasElement`) onto a padded canvas → ImageTracer (alpha-keyed, black-on-white) → contours grouped into fills+holes by **even/odd nesting** → per-part `ExtrudePolygon` + merge. This keeps strokes/line icons as real rings (e.g. bicycle wheels), letter counters open (o/a/e/g), and disjoint/decorative shapes intact. Camera tilt baked into geometry so the default face-on view reads Tilt X = 0. Per-object move (drag on ground plane) / Spin-Tilt-Roll-Size sliders (+editable number inputs) / right-drag tilt+roll / gizmo-free selection via Fabric pointer (auto-switch 2D↔3D) / delete+copy+paste for 3D / orthographic default + perspective toggle / wheel zooms 3D when a 3D object is selected (Ctrl+wheel = artboard) / multi-light setup (key+fill+ambient, colour/power/direction). Save composites 2D+3D into one PNG.
UI: top bar = Select(default)+Text+drawing tools(Magic/Brush/Pen/Line + brush options) + duplicate/delete; right panel = **Inspector** (dynamic) holding Position&Size (px/mm/cm/m/in/ft units), Appearance (fill/line/line-type/opacity/corner), Fill&Effects (gradient/shadow/skew), Text (font/size/spacing/align/justify/letter/line/bend), Pathfinder (unite/subtract/intersect/exclude), and the 3D controls — all contextual. Shapes panel = shapes only; **shape search also queries Iconify** (200k+ online icons → editable vectors). Resize modal unit-aware. Unlimited zoom. Removed promo marquee + social buttons + startup messages. Copy/paste/duplicate fixed for multi-selections (activeSelection). Curved/bend text via Fabric text-on-path.
NOT yet verified in a real browser end-to-end beyond local serving; tested via node --check + manual local serve on :8778.