Merge branch 'dev'
This commit is contained in:
commit
94eb6ed5bc
|
|
@ -383,11 +383,19 @@ export class FolkShape extends FolkElement {
|
|||
|
||||
this.#updateCursors();
|
||||
|
||||
this.x = Number(this.getAttribute("x")) || 0;
|
||||
this.y = Number(this.getAttribute("y")) || 0;
|
||||
this.width = Number(this.getAttribute("width")) || "auto";
|
||||
this.height = Number(this.getAttribute("height")) || "auto";
|
||||
this.rotation = (Number(this.getAttribute("rotation")) || 0) * (Math.PI / 180);
|
||||
// Only initialize from HTML attributes if they exist — when properties
|
||||
// are set via JS before DOM insertion, attributes are absent and reading
|
||||
// them would overwrite the already-set values with 0/"auto".
|
||||
const attrX = this.getAttribute("x");
|
||||
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.rotateOrigin = { x: 0.5, y: 0.5 };
|
||||
|
|
|
|||
|
|
@ -292,11 +292,12 @@ export class FolkWrapper extends FolkShape {
|
|||
<div class="tags"></div>
|
||||
`;
|
||||
|
||||
// Replace existing content structure
|
||||
// Replace existing content structure — save parent ref before clearing
|
||||
const existingSlot = root.querySelector("slot");
|
||||
if (existingSlot?.parentElement) {
|
||||
existingSlot.parentElement.innerHTML = "";
|
||||
existingSlot.parentElement.appendChild(wrapper);
|
||||
const slotParent = existingSlot?.parentElement;
|
||||
if (slotParent) {
|
||||
slotParent.innerHTML = "";
|
||||
slotParent.appendChild(wrapper);
|
||||
}
|
||||
|
||||
// Get references
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ app.post("/api/image-gen", async (c) => {
|
|||
};
|
||||
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",
|
||||
headers: {
|
||||
Authorization: `Key ${FAL_KEY}`,
|
||||
|
|
@ -567,7 +567,7 @@ app.post("/api/video-gen/t2v", async (c) => {
|
|||
const { prompt, duration } = await c.req.json();
|
||||
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",
|
||||
headers: {
|
||||
Authorization: `Key ${FAL_KEY}`,
|
||||
|
|
@ -600,7 +600,7 @@ app.post("/api/video-gen/i2v", async (c) => {
|
|||
const { image, prompt, duration } = await c.req.json();
|
||||
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",
|
||||
headers: {
|
||||
Authorization: `Key ${FAL_KEY}`,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ export class TabCache {
|
|||
this.panes.set(moduleId, pane);
|
||||
|
||||
// Load module-specific assets
|
||||
this.loadAssets(content.scripts, content.styles);
|
||||
this.loadAssets(content.scripts, content.styles, content.inlineStyles, moduleId);
|
||||
|
||||
// Update shell state
|
||||
this.currentModuleId = moduleId;
|
||||
|
|
@ -159,6 +159,7 @@ export class TabCache {
|
|||
title: string;
|
||||
scripts: string[];
|
||||
styles: string[];
|
||||
inlineStyles: string[];
|
||||
} | null {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
|
|
@ -197,11 +198,20 @@ export class TabCache {
|
|||
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) */
|
||||
private loadAssets(scripts: string[], styles: string[]): void {
|
||||
private loadAssets(scripts: string[], styles: string[], inlineStyles: string[] = [], moduleId?: string): void {
|
||||
for (const src of scripts) {
|
||||
const el = document.createElement("script");
|
||||
el.type = "module";
|
||||
|
|
@ -217,6 +227,16 @@ export class TabCache {
|
|||
el.href = href;
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -3655,6 +3655,8 @@
|
|||
// Find and remove the nearest SVG element under cursor
|
||||
const hit = document.elementFromPoint(e.clientX, e.clientY);
|
||||
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
||||
const wbId = hit.getAttribute("data-wb-id");
|
||||
if (wbId) sync.hardDeleteShape(wbId);
|
||||
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) => {
|
||||
if (wbTool !== "eraser") return;
|
||||
const hit = e.target;
|
||||
if (hit && hit !== wbOverlay && wbOverlay.contains(hit)) {
|
||||
const wbId = hit.getAttribute("data-wb-id");
|
||||
if (wbId) {
|
||||
sync.hardDeleteShape(wbId);
|
||||
}
|
||||
if (wbId) sync.hardDeleteShape(wbId);
|
||||
hit.remove();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ self.addEventListener("fetch", (event) => {
|
|||
// Skip non-http(s) schemes (chrome-extension://, etc.) — they can't be cached
|
||||
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 (
|
||||
event.request.url.startsWith("ws://") ||
|
||||
event.request.url.startsWith("wss://") ||
|
||||
url.pathname.startsWith("/ws/") ||
|
||||
url.pathname.startsWith("/api/")
|
||||
url.pathname.includes("/api/")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ self.addEventListener("fetch", (event) => {
|
|||
}
|
||||
return response;
|
||||
})
|
||||
.catch(() => cached as Response);
|
||||
.catch(() => cached || new Response("Offline", { status: 503 }));
|
||||
return cached || fetchPromise;
|
||||
})
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue