diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 6fbe5a843..42ccab4fe 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -196,18 +196,6 @@ }, "category": "设计" }, - { - "name": "shadcn", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/shadcn" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "开发工具" - }, { "name": "frontend-slides", "source": { @@ -244,18 +232,6 @@ }, "category": "文档处理" }, - { - "name": "ppt-master", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/ppt-master" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "文档处理" - }, { "name": "next-skills", "source": { @@ -280,18 +256,6 @@ }, "category": "开发工具" }, - { - "name": "mcp-playwright", - "source": { - "source": "local", - "path": "./plugins/codex/plugins/mcp-playwright" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "MCP" - }, { "name": "eapil-llmapi-structured", "source": { @@ -315,6 +279,42 @@ "authentication": "ON_INSTALL" }, "category": "知识库与检索" + }, + { + "name": "shadcn", + "source": { + "source": "local", + "path": "./plugins/codex/plugins/shadcn" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "开发工具" + }, + { + "name": "ppt-master", + "source": { + "source": "local", + "path": "./plugins/codex/plugins/ppt-master" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "文档处理" + }, + { + "name": "mcp-playwright", + "source": { + "source": "local", + "path": "./plugins/codex/plugins/mcp-playwright" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "MCP" } ] } diff --git a/config/external-sources.lock.json b/config/external-sources.lock.json index 8c43facd2..58d7d2874 100644 --- a/config/external-sources.lock.json +++ b/config/external-sources.lock.json @@ -33,8 +33,8 @@ "repo": "https://github.com/shadcn-ui/ui.git", "ref": "main", "adapter": "claude-skill", - "commit": "c879483c9694d7b135ed1efe8f73233d924ae551", - "syncedAt": "2026-06-17T07:05:29Z" + "commit": "c2ddedf5d24e0cee05936c69e1f199a9975a07b2", + "syncedAt": "2026-06-18T16:00:00Z" }, { "id": "frontend-slides", @@ -69,8 +69,8 @@ "repo": "https://github.com/hugohe3/ppt-master.git", "ref": "main", "adapter": "claude-skill", - "commit": "313ee6729dba50cbdd50b67debe601c483a5bcab", - "syncedAt": "2026-06-17T07:05:29Z" + "commit": "7249902c363f0b33dee9a9f7112fb258ae9c3bf7", + "syncedAt": "2026-06-18T16:00:00Z" }, { "id": "next-skills", diff --git a/plugins/codex/.agents/plugins/marketplace.json b/plugins/codex/.agents/plugins/marketplace.json index 8a2a488fb..24093d000 100644 --- a/plugins/codex/.agents/plugins/marketplace.json +++ b/plugins/codex/.agents/plugins/marketplace.json @@ -196,18 +196,6 @@ }, "category": "设计" }, - { - "name": "shadcn", - "source": { - "source": "local", - "path": "./plugins/shadcn" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "开发工具" - }, { "name": "frontend-slides", "source": { @@ -244,18 +232,6 @@ }, "category": "文档处理" }, - { - "name": "ppt-master", - "source": { - "source": "local", - "path": "./plugins/ppt-master" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "文档处理" - }, { "name": "next-skills", "source": { @@ -280,18 +256,6 @@ }, "category": "开发工具" }, - { - "name": "mcp-playwright", - "source": { - "source": "local", - "path": "./plugins/mcp-playwright" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "MCP" - }, { "name": "eapil-llmapi-structured", "source": { @@ -315,6 +279,42 @@ "authentication": "ON_INSTALL" }, "category": "知识库与检索" + }, + { + "name": "shadcn", + "source": { + "source": "local", + "path": "./plugins/shadcn" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "开发工具" + }, + { + "name": "ppt-master", + "source": { + "source": "local", + "path": "./plugins/ppt-master" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "文档处理" + }, + { + "name": "mcp-playwright", + "source": { + "source": "local", + "path": "./plugins/mcp-playwright" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "MCP" } ] } diff --git a/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json b/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json index adec3d104..43102854d 100644 --- a/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json +++ b/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json @@ -3,5 +3,5 @@ "name": "playwright浏览器自动化操作", "version": "20260605", "keySource": "none", - "syncedAt": "2026-06-17T16:01:07Z" + "syncedAt": "2026-06-18T16:01:22Z" } diff --git a/plugins/codex/plugins/ppt-master/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/ppt-master/THIRD_PARTY_SOURCE.json index a0d732f21..f35a80db6 100644 --- a/plugins/codex/plugins/ppt-master/THIRD_PARTY_SOURCE.json +++ b/plugins/codex/plugins/ppt-master/THIRD_PARTY_SOURCE.json @@ -2,8 +2,8 @@ "sourceId": "ppt-master", "repo": "https://github.com/hugohe3/ppt-master.git", "ref": "main", - "commit": "313ee6729dba50cbdd50b67debe601c483a5bcab", + "commit": "7249902c363f0b33dee9a9f7112fb258ae9c3bf7", "adapter": "claude-skill", "sourcePath": "skills/ppt-master", - "syncedAt": "2026-06-17T07:05:29Z" + "syncedAt": "2026-06-18T16:00:00Z" } diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/references/visual-styles/photo-editorial.md b/plugins/codex/plugins/ppt-master/skills/ppt-master/references/visual-styles/photo-editorial.md index b52443b77..c6748cd82 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/references/visual-styles/photo-editorial.md +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/references/visual-styles/photo-editorial.md @@ -10,6 +10,8 @@ Photo-led editorial — large full-bleed photography dominates the page, text po - Decoration: thin rules, section numbering, small figure notes; nothing competes with the image. - Whitespace: generous around text; the photo fills, the type breathes beside it. Asymmetric magazine composition. +> **No usable image → fall back to `editorial`.** This style's spine is the photograph; when a page has no suitable image available, render it in the `editorial` text-led layout (magazine columns) rather than a full-bleed placeholder — an empty / dashed image frame contradicts the style's whole premise. A deterministic, observable condition, not a judgment call. + ## 2. Typography character - Editorial serif / CJK title × clean sans body; magazine-column cadence; small precise captions and figure notes. Words are concise — they point, they don't fill. diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/app.js b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/app.js index 58f9b47b1..6b8b39b3d 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/app.js +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/app.js @@ -64,6 +64,8 @@ latin: "Latin", sample_cjk: "数字化转型战略", sample_latin: "Digital Transformation", + style_preview_label: "Overall impression (color + typography)", + style_preview_body: "· rough feel only, not the actual slide layout", mode_continuous_desc: "Generate the whole deck in one pass.", mode_split_desc: "Stop after the spec; resume SVG generation in a fresh window.", refine_off_desc: "Spec is written in one go; the pipeline auto-proceeds.", @@ -127,6 +129,8 @@ latin: "西文", sample_cjk: "数字化转型战略", sample_latin: "Digital Transformation", + style_preview_label: "整体形象(配色 + 字体)", + style_preview_body: "· 仅大致形象,非实际版式", mode_continuous_desc: "一次性连续生成整份演示文稿。", mode_split_desc: "写完设计规范后停止,另开窗口继续生成页面。", refine_off_desc: "设计规范一次写完,流程自动继续。", @@ -445,6 +449,19 @@ return spec.candidates || spec.options || []; } + function usesCustomImagePlanValue(value) { + var ids = (CAT.image_usage || []).map(function (item) { return item.id; }); + return value && ids.indexOf(value) === -1; + } + + function customImagePlanHasAiSignal() { + return imageStrategyCandidates().length > 0 || !!recId("image_ai_path"); + } + + function needsGeneratedImagesForUsage(value) { + return value === "ai" || (usesCustomImagePlanValue(value) && customImagePlanHasAiSignal()); + } + function imageStrategySelectedIndex() { var spec = imageStrategySpec(); var idx = spec.selected || 0; @@ -495,6 +512,18 @@ "body_text" ]; + function normHex(val) { + var v = (val || "").trim(); + if (!/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(v)) return null; + return v.charAt(0) === "#" ? v : "#" + v; + } + function hexOr(val, fallback) { + return normHex(val) || fallback; + } + // Replaced when the combined color+typography preview mounts; the color and + // typography sections call it after every change so the preview stays live. + var refreshStylePreview = function () {}; + function renderColor(host) { var cands = (REC.color && REC.color.candidates) || []; var sec = section(5, "sec_color"); @@ -504,11 +533,6 @@ var cardSwatchRefs = []; // idx -> {role: swatchEl}, for live override feedback var selectedIdx = -1; - function normHex(val) { - var v = (val || "").trim(); - if (!/^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(v)) return null; - return v.charAt(0) === "#" ? v : "#" + v; - } function paintSwatch(elem, val) { var n = normHex(val); elem.style.background = n || "transparent"; @@ -526,6 +550,7 @@ STATE.color = { name: c.name || "", palette: Object.assign({}, normPalette(c)) }; grid.querySelectorAll(".color-card").forEach(function (card, i) { card.classList.toggle("selected", i === idx); }); applyHexInputs(STATE.color.palette); + refreshStylePreview(); } cands.forEach(function (c, idx) { @@ -571,6 +596,7 @@ if (n && selectedIdx >= 0 && cardSwatchRefs[selectedIdx] && cardSwatchRefs[selectedIdx][role]) { cardSwatchRefs[selectedIdx][role].style.background = n; } + refreshStylePreview(); }); hexInputs[role] = inp; hexSwatches[role] = sw; line.appendChild(sw); line.appendChild(inp); @@ -593,11 +619,22 @@ host.appendChild(sec); } + function previewFontStack(primary, fallback) { + if (!primary) return fallback || ""; + if (!fallback) return primary; + return primary + ", " + fallback; + } + function fontSample(box, slot, css) { var line = el("div", "font-sample-line"); var cjk = el("span", "fs-cjk", slot.sample_cjk || t("sample_cjk")); var lat = el("span", "fs-latin", slot.sample_latin || t("sample_latin")); - if (css) { cjk.style.fontFamily = css; lat.style.fontFamily = css; } + var cjkStack = previewFontStack(slot.cjk, css); + var latinStack = previewFontStack(slot.latin, css); + if (cjkStack) cjk.style.fontFamily = cjkStack; + if (latinStack) lat.style.fontFamily = latinStack; + if (cjkStack) cjk.title = cjkStack; + if (latinStack) lat.title = latinStack; line.appendChild(cjk); line.appendChild(lat); box.appendChild(line); } @@ -622,6 +659,7 @@ if (sizeInput) sizeInput.value = STATE.typography.body_size || ""; customInput.style.display = "none"; grid.querySelectorAll(".font-card").forEach(function (card, i) { card.classList.toggle("selected", i === idx); }); + refreshStylePreview(); } function selectCustomTypography() { @@ -636,6 +674,7 @@ customCard.classList.add("selected"); customInput.style.display = "block"; customInput.focus(); + refreshStylePreview(); } cands.forEach(function (c, idx) { @@ -663,6 +702,7 @@ customInput.addEventListener("input", function () { if (!STATE.typography || STATE.typography.name !== "custom") selectCustomTypography(); STATE.typography.custom = customInput.value; + refreshStylePreview(); }); sec.appendChild(customInput); @@ -679,6 +719,7 @@ sizeInput.addEventListener("input", function () { if (!STATE.typography) STATE.typography = { name: "", heading: {}, body: {} }; STATE.typography.body_size = sizeInput.value; + refreshStylePreview(); }); sizeRow.appendChild(sizeInput); sizeRow.appendChild(el("div", "toggle-desc", t("font_body_size_hint"))); @@ -702,6 +743,86 @@ } } + // Combined color + typography preview — not a ninth confirmation, just a + // live "overall impression" of the two style choices made above. Kept + // deliberately abstract (a style chip, not a slide layout); page layout + // preview is the live-preview server's job (Step 6). + function renderStylePreview(host) { + var wrap = el("div", "style-preview"); + var label = el("div", "style-preview-label"); + label.appendChild(el("span", "spl-title", t("style_preview_label"))); + // The "rough feel, not a slide layout" caveat sits in the label in the + // UI font — never rendered in the candidate's body font, so it cannot + // pose as sample content. + label.appendChild(el("span", "spl-note", t("style_preview_body"))); + wrap.appendChild(label); + var card = el("div", "style-preview-card"); + var textcol = el("div", "sp-textcol"); + var title = el("div", "sp-title"); + var titleCjk = el("span", "sp-title-cjk"); + var titleLat = el("span", "sp-title-lat"); + title.appendChild(titleCjk); title.appendChild(titleLat); + var bodyRow = el("div", "sp-body"); + var accentBar = el("span", "sp-accent-bar"); + var bodyWrap = el("div", "sp-body-wrap"); + var bodyCjk = el("span", "sp-body-cjk"); + var bodyLat = el("span", "sp-body-lat"); + bodyWrap.appendChild(bodyCjk); bodyWrap.appendChild(bodyLat); + bodyRow.appendChild(accentBar); bodyRow.appendChild(bodyWrap); + textcol.appendChild(title); textcol.appendChild(bodyRow); + var chip = el("div", "sp-chip"); + var chipDot = el("span", "sp-chip-dot"); + var chipLabel = el("span", "sp-chip-label"); + chip.appendChild(chipDot); chip.appendChild(chipLabel); + card.appendChild(textcol); card.appendChild(chip); + wrap.appendChild(card); + host.appendChild(wrap); + // Pin the strip just under the sticky topbar so it stays visible while + // the user scrolls through the color / icon / typography sections. + var topbar = document.getElementById("topbar"); + if (topbar) wrap.style.top = topbar.offsetHeight + "px"; + + function paint() { + var pal = (STATE.color && STATE.color.palette) || {}; + var typ = STATE.typography || {}; + var head = typ.heading || {}, body = typ.body || {}; + var bg = hexOr(pal.background, "#ffffff"); + var sbg = hexOr(pal.secondary_bg, bg); + var pri = hexOr(pal.primary, "#1a3a6b"); + var acc = hexOr(pal.accent, pri); + var sacc = hexOr(pal.secondary_accent, acc); + var txt = hexOr(pal.body_text, "#1d2430"); + var bodyPx = Math.max(12, Math.min(26, parseInt(typ.body_size, 10) || 18)); + var headStack = previewFontStack(head.cjk, head.css); + var headLatStack = previewFontStack(head.latin, head.css); + var bodyStack = previewFontStack(body.cjk, body.css); + var bodyLatStack = previewFontStack(body.latin, body.css); + + card.style.background = bg; + titleCjk.textContent = head.sample_cjk || t("sample_cjk"); + titleLat.textContent = head.sample_latin || t("sample_latin"); + title.style.color = pri; + title.style.fontSize = Math.round(bodyPx * 1.7) + "px"; + titleCjk.style.fontFamily = headStack || ""; + titleLat.style.fontFamily = headLatStack || ""; + // CJK and Latin previewed with their own stacks (mirrors the title + // and the per-card font samples) so each script's font is visible. + bodyCjk.textContent = body.sample_cjk || t("sample_cjk"); + bodyLat.textContent = body.sample_latin || t("sample_latin"); + bodyWrap.style.color = txt; + bodyWrap.style.fontSize = bodyPx + "px"; + bodyCjk.style.fontFamily = bodyStack || ""; + bodyLat.style.fontFamily = bodyLatStack || ""; + accentBar.style.background = acc; + chip.style.background = sbg; + chipDot.style.background = sacc; + chipLabel.textContent = t("role_secondary_bg"); + chipLabel.style.color = txt; + } + refreshStylePreview = paint; + paint(); + } + function renderImages(host) { var sec = section(8, "sec_images"); var sub = el("div", "subfield"); @@ -711,11 +832,10 @@ var strategyGrid = el("div", "font-grid"); var strategyCands = imageStrategyCandidates(); function usesCustomImagePlan() { - var ids = (CAT.image_usage || []).map(function (item) { return item.id; }); - return STATE.image_usage && ids.indexOf(STATE.image_usage) === -1; + return usesCustomImagePlanValue(STATE.image_usage); } function needsGeneratedImages() { - return STATE.image_usage === "ai" || usesCustomImagePlan(); + return needsGeneratedImagesForUsage(STATE.image_usage); } function refreshAiControls() { var needsAiPath = needsGeneratedImages(); @@ -806,13 +926,23 @@ function renderAll() { var host = document.getElementById("sections"); host.innerHTML = ""; + // Detach the previous preview's repaint closure before the sections + // re-render: color/typography auto-select would otherwise call it and + // write to now-detached nodes until renderStylePreview remounts it. + refreshStylePreview = function () {}; renderCanvas(host); renderPages(host); renderAudience(host); renderStyle(host); - renderColor(host); - renderIcons(host); - renderTypography(host); + // Group the preview with the three sections it reflects so its sticky + // scope ends when typography scrolls past — it does not linger over the + // image / mode / refine sections below. + var styleGroup = el("div", "style-group"); + renderStylePreview(styleGroup); + renderColor(styleGroup); + renderIcons(styleGroup); + renderTypography(styleGroup); + host.appendChild(styleGroup); renderImages(host); renderMode(host); renderRefine(host); @@ -871,8 +1001,7 @@ function confirm() { var btn = document.getElementById("btn-confirm"); var payload = Object.assign({}, STATE); - var imageUsageIds = (CAT.image_usage || []).map(function (item) { return item.id; }); - var customImagePlan = payload.image_usage && imageUsageIds.indexOf(payload.image_usage) === -1; + var customImagePlan = usesCustomImagePlanValue(payload.image_usage); if (payload.image_usage === "custom" || (customImagePlan && !String(payload.image_usage).trim())) { document.getElementById("confirm-status").textContent = t("image_usage_custom_required"); var customImageInput = document.querySelector(".image-usage-custom-input"); @@ -880,7 +1009,7 @@ return; } if (customImagePlan) payload.image_usage = String(payload.image_usage).trim(); - if (payload.image_usage !== "ai" && !customImagePlan) { + if (!needsGeneratedImagesForUsage(payload.image_usage)) { delete payload.image_ai_path; delete payload.image_strategy; } diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/style.css b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/style.css index f10396e9c..b06b7768d 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/style.css +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/confirm_ui/static/style.css @@ -298,3 +298,63 @@ body { } .cf-title { font-size: 22px; font-weight: 700; color: #067647; } .cf-hint { font-size: 14px; color: var(--muted); margin-top: 10px; line-height: 1.6; } + +/* Combined color + typography "overall impression" preview — sticky strip + that rides along the color / icon / typography picking sections. */ +.style-preview { + position: sticky; + z-index: 5; + background: var(--bg); + padding: 8px 0; + margin-bottom: 16px; +} +.style-preview-label { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: 8px; + margin-bottom: 6px; +} +.spl-title { font-size: 12.5px; font-weight: 600; color: var(--muted); } +.spl-note { font-size: 11.5px; color: var(--muted); } +.style-preview-card { + border: 1px solid var(--line); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 14px 18px; + display: flex; + align-items: center; + gap: 16px; + overflow: hidden; +} +.sp-textcol { flex: 1; min-width: 0; } +.sp-title { + font-weight: 700; + line-height: 1.25; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.sp-title-lat { margin-left: 10px; opacity: 0.92; } +.sp-body { display: flex; align-items: stretch; gap: 10px; margin-top: 6px; } +.sp-accent-bar { flex: 0 0 4px; border-radius: 2px; } +.sp-body-wrap { + flex: 1; + min-width: 0; + line-height: 1.5; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.sp-body-lat { margin-left: 10px; opacity: 0.92; } +.sp-chip { + flex: none; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 7px 12px; + border-radius: 8px; + border: 1px solid rgba(0, 0, 0, 0.06); +} +.sp-chip-dot { width: 12px; height: 12px; border-radius: 50%; } +.sp-chip-label { font-size: 12.5px; } diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/docs/confirm_ui.md b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/docs/confirm_ui.md index 3df8d525e..77c8528f0 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/docs/confirm_ui.md +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/docs/confirm_ui.md @@ -103,9 +103,11 @@ Both files live under `/confirm_ui/`. - `recommend.*` names the recommended `id` for each enumerable field (must match a `catalogs.json` id, or be a free string for a recommended custom value). The page badges and pre-selects it. **Guarantee**: if a `recommend.*` is omitted, the page falls back to the first catalog option so every enumerable field always shows one badged recommendation — but the AI should still set them for a meaningful default. Legacy aliases are accepted for old files (`line` → `tabler-outline`, `filled` → `tabler-filled`, `monochrome` → `chunk-filled`, `search` → `web`, `default` → `auto`, `builtin` → `host-native`), but new files should write canonical ids. For `recommend.image_usage`, do not write bare `"custom"`; if several image sources are mixed, write the concrete prose plan directly, such as `"封面用 AI 生成,产品页用用户素材,行业页用网络来源"` / `"AI cover + user product assets + web industry images"`. - When `recommend.image_usage` is `ai` or a custom plan that includes AI, also set `recommend.image_ai_path` to one of `auto` / `api` / `host-native` / `manual`; the page presents these as explicit choices. +- For a custom image plan, the page treats "may include AI" as true only when the recommendation includes `recommend.image_ai_path` or `image_strategy.candidates`; a custom plan without those signals is handled as non-AI and omits AI controls / fields. - **Color candidates carry the user-facing core `palette`**: `background`, `secondary_bg`, `primary`, `accent`, `secondary_accent`, and `body_text`. The page renders labelled swatches and offers per-role override inputs. Legacy `text` is accepted as an alias for `body_text`, but new files should write `body_text`. Strategist derives secondary text, borders, state colors, and visual-style neutral tiers later when writing `design_spec.md` / `spec_lock.md`; those are not user-facing confirmation choices. - **Candidate display text may be bilingual**: color / typography candidates can provide `name_zh` / `name_en` and `note_zh` / `note_en`; the page falls back to legacy `name` / `note`. -- **Typography candidates split CJK and Latin** for both `heading` and `body`; `css` is the preview `font-family` stack. Each candidate should also include `body_size` (the body baseline in px; common values are 18 and 24, but any reasonable integer is valid). The page exposes `body_size` as an editable numeric field, and also offers a custom typography text box so the user is not limited to the proposed candidates. +- **Typography candidates split CJK and Latin** for both `heading` and `body`; `css` is the fallback preview `font-family` stack. The page previews CJK sample text with `cjk + css` and Latin sample text with `latin + css`, so the two script choices are visible independently. Each candidate should also include `body_size` (the body baseline in px; common values are 18 and 24, but any reasonable integer is valid). The page exposes `body_size` as an editable numeric field, and also offers a custom typography text box so the user is not limited to the proposed candidates. +- **Combined style preview** — a compact live "overall impression" strip sits just above the color section and is **sticky**: it pins under the topbar so it stays visible while the user scrolls through the color / icon / typography sections, keeping the picking controls and their combined effect on screen together. It applies the currently selected color palette **and** typography (heading sample in `primary` over `background`, body sample in `body_text`, an `accent` bar, a `secondary_bg` chip) and repaints on every color / HEX-override / font / `body_size` change. It does not replace the per-candidate swatches or font samples (those stay for picking); it is deliberately an abstract style chip, **not** a slide-layout preview — page layout preview remains the live-preview server's job (Step 6). No schema field; it derives entirely from the existing color + typography selections. - **Generated image style candidates** live in `image_strategy.candidates` and are shown only when `image_usage` is `ai` or a custom image plan may include generated images. Each candidate records `rendering`, `palette`, and short `visual` / `color` / `mood` lines from Strategist h.5. The chosen value is written to `result.json.image_strategy`; it is omitted when generated images are not part of the plan. - `recommend.generation_mode` and `refine_spec` mirror the two mandatory notes in SKILL.md Step 4. Confirmed `generation_mode: "split"` / `refine_spec: true` are explicit user choices, equivalent to opting in through chat. - `lang` is a soft default; an explicit user language choice in the page (persisted to `localStorage`) wins. diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/source_to_md/pdf_to_md.py b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/source_to_md/pdf_to_md.py index c8367c2fd..c24377c4b 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/source_to_md/pdf_to_md.py +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/source_to_md/pdf_to_md.py @@ -340,7 +340,12 @@ VECTOR_FIGURE_DPI = 180 VECTOR_CAPTION_SEARCH_HEIGHT = 380 VECTOR_CAPTION_HORIZONTAL_GAP = 90 MAX_VECTOR_BACKGROUND_AREA_RATIO = 1.9 -FIGURE_CAPTION_RE = re.compile(r'^(?:Figure|Fig\.)\s*\d+\s*[:.]', re.IGNORECASE) +# Caption delimiters: ``Figure 1:`` / ``Figure 1.`` (classic) and ``Figure 1 |`` +# (the DeepMind / Distill / Nature house style used by many ML papers, incl. +# full-width ``|``). Without the pipe variants, captioned vector figures route +# to the generic per-drawing fallback, which discards fine-grained line plots +# (each plot is hundreds of tiny primitives, none large enough on its own). +FIGURE_CAPTION_RE = re.compile(r'^(?:Figure|Fig\.?)\s*\d+\s*[:.||]', re.IGNORECASE) def should_keep_image( diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_editor/static/style.css b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_editor/static/style.css index c43711b39..893a3e2e3 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_editor/static/style.css +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_editor/static/style.css @@ -229,9 +229,14 @@ html, body { #svg-content svg { display: block; + /* Both dimensions auto so the box hugs the slide at any aspect ratio: with + only height:auto, portrait formats (e.g. 1242x1660) keep their attribute + width while max-height shrinks the height, leaving large white bands + inside the white card. width:auto lets the element scale proportionally. */ + width: auto; + height: auto; max-width: 100%; max-height: calc(100vh - 72px); - height: auto; } /* ---------- SVG element interaction states ---------- */ diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py index c08a77686..df35b8126 100644 --- a/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py +++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/scripts/svg_to_pptx/pptx_cli.py @@ -17,7 +17,7 @@ if __package__ in {None, ''}: sys.modules.setdefault('svg_to_pptx', package) __package__ = 'svg_to_pptx' -from .pptx_dimensions import CANVAS_FORMATS, get_project_info +from .pptx_dimensions import CANVAS_FORMATS, get_project_info, get_viewbox_dimensions from .pptx_discovery import find_svg_files, find_notes_files from .pptx_builder import create_pptx_with_native_svg from .pptx_narration import NARRATION_EXTENSIONS, find_narration_files, probe_audio_duration @@ -334,6 +334,28 @@ Recorded narration: verbose = not args.quiet + # Honor the actual SVG pixels over a stale project-recorded format. The + # canvas_format read from project init can disagree with what the Executor + # actually drew — e.g. a mirror template imported at 2560×1440 while the + # project was initialized as ppt169 (1280×720). When the first SVG's real + # viewBox doesn't match the recorded format's dimensions, drop the format + # so the builder sizes the slide by pixels (custom_pixels path). Standard + # decks match exactly, so this only changes behavior on the conflict case. + # An explicit --format always wins and is never second-guessed. + if args.format is None and canvas_format: + fmt_info = CANVAS_FORMATS.get(canvas_format) + actual_dims = get_viewbox_dimensions(ref_files[0]) + if fmt_info and actual_dims: + fmt_dims = (fmt_info.get('width'), fmt_info.get('height')) + if fmt_dims != actual_dims: + if verbose: + print( + f" Recorded format '{canvas_format}' " + f"({fmt_dims[0]}×{fmt_dims[1]}) differs from SVG viewBox " + f"({actual_dims[0]}×{actual_dims[1]}); exporting by SVG pixels" + ) + canvas_format = None + enable_notes = not args.no_notes notes: dict[str, str] = {} if enable_notes: diff --git a/plugins/codex/plugins/shadcn/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/shadcn/THIRD_PARTY_SOURCE.json index 788014c4e..b4d6311b4 100644 --- a/plugins/codex/plugins/shadcn/THIRD_PARTY_SOURCE.json +++ b/plugins/codex/plugins/shadcn/THIRD_PARTY_SOURCE.json @@ -2,8 +2,8 @@ "sourceId": "shadcn", "repo": "https://github.com/shadcn-ui/ui.git", "ref": "main", - "commit": "c879483c9694d7b135ed1efe8f73233d924ae551", + "commit": "c2ddedf5d24e0cee05936c69e1f199a9975a07b2", "adapter": "claude-skill", "sourcePath": "skills/shadcn", - "syncedAt": "2026-06-17T07:05:29Z" + "syncedAt": "2026-06-18T16:00:00Z" }