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 cloneit 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
- Clone:
git clone https://huggingface.co/spaces/mlmihjaz/LTMthencd LTM. - Auth for pushing:
git pushneeds an HF write token — runhuggingface-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. - It's a single static file — no install/build. Just open
index.html, or serve locally to test:python -m http.server 8077→ openhttp://127.0.0.1:8077/index.html. - 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). - Workflow: edit
index.html→ test locally (hard-refresh Ctrl+Shift+R) →git commit+git push origin mainto deploy. The live.static.hf.spacesite only updates AFTER a push. - Validate JS after edits: extract the inline
<script>and runnode --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+Aselect-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 — Gorequiredrejects zero)
- Increment:
- 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 localStoragedf_visits_cachefor instant paint + offline fallback. - Server value was seeded to
1990; it has since climbed naturally (1996 at last check). To re-seed: hit theset?count=Nendpoint.
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
contextContainerinafter:render. Threshold7/zoom(screen-constant). State insnapLines; handlers wired insideinitCanvas. NOTE: zoom is CSSscale()on#canvas-container, so Fabric viewport is identity — guide coords are raw canvas coords (×getRetinaScaling()viasetTransform). - 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(FabrictoGroup/toActiveSelection); Ctrl+G / Ctrl+Shift+G + Arrange menu. - Save / Load projects:
.dfp.jsonexport (💾 Save / Ctrl+S) + 📂 Open import; autosave tolocalStorage df_autosave(debounced 800ms viascheduleAutosaveinonCanvasChange), restored on load (restoreAutosave); ✲ New clears. Project shape:{app,v,name,w,h,json}. - Verified: JS
node --checkclean, 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, globalImageTracer). 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/whitepal; 1-color also drops near-white paths. Detail slider maps toltres/pathomit. Wrapped insetTimeoutso 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) viatoObject(['_name','_type'])+ a PNG thumbnail (obj.toDataURL).renderLibrary()grid (click=add viafabric.util.enlivenObjects, hover ×=delete). Export/Import the whole library as JSON ("for future versions"). Quota errors are caught. - Verified:
node --checkclean, 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 intogroup/activeSelectionchildren so a fill applies to every path of a traced vector or grouped shapes (even while grouped).updateActiveShaperoutes groups/selections through it;updateRightPanelsyncssh-fillfor groups (first child with a real fill);onSelectionChangeopens the Shapes panel forgrouptoo. Colors survive ungroup→recolor→regroup. - Pathfinder boolean ops (paper.js): added paper-core CDN (
paper@0.12.17, globalpaper,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 viaobj.toSVG()→importSVG(sub-paths united inobjToPaperPath), runs the op (subtract = bottom minus the ones above, Illustrator "Minus Front"), then rebuilds onefabric.Pathfromresult.pathData(fillRule evenodd, inherits bottom shape's style) and removes the originals. - Verified:
node --checkclean, 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
traceSourceObjfrom 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 callspreventDefault()to kill Chrome's middle-click autoscroll (the reason earlier attempts failed),setPointerCapture, thenpointermoveupdatespanX/panYfrom 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(altDragPendingwith original left/top), clone dropped on the firstobject: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(reusescopiedObject); added Ctrl+X. - Ungroup freeze fix:
updateLayerscaps 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-unitselect) with px/mm/cm/inch/m (96px=1in mapping).drawRulers()usescanvas-container.getBoundingClientRect()for origin +zoomfor scale, "nice" tick steps, pink cursor indicator; called fromapplyTransform, resize, and unit change. - Verified:
node --checkclean, 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 nativesetZoom/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 fromapplyZoom) counter-scalescornerSize/touchCornerSize/borderScaleFactorby1/zoomso handles stay constant on-screen; Canva-style white circular handles w/ purple stroke set onfabric.Object.prototypeininitCanvas. Snap-guide line width also/zoom. Ruler robustness: trim CSS-var colors,ResizeObserveron#canvas-wrap, rAF-throttled cursor redraw. - Live transform readout:
object:moving/scaling/rotating→updateRightPanelso X/Y/W/H update live. - Shapes scale to artboard:
addShapemultiplies default sizes byk = clamp(min(cw,ch)/600, 0.6, 4)so shapes aren't specks on big pages. - Recent colors + shared color:
recentColors(localStoragedf_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-opacitymoved here)/recent/delete. Image Adjustments+Filters panel (#rp-adj) is moved at init into#ct-pop-adjustpopover (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/loadProjectnow synccustom-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 inobjToPaperPath(obj, m): capture each object'scalcTransformMatrix()before discard (matsinpathfinder), neutralize the object to read transform-free centre-origin local geometry viatoSVG, import to paper, then bake the matrix into the point coords withlocal.getPathData(paperMatrix, 4)and rebuild a fresh identity-matrixCompoundPathfrom 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 anafter:renderhook (NOT a canvas object, so no layer/history pollution). Recognition = least-squares fitting:_resample(64), Kåsa_fitCircle, PCA rotation-aware_fitEllipse, line fit,_rdpcorners +_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: modelcdn.jsdelivr.net/gh/yining1023/doodleNet@master/demo/DoodleClassifier_345/model/model.json+ siblingclass_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:createdtags brush paths. Option changes re-apply viarefreshBrush(). - 📐 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 thed(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 viaobj.calcTransformMatrix()+pathOffset,fabric.util.transformPoint), removes the object, edits a node model, draws live inafter: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/_lerpBé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 cleanfabric.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.