diff --git a/config/external-sources.lock.json b/config/external-sources.lock.json index b0ff8389..3d871910 100644 --- a/config/external-sources.lock.json +++ b/config/external-sources.lock.json @@ -15,8 +15,8 @@ "repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git", "ref": "main", "adapter": "claude-skill", - "commit": "823b0a14d3539b5d78c0efb614426a4fab5983ec", - "syncedAt": "2026-09-26T16:00:00Z" + "commit": "09170eec67eefd46a7ae85de61b40c194020f997", + "syncedAt": "2026-09-27T16:00:00Z" }, { "id": "caveman", @@ -78,8 +78,8 @@ "repo": "https://github.com/hugohe3/ppt-master.git", "ref": "main", "adapter": "claude-skill", - "commit": "481e057ecd9f5ff094c9c789b17b2d1331e278e8", - "syncedAt": "2026-09-23T16:00:00Z" + "commit": "680de11f1bef4628b68d5daad9dffec569fbd51f", + "syncedAt": "2026-09-27T16:00:00Z" }, { "id": "grill-me", @@ -168,8 +168,8 @@ "repo": "https://github.com/vercel/next.js.git", "ref": "canary", "adapter": "skill-collection", - "commit": "1cc0f6617e2f7df9068083066a4c2db76b51fdcb", - "syncedAt": "2026-09-26T16:00:00Z" + "commit": "0423222b7eb3a1373b5bff4c939fd69858928993", + "syncedAt": "2026-09-27T16:00:00Z" }, { "id": "shuorenhua", @@ -177,8 +177,8 @@ "repo": "https://github.com/MrGeDiao/shuorenhua.git", "ref": "main", "adapter": "claude-skill", - "commit": "cd681637edb6000bde4b09f63e489a7012dd2a72", - "syncedAt": "2026-09-26T16:00:00Z" + "commit": "9e6400d5e2cc050485c38fbf0dda682bce66c180", + "syncedAt": "2026-09-27T16:00:00Z" } ] } diff --git a/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json b/plugins/codex/plugins/mcp-playwright/MCP_SOURCE.json index 6078c213..6d5ebe31 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-09-26T16:02:01Z" + "syncedAt": "2026-09-27T16:02:07Z" } diff --git a/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json index 792115b2..e9e59636 100644 --- a/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json +++ b/plugins/codex/plugins/next-skills/THIRD_PARTY_SOURCE.json @@ -2,8 +2,8 @@ "sourceId": "next-skills", "repo": "https://github.com/vercel/next.js.git", "ref": "canary", - "commit": "1cc0f6617e2f7df9068083066a4c2db76b51fdcb", + "commit": "0423222b7eb3a1373b5bff4c939fd69858928993", "adapter": "skill-collection", "sourcePath": "skills", - "syncedAt": "2026-09-26T16:00:00Z" + "syncedAt": "2026-09-27T16:00:00Z" } diff --git a/plugins/codex/plugins/ppt-master/README.md b/plugins/codex/plugins/ppt-master/README.md index b7d85bab..78f7b029 100644 --- a/plugins/codex/plugins/ppt-master/README.md +++ b/plugins/codex/plugins/ppt-master/README.md @@ -15,7 +15,7 @@ English | [中文](./README_CN.md) ## ❤️ Sponsors -This project is kept free and open source with the support of Kimi, PackyCode, APIKEY.FAN, RunAPI, YouYun ZhiSuan, APIMart and other sponsors. +This project is kept free and open source with the support of Kimi, PackyCode, APIKEY.FAN, RunAPI, APIMart and other sponsors. > **[Want to appear here?](SPONSORING.md)** @@ -45,10 +45,6 @@ Thanks to [Kimi](https://www.kimi.com/code/?aff=ppt-master) for sponsoring this



