File size: 22,411 Bytes
19388a8 3627e18 19388a8 34f3659 dede77e 02807ea 0cd47fc 3627e18 0cd47fc 6a77127 aa0793c 6a77127 19388a8 aa0793c 19388a8 be189e0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | # 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.
|