Merge branch 'dev'
This commit is contained in:
commit
94eb6ed5bc
|
|
@ -383,11 +383,19 @@ export class FolkShape extends FolkElement {
|
||||||
|
|
||||||
this.#updateCursors();
|
this.#updateCursors();
|
||||||
|
|
||||||
this.x = Number(this.getAttribute("x")) || 0;
|
// Only initialize from HTML attributes if they exist — when properties
|
||||||
this.y = Number(this.getAttribute("y")) || 0;
|
// are set via JS before DOM insertion, attributes are absent and reading
|
||||||
this.width = Number(this.getAttribute("width")) || "auto";
|
// them would overwrite the already-set values with 0/"auto".
|
||||||
this.height = Number(this.getAttribute("height")) || "auto";
|
const attrX = this.getAttribute("x");
|
||||||
this.rotation = (Number(this.getAttribute("rotation")) || 0) * (Math.PI / 180);
|
const attrY = this.getAttribute("y");
|
||||||
|
const attrW = this.getAttribute("width");
|
||||||
|
const attrH = this.getAttribute("height");
|
||||||
|
const attrR = this.getAttribute("rotation");
|
||||||
|
if (attrX !== null) this.x = Number(attrX) || 0;
|
||||||
|
if (attrY !== null) this.y = Number(attrY) || 0;
|
||||||
|
if (attrW !== null) this.width = Number(attrW) || "auto";
|
||||||
|
if (attrH !== null) this.height = Number(attrH) || "auto";
|
||||||
|
if (attrR !== null) this.rotation = (Number(attrR) || 0) * (Math.PI / 180);
|
||||||
|
|
||||||
this.#rect.transformOrigin = { x: 0, y: 0 };
|
this.#rect.transformOrigin = { x: 0, y: 0 };
|
||||||
this.#rect.rotateOrigin = { x: 0.5, y: 0.5 };
|
this.#rect.rotateOrigin = { x: 0.5, y: 0.5 };
|
||||||
|
|
|
||||||
|
|
@ -292,11 +292,12 @@ export class FolkWrapper extends FolkShape {
|
||||||
<div class="tags"></div>
|
<div class="tags"></div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Replace existing content structure
|
// Replace existing content structure — save parent ref before clearing
|
||||||
const existingSlot = root.querySelector("slot");
|
const existingSlot = root.querySelector("slot");
|
||||||
if (existingSlot?.parentElement) {
|
const slotParent = existingSlot?.parentElement;
|
||||||
existingSlot.parentElement.innerHTML = "";
|
if (slotParent) {
|
||||||
existingSlot.parentElement.appendChild(wrapper);
|
slotParent.innerHTML = "";
|
||||||
|
slotParent.appendChild(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get references
|
// Get references
|
||||||
|
|
|
||||||
|
|
@ -533,7 +533,7 @@ app.post("/api/image-gen", async (c) => {
|
||||||
};
|
};
|
||||||
const styledPrompt = (stylePrompts[style] || "") + prompt;
|
const styledPrompt = (stylePrompts[style] || "") + prompt;
|
||||||
|
|
||||||
const res = await fetch("https://queue.fal.run/fal-ai/flux-pro/v1.1", {
|
const res = await fetch("https://fal.run/fal-ai/flux-pro/v1.1", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Key ${FAL_KEY}`,
|
Authorization: `Key ${FAL_KEY}`,
|
||||||
|
|
@ -567,7 +567,7 @@ app.post("/api/video-gen/t2v", async (c) => {
|
||||||
const { prompt, duration } = await c.req.json();
|
const { prompt, duration } = await c.req.json();
|
||||||
if (!prompt) return c.json({ error: "prompt required" }, 400);
|
if (!prompt) return c.json({ error: "prompt required" }, 400);
|
||||||
|
|
||||||
const res = await fetch("https://queue.fal.run/fal-ai/wan/v2.1", {
|
const res = await fetch("https://fal.run/fal-ai/wan/v2.1", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Key ${FAL_KEY}`,
|
Authorization: `Key ${FAL_KEY}`,
|
||||||
|
|
@ -600,7 +600,7 @@ app.post("/api/video-gen/i2v", async (c) => {
|
||||||
const { image, prompt, duration } = await c.req.json();
|
const { image, prompt, duration } = await c.req.json();
|
||||||
if (!image) return c.json({ error: "image required" }, 400);
|
if (!image) return c.json({ error: "image required" }, 400);
|
||||||
|
|
||||||
const res = await fetch("https://queue.fal.run/fal-ai/kling-video/v1/standard/image-to-video", {
|
const res = await fetch("https://fal.run/fal-ai/kling-video/v1/standard/image-to-video", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Key ${FAL_KEY}`,
|
Authorization: `Key ${FAL_KEY}`,
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export class TabCache {
|
||||||
this.panes.set(moduleId, pane);
|
this.panes.set(moduleId, pane);
|
||||||
|
|
||||||
// Load module-specific assets
|
// Load module-specific assets
|
||||||
this.loadAssets(content.scripts, content.styles);
|
this.loadAssets(content.scripts, content.styles, content.inlineStyles, moduleId);
|
||||||
|
|
||||||
// Update shell state
|
// Update shell state
|
||||||
this.currentModuleId = moduleId;
|
this.currentModuleId = moduleId;
|
||||||
|
|
@ -159,6 +159,7 @@ export class TabCache {
|
||||||
title: string;
|
title: string;
|
||||||
scripts: string[];
|
scripts: string[];
|
||||||
styles: string[];
|
styles: string[];
|
||||||
|
inlineStyles: string[];
|
||||||
} | null {
|
} | null {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(html, "text/html");
|
const doc = parser.parseFromString(html, "text/html");
|
||||||
|
|
@ -197,11 +198,20 @@ export class TabCache {
|
||||||
styles.push(href);
|
styles.push(href);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { body, title, scripts, styles };
|
// Extract inline <style> blocks (exclude shell-embedded styles)
|
||||||
|
const inlineStyles: string[] = [];
|
||||||
|
doc.querySelectorAll("head style").forEach((s) => {
|
||||||
|
const css = s.textContent || "";
|
||||||
|
// Skip the shell's own embedded styles (tab-pane, spinner, etc.)
|
||||||
|
if (css.includes(".rspace-tab-pane")) return;
|
||||||
|
if (css.trim()) inlineStyles.push(css);
|
||||||
|
});
|
||||||
|
|
||||||
|
return { body, title, scripts, styles, inlineStyles };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load script and style assets (idempotent — browsers deduplicate module scripts) */
|
/** Load script and style assets (idempotent — browsers deduplicate module scripts) */
|
||||||
private loadAssets(scripts: string[], styles: string[]): void {
|
private loadAssets(scripts: string[], styles: string[], inlineStyles: string[] = [], moduleId?: string): void {
|
||||||
for (const src of scripts) {
|
for (const src of scripts) {
|
||||||
const el = document.createElement("script");
|
const el = document.createElement("script");
|
||||||
el.type = "module";
|
el.type = "module";
|
||||||
|
|
@ -217,6 +227,16 @@ export class TabCache {
|
||||||
el.href = href;
|
el.href = href;
|
||||||
document.head.appendChild(el);
|
document.head.appendChild(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inject inline <style> blocks (tagged for deduplication)
|
||||||
|
for (let i = 0; i < inlineStyles.length; i++) {
|
||||||
|
const tag = `tab-style-${moduleId || "unknown"}-${i}`;
|
||||||
|
if (document.querySelector(`style[data-tab-style="${tag}"]`)) continue;
|
||||||
|
const el = document.createElement("style");
|
||||||
|
el.dataset.tabStyle = tag;
|
||||||
|
el.textContent = inlineStyles[i];
|
||||||
|
document.head.appendChild(el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show a specific pane and update shell state */
|
/** Show a specific pane and update shell state */
|
||||||
|
|
|
||||||
|
|
@ -3655,6 +3655,8 @@
|
||||||
// Find and remove the nearest SVG element under cursor
|
// Find and remove the nearest SVG element under cursor
|
||||||
const hit = document.elementFromPoint(e.clientX, e.clientY);
|
const hit = document.elementFromPoint(e.clientX, e.clientY);
|
||||||
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
||||||
|
const wbId = hit.getAttribute("data-wb-id");
|
||||||
|
if (wbId) sync.hardDeleteShape(wbId);
|
||||||
hit.remove();
|
hit.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3766,15 +3768,14 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Eraser: click on existing SVG strokes to delete them + remove from Automerge
|
// Eraser click fallback — deletion is handled in pointerdown above,
|
||||||
|
// but catch any clicks that slip through (e.g. keyboard-triggered)
|
||||||
wbOverlay.addEventListener("click", (e) => {
|
wbOverlay.addEventListener("click", (e) => {
|
||||||
if (wbTool !== "eraser") return;
|
if (wbTool !== "eraser") return;
|
||||||
const hit = e.target;
|
const hit = e.target;
|
||||||
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
||||||
const wbId = hit.getAttribute("data-wb-id");
|
const wbId = hit.getAttribute("data-wb-id");
|
||||||
if (wbId) {
|
if (wbId) sync.hardDeleteShape(wbId);
|
||||||
sync.hardDeleteShape(wbId);
|
|
||||||
}
|
|
||||||
hit.remove();
|
hit.remove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,12 @@ self.addEventListener("fetch", (event) => {
|
||||||
// Skip non-http(s) schemes (chrome-extension://, etc.) — they can't be cached
|
// Skip non-http(s) schemes (chrome-extension://, etc.) — they can't be cached
|
||||||
if (!url.protocol.startsWith("http")) return;
|
if (!url.protocol.startsWith("http")) return;
|
||||||
|
|
||||||
// Skip WebSocket and API requests entirely
|
// Skip WebSocket and API requests entirely (including module APIs like /space/module/api/...)
|
||||||
if (
|
if (
|
||||||
event.request.url.startsWith("ws://") ||
|
event.request.url.startsWith("ws://") ||
|
||||||
event.request.url.startsWith("wss://") ||
|
event.request.url.startsWith("wss://") ||
|
||||||
url.pathname.startsWith("/ws/") ||
|
url.pathname.startsWith("/ws/") ||
|
||||||
url.pathname.startsWith("/api/")
|
url.pathname.includes("/api/")
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +101,7 @@ self.addEventListener("fetch", (event) => {
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
.catch(() => cached as Response);
|
.catch(() => cached || new Response("Offline", { status: 503 }));
|
||||||
return cached || fetchPromise;
|
return cached || fetchPromise;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue