Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions src/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,6 @@ function isHex(s) {
return typeof s === 'string' && /^#[0-9a-f]{3,8}$/i.test(s.trim());
}

function hexToRgb(hex) {
const m = String(hex).trim().toLowerCase().replace(/^#/, '');
const full = m.length === 3 ? m.split('').map((c) => c + c).join('') : m.slice(0, 6);
return {
r: parseInt(full.slice(0, 2), 16) || 0,
g: parseInt(full.slice(2, 4), 16) || 0,
b: parseInt(full.slice(4, 6), 16) || 0,
};
}

function rgbToHex({ r, g, b }) {
return '#' + [r, g, b].map((v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, '0')).join('');
}

function opSharpenRadii(design, factor = 0.5) {
const radii = design.borders?.radii || [];
const next = radii.map((r) => ({ ...r, value: Math.max(0, Math.round((r.value || 0) * factor)) }));
Expand Down
4 changes: 2 additions & 2 deletions src/studio.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,9 @@ export async function runStudio(opts) {
res.end('not found');
return;
}
// Race-safe read — single try/catch instead of exists→stat→read chain.
// Race-free read — let readFileSync surface ENOENT / EISDIR / EACCES
// in one syscall instead of a stat→read pair (which would TOCTOU).
try {
if (!statSync(filePath).isFile()) throw new Error('not a file');
const body = readFileSync(filePath);
const ext = extname(filePath).toLowerCase();
res.writeHead(200, { 'content-type': MIME[ext] || 'application/octet-stream' });
Expand Down
23 changes: 17 additions & 6 deletions src/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@ import { formatTokens } from './formatters/tokens.js';
import { formatTailwind } from './formatters/tailwind.js';
import { formatCssVars } from './formatters/css-vars.js';
import { saveSnapshot, getHistory } from './history.js';
import { writeFileSync, statSync } from 'fs';
import { openSync, closeSync, ftruncateSync, writeSync } from 'fs';
import { join } from 'path';

// Race-safe "update only if file exists" — statSync inside try/catch
// closes the toctou window vs. existsSync→writeFileSync.
// Race-free "update only if file exists" — open with 'r+' atomically
// requires an existing file (throws ENOENT otherwise) and gives us a
// write-capable descriptor in one syscall, eliminating the toctou window
// that statSync→writeFileSync would have. Truncate then write through the
// same fd so no other process can sneak between check and write.
Comment on lines +11 to +15
function updateIfExists(path, content) {
let fd;
try {
if (!statSync(path).isFile()) return false;
writeFileSync(path, content, 'utf-8');
fd = openSync(path, 'r+');
ftruncateSync(fd, 0);
writeSync(fd, content, 0, 'utf-8');
Comment on lines +20 to +21
return true;
} catch { return false; }
} catch {
return false;
} finally {
if (fd !== undefined) {
try { closeSync(fd); } catch { /* best-effort close */ }
}
}
}

export async function syncDesign(url, options = {}) {
Expand Down
15 changes: 0 additions & 15 deletions website/app/components/HeroExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export default function HeroExtractor() {
const [errorMsg, setErrorMsg] = useState(null);
const [rateLimitMsg, setRateLimitMsg] = useState(null);
const [downloadBusy, setDownloadBusy] = useState(false);
const [copied, setCopied] = useState(false);
const inputRef = useRef(null);

// Accept ?url= query param (Chrome extension handoff, deep links). Only
Expand Down Expand Up @@ -63,7 +62,6 @@ export default function HeroExtractor() {
setFiles(null);
setErrorMsg(null);
setRateLimitMsg(null);
setCopied(false);
}, []);

const handleEvent = useCallback((event) => {
Expand Down Expand Up @@ -203,19 +201,6 @@ export default function HeroExtractor() {
}
}, [files, downloadBusy]);

const handleCopyMarkdown = useCallback(async () => {
if (!files) return;
const mdKey = Object.keys(files).find((k) => k.endsWith('-design-language.md'));
if (!mdKey) return;
try {
await navigator.clipboard.writeText(files[mdKey]);
setCopied(true);
setTimeout(() => setCopied(false), 1800);
} catch {
// no-op
}
}, [files]);

const stageLabel = stage ? STAGE_LABEL[stage] || stage : null;
const streaming = status === 'streaming';
const disabled = streaming;
Expand Down
Loading