-
-[YouYun ZhiSuan](https://www.compshare.cn/coding-plan?ytag=GPU_YY-git_pptmaster0624), UCloud's AI cloud platform, provides domestic and international model APIs, CodingPlan packages, enterprise concurrency, technical support, and invoicing. Register through the dedicated link to receive up to **¥10 in free trial credit**. PPT Master is also available there as a hosted Agent for users who do not want to deploy it locally.
-
### APIMart
diff --git a/plugins/codex/plugins/ppt-master/skills/ppt-master/SPONSORS_CN.md b/plugins/codex/plugins/ppt-master/skills/ppt-master/SPONSORS_CN.md
index a272df2c..b4159e68 100644
--- a/plugins/codex/plugins/ppt-master/skills/ppt-master/SPONSORS_CN.md
+++ b/plugins/codex/plugins/ppt-master/skills/ppt-master/SPONSORS_CN.md
@@ -40,12 +40,6 @@ PPT Master 始终免费开源。以下赞助方共同支持项目的持续维护
[RunAPI](https://runapi.host/register?aff=WMLJ) 通过一个 API Key 提供 OpenAI、Claude、Gemini、DeepSeek、Grok 等 150+ 主流模型。通过专属链接注册并联系管理员,即可领取 **¥7 免费额度**。
-### 优云智算
-
-
-
-[优云智算](https://www.compshare.cn/coding-plan?ytag=GPU_YY-git_pptmaster0624) 是 UCloud 旗下 AI 云平台,提供国内外模型 API、CodingPlan 套餐、企业级并发、技术支持和开票服务。通过专属链接注册,最高可获得 **¥10 免费体验金**。平台还提供无需本地部署的 PPT Master Agent。
-
### APIMart
diff --git a/plugins/codex/plugins/shuorenhua/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/shuorenhua/THIRD_PARTY_SOURCE.json
index 3db57b0c..1b4b7131 100644
--- a/plugins/codex/plugins/shuorenhua/THIRD_PARTY_SOURCE.json
+++ b/plugins/codex/plugins/shuorenhua/THIRD_PARTY_SOURCE.json
@@ -2,8 +2,8 @@
"sourceId": "shuorenhua",
"repo": "https://github.com/MrGeDiao/shuorenhua.git",
"ref": "main",
- "commit": "cd681637edb6000bde4b09f63e489a7012dd2a72",
+ "commit": "9e6400d5e2cc050485c38fbf0dda682bce66c180",
"adapter": "claude-skill",
"sourcePath": ".",
- "syncedAt": "2026-09-26T16:00:00Z"
+ "syncedAt": "2026-09-27T16:00:00Z"
}
diff --git a/plugins/codex/plugins/ui-ux-pro-max/THIRD_PARTY_SOURCE.json b/plugins/codex/plugins/ui-ux-pro-max/THIRD_PARTY_SOURCE.json
index 77cb092e..fb51818c 100644
--- a/plugins/codex/plugins/ui-ux-pro-max/THIRD_PARTY_SOURCE.json
+++ b/plugins/codex/plugins/ui-ux-pro-max/THIRD_PARTY_SOURCE.json
@@ -2,8 +2,8 @@
"sourceId": "ui-ux-pro-max",
"repo": "https://github.com/nextlevelbuilder/ui-ux-pro-max-skill.git",
"ref": "main",
- "commit": "823b0a14d3539b5d78c0efb614426a4fab5983ec",
+ "commit": "09170eec67eefd46a7ae85de61b40c194020f997",
"adapter": "claude-skill",
"sourcePath": ".claude/skills/ui-ux-pro-max",
- "syncedAt": "2026-09-26T16:00:00Z"
+ "syncedAt": "2026-09-27T16:00:00Z"
}
diff --git a/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/design_system.py b/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/design_system.py
index 1114088e..d5fe1575 100644
--- a/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/design_system.py
+++ b/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/design_system.py
@@ -254,6 +254,31 @@ def _filter_anti_patterns_for_mode(anti_patterns: str, mode: str) -> str:
return " + ".join(clause.strip() for clause in kept if clause.strip())
+def _mode_support_labels(style: dict, anti_patterns: str):
+ """Light/dark support labels that agree with the product's anti-patterns.
+
+ A light result keeps its "avoid dark mode" clause, so a dark-capable style
+ must not read as a plain "Dark supported" next to it.
+ """
+ light = style.get("light_mode", "")
+ dark = style.get("dark_mode", "")
+ avoids_dark = any(
+ marker in (anti_patterns or "").lower() for marker in _DARK_ANTI_PATTERN_MARKERS
+ )
+ if dark and dark != "not-recommended" and avoids_dark:
+ dark = f"{dark}, but avoid for this product"
+ return light, dark
+
+
+def _button_outline_text_color(colors: dict) -> str:
+ """Primary for outline-button text when it is readable, else the foreground."""
+ primary = colors.get("primary", "#2563EB")
+ background = colors.get("background", "#FFFFFF")
+ if (_contrast_ratio(primary, background) or 0) >= 4.5:
+ return primary
+ return colors.get("foreground") or primary
+
+
# ============ DESIGN SYSTEM GENERATOR ============
class DesignSystemGenerator:
"""Generates design system recommendations from aggregated searches."""
@@ -650,10 +675,13 @@ def format_ascii_box(design_system: dict) -> str:
motion_snippet = design_system.get("motion_snippet", {})
def wrap_text(text: str, prefix: str, width: int) -> list:
- """Wrap long text into multiple lines."""
+ """Wrap long text into multiple lines, splitting tokens (URLs) wider than a line."""
if not text:
return []
- words = text.split()
+ room = width - 2 - len(prefix)
+ words = []
+ for word in text.split():
+ words.extend(word[i:i + room] for i in range(0, len(word), room))
lines = []
current_line = prefix
for word in words:
@@ -667,6 +695,10 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(current_line)
return lines
+ def add_wrapped(text: str, prefix: str = "│ ") -> None:
+ for line in wrap_text(text, prefix, BOX_WIDTH):
+ lines.append(line.ljust(BOX_WIDTH) + "│")
+
# Build sections from pattern
sections = pattern.get("sections", "").split(" > ")
sections = [s.strip() for s in sections if s.strip()]
@@ -683,7 +715,7 @@ def format_ascii_box(design_system: dict) -> str:
# Design Dials section (only if at least one dial was set)
if any(dials.get(k) is not None for k in ("variance", "motion", "density")):
- lines.append(section_header("DESIGN DIALS", BOX_WIDTH + 1))
+ lines.append(section_header("DESIGN DIALS", BOX_WIDTH))
if dials.get("variance") is not None:
lines.append(f"│ Variance: {dials['variance']}/10 — {dials['variance_label']}".ljust(BOX_WIDTH) + "│")
if dials.get("motion") is not None:
@@ -692,71 +724,65 @@ def format_ascii_box(design_system: dict) -> str:
lines.append(f"│ Density: {dials['density']}/10 — {dials['density_label']}".ljust(BOX_WIDTH) + "│")
# Pattern section
- lines.append(section_header("PATTERN", BOX_WIDTH + 1))
- lines.append(f"│ Name: {pattern.get('name', '')}".ljust(BOX_WIDTH) + "│")
+ lines.append(section_header("PATTERN", BOX_WIDTH))
+ add_wrapped(f"Name: {pattern.get('name', '')}", "│ ")
if pattern.get('conversion'):
- lines.append(f"│ Conversion: {pattern.get('conversion', '')}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Conversion: {pattern.get('conversion', '')}")
if pattern.get('cta_placement'):
- lines.append(f"│ CTA: {pattern.get('cta_placement', '')}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"CTA: {pattern.get('cta_placement', '')}")
lines.append("│ Sections:".ljust(BOX_WIDTH) + "│")
for i, section in enumerate(sections, 1):
- lines.append(f"│ {i}. {section}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"{i}. {section}", "│ ")
# Style section
- lines.append(section_header("STYLE", BOX_WIDTH + 1))
- lines.append(f"│ Name: {style.get('name', '')}".ljust(BOX_WIDTH) + "│")
- light = style.get("light_mode", "")
- dark = style.get("dark_mode", "")
+ lines.append(section_header("STYLE", BOX_WIDTH))
+ add_wrapped(f"Name: {style.get('name', '')}", "│ ")
+ light, dark = _mode_support_labels(style, anti_patterns)
if light or dark:
- lines.append(f"│ Mode Support: Light {light} Dark {dark}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Mode Support: Light {light} | Dark {dark}")
if style.get("keywords"):
- for line in wrap_text(f"Keywords: {style.get('keywords', '')}", "│ ", BOX_WIDTH):
- lines.append(line.ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Keywords: {style.get('keywords', '')}")
if style.get("best_for"):
- for line in wrap_text(f"Best For: {style.get('best_for', '')}", "│ ", BOX_WIDTH):
- lines.append(line.ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Best For: {style.get('best_for', '')}")
if style.get("performance") or style.get("accessibility"):
- perf_a11y = f"Performance: {style.get('performance', '')} | Accessibility: {style.get('accessibility', '')}"
- lines.append(f"│ {perf_a11y}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Performance: {style.get('performance', '')} | Accessibility: {style.get('accessibility', '')}")
# Colors section (extended palette with ANSI swatches)
- lines.append(section_header("COLORS", BOX_WIDTH + 1))
+ lines.append(section_header("COLORS", BOX_WIDTH))
for label, key, css_var in SEMANTIC_COLOR_ENTRIES:
hex_val = colors.get(key, "")
if not hex_val:
continue
swatch = hex_to_ansi(hex_val)
- content = f"│ {swatch}{label + ':':14s} {hex_val:10s} ({css_var})"
+ content = f"│ {swatch}{label + ':':18s} {hex_val:10s} ({css_var})"
lines.append(ansi_ljust(content, BOX_WIDTH) + "│")
if colors.get("notes"):
for line in wrap_text(f"Notes: {colors.get('notes', '')}", "│ ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "│")
# Typography section
- lines.append(section_header("TYPOGRAPHY", BOX_WIDTH + 1))
- lines.append(f"│ {typography.get('heading', '')} / {typography.get('body', '')}".ljust(BOX_WIDTH) + "│")
+ lines.append(section_header("TYPOGRAPHY", BOX_WIDTH))
+ add_wrapped(f"{typography.get('heading', '')} / {typography.get('body', '')}", "│ ")
if typography.get("mood"):
- for line in wrap_text(f"Mood: {typography.get('mood', '')}", "│ ", BOX_WIDTH):
- lines.append(line.ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Mood: {typography.get('mood', '')}")
if typography.get("best_for"):
- for line in wrap_text(f"Best For: {typography.get('best_for', '')}", "│ ", BOX_WIDTH):
- lines.append(line.ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Best For: {typography.get('best_for', '')}")
if typography.get("google_fonts_url"):
- lines.append(f"│ Google Fonts: {typography.get('google_fonts_url', '')}".ljust(BOX_WIDTH) + "│")
+ add_wrapped(f"Google Fonts: {typography.get('google_fonts_url', '')}")
if typography.get("css_import"):
- lines.append(f"│ CSS Import: {typography.get('css_import', '')[:70]}...".ljust(BOX_WIDTH) + "│")
+ lines.append(f"│ CSS Import: {typography.get('css_import', '')[:67]}...".ljust(BOX_WIDTH) + "│")
# Key Effects section
if effects:
- lines.append(section_header("KEY EFFECTS", BOX_WIDTH + 1))
+ lines.append(section_header("KEY EFFECTS", BOX_WIDTH))
for line in wrap_text(effects, "│ ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "│")
# Motion section (GSAP skeleton, only if --motion dial was set)
if motion_snippet:
- lines.append(section_header("MOTION", BOX_WIDTH + 1))
- lines.append(f"│ {motion_snippet.get('Category', '')} ({motion_snippet.get('Intensity Tier', '')})".ljust(BOX_WIDTH) + "│")
- lines.append(f"│ Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: {motion_snippet.get('Easing', '')}".ljust(BOX_WIDTH) + "│")
+ lines.append(section_header("MOTION", BOX_WIDTH))
+ add_wrapped(f"{motion_snippet.get('Category', '')} ({motion_snippet.get('Intensity Tier', '')})", "│ ")
+ add_wrapped(f"Trigger: {motion_snippet.get('Trigger', '')} | Duration: {motion_snippet.get('Duration', '')} | Easing: {motion_snippet.get('Easing', '')}")
for line in wrap_text(f"GSAP: {motion_snippet.get('GSAP Snippet', '')}", "│ ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "│")
if motion_snippet.get("Framework Notes"):
@@ -765,12 +791,12 @@ def format_ascii_box(design_system: dict) -> str:
# Anti-patterns section
if anti_patterns:
- lines.append(section_header("AVOID", BOX_WIDTH + 1))
+ lines.append(section_header("AVOID", BOX_WIDTH))
for line in wrap_text(anti_patterns, "│ ", BOX_WIDTH):
lines.append(line.ljust(BOX_WIDTH) + "│")
# Pre-Delivery Checklist section
- lines.append(section_header("PRE-DELIVERY CHECKLIST", BOX_WIDTH + 1))
+ lines.append(section_header("PRE-DELIVERY CHECKLIST", BOX_WIDTH))
checklist_items = [
"[ ] No emojis as icons (use SVG: Heroicons/Lucide)",
"[ ] cursor-pointer on all clickable elements",
@@ -830,8 +856,7 @@ def format_markdown(design_system: dict) -> str:
# Style section
lines.append("### Style")
lines.append(f"- **Name:** {style.get('name', '')}")
- light = style.get("light_mode", "")
- dark = style.get("dark_mode", "")
+ light, dark = _mode_support_labels(style, anti_patterns)
if light or dark:
lines.append(f"- **Mode Support:** Light {light} | Dark {dark}")
if style.get('keywords'):
@@ -1194,7 +1219,7 @@ def format_master_md(design_system: dict) -> str:
lines.append("/* Primary Button */")
lines.append(".btn-primary {")
lines.append(f" background: {colors.get('cta', '#F97316')};")
- lines.append(" color: white;")
+ lines.append(f" color: {colors.get('on_cta') or 'white'};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
lines.append(" font-weight: 600;")
@@ -1210,7 +1235,7 @@ def format_master_md(design_system: dict) -> str:
lines.append("/* Secondary Button */")
lines.append(".btn-secondary {")
lines.append(f" background: transparent;")
- lines.append(f" color: {colors.get('primary', '#2563EB')};")
+ lines.append(f" color: {_button_outline_text_color(colors)};")
lines.append(f" border: 2px solid {colors.get('primary', '#2563EB')};")
lines.append(" padding: 12px 24px;")
lines.append(" border-radius: 8px;")
diff --git a/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py b/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py
index abe615ba..44c80c1c 100644
--- a/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py
+++ b/plugins/codex/plugins/ui-ux-pro-max/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py
@@ -16,6 +16,7 @@ or directly:
python scripts/tests/test_design_system_mode.py
"""
+import re
import sys
import unittest
from pathlib import Path
@@ -26,13 +27,18 @@ sys.path.insert(0, str(SCRIPTS_DIR))
from design_system import ( # noqa: E402
_filter_anti_patterns_for_mode,
_contrast_ratio,
+ _mode_support_labels,
_palette_is_dark,
_query_wants_dark,
_relative_luminance,
_resolve_color_mode,
_select_palette_for_mode,
_style_is_dark_primary,
+ BOX_WIDTH,
DesignSystemGenerator,
+ format_ascii_box,
+ format_markdown,
+ format_master_md,
) # noqa: I001 - private helpers first, public class last
LIGHT_PALETTE = {"Product Type": "SaaS", "Background": "#F8FAFC", "Foreground": "#020617"}
@@ -185,5 +191,43 @@ class TestEndToEndCoherence(unittest.TestCase):
self.assertFalse(_palette_is_dark({"Background": ds["colors"]["background"]}))
+class TestLightResultOutputCoherence(unittest.TestCase):
+ """A light result that avoids dark mode must not also call dark "supported"."""
+
+ QUERY = "beauty spa wellness"
+
+ @classmethod
+ def setUpClass(cls):
+ cls.ds = DesignSystemGenerator().generate(cls.QUERY, "Serenity Spa")
+
+ def test_dark_support_is_qualified_when_the_product_avoids_dark_mode(self):
+ self.assertIn("dark mode", self.ds["anti_patterns"].lower())
+ light, dark = _mode_support_labels(self.ds["style"], self.ds["anti_patterns"])
+ self.assertIn("avoid for this product", dark)
+ self.assertIn("avoid for this product", format_ascii_box(self.ds))
+ self.assertIn("avoid for this product", format_markdown(self.ds))
+
+ def test_labels_unchanged_when_dark_mode_is_not_avoided(self):
+ style = {"light_mode": "supported", "dark_mode": "supported"}
+ self.assertEqual(_mode_support_labels(style, "Harsh animations"),
+ ("supported", "supported"))
+
+ def test_ascii_box_lines_never_overflow_the_border(self):
+ for query in (self.QUERY, "SaaS invoicing fintech B2B professional dark mode"):
+ with self.subTest(query=query):
+ ds = DesignSystemGenerator().generate(query, motion=7)
+ widths = {len(re.sub(r"\033\[[0-9;]*m", "", line))
+ for line in format_ascii_box(ds).splitlines()}
+ self.assertEqual(widths, {BOX_WIDTH + 1})
+
+ def test_master_button_text_meets_contrast_on_its_background(self):
+ colors = self.ds["colors"]
+ master = format_master_md(self.ds)
+ primary = re.search(r"\.btn-primary \{\n background: (#\w+);\n color: (#\w+);", master)
+ secondary = re.search(r"\.btn-secondary \{\n background: transparent;\n color: (#\w+);", master)
+ self.assertGreaterEqual(_contrast_ratio(primary.group(2), primary.group(1)), 4.5)
+ self.assertGreaterEqual(_contrast_ratio(secondary.group(1), colors["background"]), 4.5)
+
+
if __name__ == "__main__":
unittest.main(verbosity=2)