This content cannot be embedded in an iframe.
- +Model generated! Download files below.
';
+ this.#previewArea.innerHTML = 'Model generated! Download files below.
';
}
}
diff --git a/lib/folk-google-item.ts b/lib/folk-google-item.ts
index 2f809c2..9c1d5d8 100644
--- a/lib/folk-google-item.ts
+++ b/lib/folk-google-item.ts
@@ -5,10 +5,10 @@ export type GoogleService = "gmail" | "drive" | "photos" | "calendar";
export type ItemVisibility = "local" | "shared";
const SERVICE_ICONS: Record
- ${this.#visibility === "local" ? "\u{1F512}" : "\u{1F310}"}
+ ${this.#visibility === "local" ? "๐" : "๐"}
@@ -245,7 +245,7 @@ export class FolkGoogleItem extends FolkShape {
e.stopPropagation();
this.#visibility = this.#visibility === "local" ? "shared" : "local";
- badge.textContent = this.#visibility === "local" ? "\u{1F512}" : "\u{1F310}";
+ badge.textContent = this.#visibility === "local" ? "๐" : "๐";
card.classList.remove("local", "shared");
card.classList.add(this.#visibility);
diff --git a/lib/folk-image-gen.ts b/lib/folk-image-gen.ts
index 40ca1ff..a1b8cd7 100644
--- a/lib/folk-image-gen.ts
+++ b/lib/folk-image-gen.ts
@@ -364,7 +364,7 @@ export class FolkImageGen extends FolkShape {
if (!this.#imageArea) return;
this.#imageArea.innerHTML = `
= {
- FLIGHT: "\u2708\uFE0F",
- TRANSPORT: "\uD83D\uDE8C",
- ACCOMMODATION: "\uD83C\uDFE8",
- ACTIVITY: "\uD83C\uDFAF",
- MEAL: "\uD83C\uDF7D\uFE0F",
- FREE_TIME: "\u2600\uFE0F",
- OTHER: "\uD83D\uDCCC",
+ FLIGHT: "โ๏ธ",
+ TRANSPORT: "๐",
+ ACCOMMODATION: "๐จ",
+ ACTIVITY: "๐ฏ",
+ MEAL: "๐ฝ๏ธ",
+ FREE_TIME: "โ๏ธ",
+ OTHER: "๐",
};
declare global {
@@ -222,11 +222,11 @@ export class FolkItinerary extends FolkShape {
wrapper.innerHTML = html`
@@ -580,7 +580,7 @@ export class FolkSocialPost extends FolkShape {
${this.#escapeHtml(this.#error || "Unknown error")}
- ${this.#images.length > 0 ? this.#renderImageList() : 'Try again with a different prompt
'}
+ ${this.#images.length > 0 ? this.#renderImageList() : 'Try again with a different prompt
'}
`;
}
@@ -374,7 +374,7 @@ export class FolkImageGen extends FolkShape {
if (this.#images.length === 0) {
this.#imageArea.innerHTML = `
-
+
Enter a prompt and click Generate
`;
diff --git a/lib/folk-itinerary.ts b/lib/folk-itinerary.ts
index 2a16d11..6dc385e 100644
--- a/lib/folk-itinerary.ts
+++ b/lib/folk-itinerary.ts
@@ -156,13 +156,13 @@ export interface ItineraryEntry {
}
const CATEGORY_ICONS: Record
- \uD83D\uDCC5
+ ๐
Itinerary
-
+
diff --git a/lib/folk-kicad.ts b/lib/folk-kicad.ts
index 46dc55f..eeb7ecd 100644
--- a/lib/folk-kicad.ts
+++ b/lib/folk-kicad.ts
@@ -434,14 +434,14 @@ export class FolkKiCAD extends FolkShape {
if (this.#schematicSvg) {
this.#previewArea.innerHTML = `
`;
} else {
- this.#previewArea.innerHTML = '
`;
} else {
- this.#previewArea.innerHTML = '
`;
} else {
- this.#previewArea.innerHTML = '
`;
diff --git a/lib/folk-obs-note.ts b/lib/folk-obs-note.ts
index 5582269..a9b2e5a 100644
--- a/lib/folk-obs-note.ts
+++ b/lib/folk-obs-note.ts
@@ -325,12 +325,12 @@ export class FolkObsNote extends FolkShape {
wrapper.innerHTML = html`
Schematic will appear here
';
+ this.#previewArea.innerHTML = 'Schematic will appear here
';
}
break;
case "board":
if (this.#boardSvg) {
this.#previewArea.innerHTML = `Board layout will appear here
';
+ this.#previewArea.innerHTML = 'Board layout will appear here
';
}
break;
case "drc":
@@ -451,14 +451,14 @@ export class FolkKiCAD extends FolkShape {
this.#previewArea.innerHTML = `
${passed - ? '\u2705 DRC Passed' - : `\u274C ${violations.length} Violation(s)` + ? 'โ DRC Passed' + : `โ ${violations.length} Violation(s)` }
- ${violations.map((v: any) => `\u2022 ${this.#escapeHtml(v.message || v)}
`).join("")} + ${violations.map((v: any) => `โข ${this.#escapeHtml(v.message || v)}
`).join("")}DRC results will appear here
';
+ this.#previewArea.innerHTML = 'DRC results will appear here
';
}
break;
}
diff --git a/lib/folk-map.ts b/lib/folk-map.ts
index d83336a..6f18743 100644
--- a/lib/folk-map.ts
+++ b/lib/folk-map.ts
@@ -290,20 +290,20 @@ export class FolkMap extends FolkShape {
wrapper.innerHTML = html`
- \u{1F5FA}
+ ๐บ
Map
-
+
Loading map...
-
+
-
+
- \u{1F4DD}
+ ๐
-
-
+
+
@@ -340,8 +340,8 @@ export class FolkObsNote extends FolkShape {
-
-
+
+
@@ -609,10 +609,10 @@ export class FolkObsNote extends FolkShape {
if (this.#isDirty) {
this.#saveStatusEl.className = "save-status unsaved";
- this.#saveStatusEl.innerHTML = "\u2022Unsaved";
+ this.#saveStatusEl.innerHTML = "โขUnsaved";
} else {
this.#saveStatusEl.className = "save-status saved";
- this.#saveStatusEl.innerHTML = "\u2713Saved";
+ this.#saveStatusEl.innerHTML = "โSaved";
}
}
diff --git a/lib/folk-packing-list.ts b/lib/folk-packing-list.ts
index 86a4d37..c1264bc 100644
--- a/lib/folk-packing-list.ts
+++ b/lib/folk-packing-list.ts
@@ -117,7 +117,7 @@ const styles = css`
}
.checkbox.checked::after {
- content: "\u2713";
+ content: "โ";
color: white;
font-size: 10px;
font-weight: 700;
@@ -224,11 +224,11 @@ export class FolkPackingList extends FolkShape {
wrapper.innerHTML = html`
@@ -359,7 +359,7 @@ export class FolkObsNote extends FolkShape {
0 characters
- \u2713
+ โ
Saved
- \uD83C\uDF92
+ ๐
Packing List
-
+
diff --git a/lib/folk-piano.ts b/lib/folk-piano.ts
index 095c572..9c0b33f 100644
--- a/lib/folk-piano.ts
+++ b/lib/folk-piano.ts
@@ -171,16 +171,16 @@ export class FolkPiano extends FolkShape {
wrapper.innerHTML = html`
`;
@@ -231,14 +231,14 @@ export class FolkPiano extends FolkShape {
minimizeBtn?.addEventListener("click", (e) => {
e.stopPropagation();
this.isMinimized = !this.#isMinimized;
- minimizeBtn.textContent = this.#isMinimized ? "\u{1F53D}" : "\u{1F53C}";
+ minimizeBtn.textContent = this.#isMinimized ? "๐ฝ" : "๐ผ";
});
// Click minimized view to expand
this.#minimizedEl?.addEventListener("click", (e) => {
e.stopPropagation();
this.isMinimized = false;
- if (minimizeBtn) minimizeBtn.textContent = "\u{1F53C}";
+ if (minimizeBtn) minimizeBtn.textContent = "๐ผ";
});
// Retry button
diff --git a/lib/folk-prompt.ts b/lib/folk-prompt.ts
index d692f0b..584f7de 100644
--- a/lib/folk-prompt.ts
+++ b/lib/folk-prompt.ts
@@ -91,7 +91,7 @@ const styles = css`
}
.message.streaming::after {
- content: "\u258C";
+ content: "โ";
animation: blink 1s infinite;
}
@@ -262,18 +262,18 @@ export class FolkPrompt extends FolkShape {
wrapper.innerHTML = html`
@@ -418,7 +418,7 @@ export class FolkPrompt extends FolkShape {
if (this.#messages.length === 0 && !this.#error) {
this.#messagesEl.innerHTML = `
- \u{1F3B9}
+ ๐น
Loading Shared Piano...
-
+
- \u{1F4AC}
+ ๐ฌ
AI Prompt
-
+
-
+
Ask me anything!
I can help with code, writing, analysis, and more
diff --git a/lib/folk-shape.ts b/lib/folk-shape.ts
index 46e5049..69bacb1 100644
--- a/lib/folk-shape.ts
+++ b/lib/folk-shape.ts
@@ -363,6 +363,16 @@ export class FolkShape extends FolkElement {
return this.#readonlyRect;
}
+ /** Compute the effective CSS transform scale of the parent (e.g. canvas zoom). */
+ #getParentScale(): number {
+ const parent = this.parentElement;
+ if (!parent) return 1;
+ const rect = parent.getBoundingClientRect();
+ const w = parent.offsetWidth;
+ if (!w || !rect.width) return 1;
+ return rect.width / w;
+ }
+
handleEvent(event: PointerEvent | KeyboardEvent | TouchEvent) {
// Handle touch events for mobile drag support
if (event instanceof TouchEvent) {
@@ -390,7 +400,7 @@ export class FolkShape extends FolkElement {
if (event.type === "touchmove" && this.#isTouchDragging && event.touches.length === 1) {
const touch = event.touches[0];
if (this.#lastTouchPos) {
- const zoom = window.visualViewport?.scale ?? 1;
+ const zoom = (window.visualViewport?.scale ?? 1) * this.#getParentScale();
const moveDelta = {
x: (touch.clientX - this.#lastTouchPos.x) / zoom,
y: (touch.clientY - this.#lastTouchPos.y) / zoom,
@@ -478,7 +488,7 @@ export class FolkShape extends FolkElement {
};
} else if (event.type === "pointermove") {
if (!target) return;
- const zoom = window.visualViewport?.scale ?? 1;
+ const zoom = (window.visualViewport?.scale ?? 1) * this.#getParentScale();
moveDelta = {
x: event.movementX / zoom,
y: event.movementY / zoom,
@@ -659,9 +669,10 @@ export class FolkShape extends FolkElement {
/**
* After moving, push this shape away from any overlapping siblings.
- * Resolves by minimum penetration on the axis aligned with movement direction.
+ * Uses minimum penetration depth โ picks the smallest displacement
+ * among all four directions to resolve each overlap.
*/
- #resolveOverlaps(dx: number, dy: number) {
+ #resolveOverlaps(_dx: number, _dy: number) {
const parent = this.parentElement;
if (!parent) return;
@@ -680,21 +691,25 @@ export class FolkShape extends FolkElement {
if (!overlapX || !overlapY) continue;
- // Distance to clear on each side
- const clearRight = (other.x + other.w + gap) - me.x; // push me right of other
- const clearLeft = other.x - (me.x + me.w + gap); // push me left of other (negative)
- const clearDown = (other.y + other.h + gap) - me.y; // push me below other
- const clearUp = other.y - (me.y + me.h + gap); // push me above other (negative)
+ // Distance to clear on each side (4 possible escape directions)
+ const clearRight = (other.x + other.w + gap) - me.x;
+ const clearLeft = other.x - (me.x + me.w + gap);
+ const clearDown = (other.y + other.h + gap) - me.y;
+ const clearUp = other.y - (me.y + me.h + gap);
- // Pick push direction per axis based on movement direction
- const pushX = dx >= 0 ? clearRight : clearLeft;
- const pushY = dy >= 0 ? clearDown : clearUp;
+ // Pick the direction with smallest absolute displacement
+ const candidates = [
+ { axis: "x" as const, d: clearRight },
+ { axis: "x" as const, d: clearLeft },
+ { axis: "y" as const, d: clearDown },
+ { axis: "y" as const, d: clearUp },
+ ];
+ const best = candidates.reduce((a, b) => Math.abs(a.d) < Math.abs(b.d) ? a : b);
- // Apply the axis with the smallest absolute displacement
- if (Math.abs(pushX) <= Math.abs(pushY)) {
- this.#rect.x += pushX;
+ if (best.axis === "x") {
+ this.#rect.x += best.d;
} else {
- this.#rect.y += pushY;
+ this.#rect.y += best.d;
}
me.x = this.#rect.x;
diff --git a/lib/folk-social-post.ts b/lib/folk-social-post.ts
index d0f7a0a..9f11ae6 100644
--- a/lib/folk-social-post.ts
+++ b/lib/folk-social-post.ts
@@ -370,13 +370,13 @@ const PLATFORM_CONFIG: Record<
SocialPlatform,
{ icon: string; label: string; color: string }
> = {
- x: { icon: "\ud835\udd4f", label: "X", color: "#000000" },
+ x: { icon: "๐", label: "X", color: "#000000" },
linkedin: { icon: "in", label: "LinkedIn", color: "#0A66C2" },
- instagram: { icon: "\ud83d\udcf7", label: "Instagram", color: "#E4405F" },
- youtube: { icon: "\u25b6", label: "YouTube", color: "#FF0000" },
+ instagram: { icon: "๐ท", label: "Instagram", color: "#E4405F" },
+ youtube: { icon: "โถ", label: "YouTube", color: "#FF0000" },
threads: { icon: "@", label: "Threads", color: "#000000" },
- bluesky: { icon: "\ud83e\ude77", label: "Bluesky", color: "#0085FF" },
- tiktok: { icon: "\u266b", label: "TikTok", color: "#010101" },
+ bluesky: { icon: "๐ฉท", label: "Bluesky", color: "#0085FF" },
+ tiktok: { icon: "โซ", label: "TikTok", color: "#010101" },
facebook: { icon: "f", label: "Facebook", color: "#1877F2" },
};
@@ -561,8 +561,8 @@ export class FolkSocialPost extends FolkShape {
${config.label}
-
-
+
+
@@ -572,7 +572,7 @@ export class FolkSocialPost extends FolkShape {
- \ud83d\uddbc
+ ๐ผ
No media