Sync third-party and MCP marketplace plugins

Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources.
Confidence: high
Scope-risk: narrow
Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history.
Tested: Marketplace validation passed.
This commit is contained in:
KeyInfo Bot
2026-07-08 00:01:24 +08:00
parent 0ac6e7e495
commit 37481a78ca
9 changed files with 230 additions and 59 deletions
+17 -17
View File
@@ -281,22 +281,22 @@
"category": "文档处理"
},
{
"name": "ppt-master",
"name": "ui-ux-pro-max",
"source": {
"source": "local",
"path": "./plugins/ppt-master"
"path": "./plugins/ui-ux-pro-max"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "文档处理"
"category": "设计"
},
{
"name": "ui-ux-pro-max",
"name": "taste-skill",
"source": {
"source": "local",
"path": "./plugins/ui-ux-pro-max"
"path": "./plugins/taste-skill"
},
"policy": {
"installation": "AVAILABLE",
@@ -316,6 +316,18 @@
},
"category": "开发工具"
},
{
"name": "ppt-master",
"source": {
"source": "local",
"path": "./plugins/ppt-master"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "文档处理"
},
{
"name": "next-skills",
"source": {
@@ -339,18 +351,6 @@
"authentication": "ON_INSTALL"
},
"category": "MCP"
},
{
"name": "taste-skill",
"source": {
"source": "local",
"path": "./plugins/taste-skill"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "设计"
}
]
}
@@ -3,5 +3,5 @@
"name": "playwright浏览器自动化操作",
"version": "20260605",
"keySource": "none",
"syncedAt": "2026-07-06T16:01:31Z"
"syncedAt": "2026-07-07T16:01:24Z"
}
@@ -2,8 +2,8 @@
"sourceId": "next-skills",
"repo": "https://github.com/vercel/next.js.git",
"ref": "canary",
"commit": "6065bf6fcfc48484634dc3708c080a27c5684e5d",
"commit": "8b5df172e4908b68f7ef88e8ad2b98070baf9ced",
"adapter": "skill-collection",
"sourcePath": "skills",
"syncedAt": "2026-07-06T16:00:00Z"
"syncedAt": "2026-07-07T16:00:00Z"
}
@@ -127,6 +127,47 @@ Knowing how to use Python and AI agents will matter more and more, and this proj
---
## You Might Also Like
### <a href="https://github.com/microsoft/ResearchStudio">ResearchStudio-<img src="https://raw.githubusercontent.com/ai-nuts/Storage/main/ResearchStudio/ResearchStudio-Reel/docs/figures/reel-wordmark.png" alt="Reel" height="16"></a>
> A Microsoft open-source project I recently joined — from **paper** to **talk video**, **poster**, and **blog**, automating the **last mile** of research dissemination.
>
> 📦 **Repo:** [microsoft/ResearchStudio](https://github.com/microsoft/ResearchStudio) · 📄 **Paper:** [arXiv:2607.04438](https://arxiv.org/abs/2607.04438)
<table align="center">
<tr>
<td align="center" valign="middle" width="53%">
<a href="https://aka.ms/ResearchStudio">
<img src="https://raw.githubusercontent.com/ai-nuts/Storage/main/ResearchStudio/ResearchStudio-Reel/docs/figures/reel_demo.gif" width="100%"
alt="ResearchStudio-Reel demo" />
</a>
</td>
<td align="center" valign="middle" width="47%">
<a href="https://aka.ms/ResearchStudio">
<img src="https://raw.githubusercontent.com/ai-nuts/Storage/main/ResearchStudio/ResearchStudio-Reel/docs/examples/latent_diffusion_landscape/poster.png" width="100%" alt="ResearchStudio-Reel generated poster" />
</a>
</td>
</tr>
</table>
<details>
<summary><strong>BibTeX</strong> — if you use ResearchStudio-Reel in your research</summary>
```bibtex
@article{xiao2026researchstudioreel,
title = {ResearchStudio-Reel: Automate the Last Mile of Research from Paper to Poster, Video, and Blog},
author = {Lingao Xiao and Yalun Dai and Yangyu Huang and Qihao Zhao and Wenshan Wu and Hugo He and Ruishuo Chen and Jin Jiang and Qianli Ma and Jiahuan Zhang and Xin Zhang and Ying Xin and Yang Ou and Yan Xia and Scarlett Li and Longbo Huang and Zhipeng Zhang and Yang He and Yap Kim Hui and Yan Lu},
journal = {arXiv preprint arXiv:2607.04438},
year = {2026},
url = {https://arxiv.org/abs/2607.04438}
}
```
</details>
---
## Quick Start
### 1. Prerequisites
@@ -2,8 +2,8 @@
"sourceId": "ppt-master",
"repo": "https://github.com/hugohe3/ppt-master.git",
"ref": "main",
"commit": "eb9243be673d0019dc1c7998262214a9a977a7d4",
"commit": "0b3f73e5f0868171eef82baef44be660888f6c71",
"adapter": "claude-skill",
"sourcePath": "skills/ppt-master",
"syncedAt": "2026-07-05T16:00:01Z"
"syncedAt": "2026-07-07T16:00:00Z"
}
@@ -13,10 +13,12 @@ from resource_paths import icon_search_dirs_for_svg
from .context import ConvertContext, ShapeResult
from .utils import (
SVG_NS, EMU_PER_PX,
_extract_inheritable_styles, parse_transform_matrix, resolve_url_id,
_extract_inheritable_styles, _f, _get_attr, parse_transform_matrix, resolve_url_id,
parse_svg_length,
)
from .styles import build_effect_xml
from .styles import (
build_effect_xml, build_fill_xml, get_fill_opacity, get_stroke_opacity,
)
from .elements import (
convert_rect, convert_circle, convert_ellipse,
convert_line, convert_path,
@@ -361,6 +363,118 @@ _CONVERTERS = {
_SUPPORTED_VISUAL_CHILD_TAGS = frozenset(('tspan',))
def _parse_svg_canvas(root: ET.Element) -> tuple[float, float, float, float]:
"""Return the SVG canvas as (x, y, width, height) in SVG units."""
view_box = root.get('viewBox', '')
parts = re.split(r'[\s,]+', view_box.strip()) if view_box else []
if len(parts) == 4:
try:
x, y, w, h = (float(part) for part in parts)
if w > 0 and h > 0:
return x, y, w, h
except ValueError:
pass
return 0.0, 0.0, _f(root.get('width')), _f(root.get('height'))
def _is_full_canvas_rect(
elem: ET.Element,
ctx: ConvertContext,
canvas: tuple[float, float, float, float],
) -> bool:
"""Return whether a rect is a safe candidate for native slide background."""
if elem.get('transform') or elem.get('filter') or elem.get('clip-path'):
return False
if _f(elem.get('rx')) > 0 or _f(elem.get('ry')) > 0:
return False
canvas_x, canvas_y, canvas_w, canvas_h = canvas
if canvas_w <= 0 or canvas_h <= 0:
return False
tolerance = 0.5
if abs(_f(elem.get('x')) - canvas_x) > tolerance:
return False
if abs(_f(elem.get('y')) - canvas_y) > tolerance:
return False
if abs(_f(elem.get('width')) - canvas_w) > tolerance:
return False
if abs(_f(elem.get('height')) - canvas_h) > tolerance:
return False
fill = _get_attr(elem, 'fill', ctx)
if fill == 'none':
return False
stroke = _get_attr(elem, 'stroke', ctx)
stroke_width = _f(_get_attr(elem, 'stroke-width', ctx), 1.0)
stroke_opacity = get_stroke_opacity(elem, ctx)
if stroke and stroke != 'none' and stroke_width > 0 and stroke_opacity != 0:
return False
return True
def _background_xml_from_rect(
elem: ET.Element,
ctx: ConvertContext,
) -> str:
"""Build native ``p:bg`` XML from a full-slide SVG background rect."""
fill_xml = build_fill_xml(elem, ctx, get_fill_opacity(elem, ctx))
if not fill_xml or '<a:noFill' in fill_xml:
return ''
return f'<p:bg><p:bgPr>{fill_xml}<a:effectLst/></p:bgPr></p:bg>'
def _extract_background_candidate(
root: ET.Element,
ctx: ConvertContext,
) -> tuple[str, int | None]:
"""Promote a first-layer SVG background rect to native PowerPoint bgPr.
PowerPoint stores page background fills under ``p:cSld/p:bg/p:bgPr``.
Keeping the full-canvas SVG rect in ``p:spTree`` makes it an ordinary
selectable shape, so users hit it during bulk element selection. Only the
first visual layer is considered, matching pptx_to_svg's round-trip output
and avoiding accidental promotion of content panels.
"""
canvas = _parse_svg_canvas(root)
for child in root:
tag = child.tag.replace(f'{{{SVG_NS}}}', '')
if tag in _NON_VISUAL_TAGS:
continue
if tag == 'rect' and _is_full_canvas_rect(child, ctx, canvas):
bg_xml = _background_xml_from_rect(child, ctx)
if bg_xml:
return bg_xml, id(child)
return '', None
if tag != 'g':
return '', None
if child.get('transform') or child.get('filter') or child.get('clip-path'):
return '', None
style_overrides = _extract_inheritable_styles(child)
child_ctx = ctx.child(style_overrides=style_overrides)
visual_children = [
grandchild for grandchild in child
if grandchild.tag.replace(f'{{{SVG_NS}}}', '') not in _NON_VISUAL_TAGS
]
if len(visual_children) != 1:
return '', None
only_child = visual_children[0]
only_tag = only_child.tag.replace(f'{{{SVG_NS}}}', '')
if only_tag == 'rect' and _is_full_canvas_rect(only_child, child_ctx, canvas):
bg_xml = _background_xml_from_rect(only_child, child_ctx)
if bg_xml:
ctx.sync_from_child(child_ctx)
return bg_xml, id(child)
return '', None
return '', None
return '', None
def collect_defs(root: ET.Element) -> dict[str, ET.Element]:
"""Collect all <defs> children into an {id: element} dictionary."""
defs: dict[str, ET.Element] = {}
@@ -568,6 +682,14 @@ def convert_svg_to_slide_shapes(
shapes: list[str] = []
converted = 0
skipped = 0
background_xml, background_skip_id = _extract_background_candidate(root, ctx)
promoted_backgrounds = 1 if background_xml else 0
if background_xml and trace_events is not None:
trace_events.append({
'tag': 'rect',
'decision': 'native-background',
'reason': 'promoted-full-canvas-rect-to-bgPr',
})
# Per-element shape ids of every top-level child, used as an animation
# fallback when no <g id="..."> groups are present at the root.
fallback_targets: list = []
@@ -576,6 +698,8 @@ def convert_svg_to_slide_shapes(
tag = child.tag.replace(f'{{{SVG_NS}}}', '')
if tag == 'defs':
continue
if id(child) == background_skip_id:
continue
result = convert_element(child, ctx)
if result:
shapes.append(result.xml)
@@ -598,7 +722,11 @@ def convert_svg_to_slide_shapes(
ctx.anim_targets = fallback_targets
if verbose:
print(f' Converted {converted} elements, skipped {skipped}')
promoted = (
f', promoted {promoted_backgrounds} background'
if promoted_backgrounds else ''
)
print(f' Converted {converted} elements, skipped {skipped}{promoted}')
if trace_out is not None:
trace_out.append({
@@ -607,6 +735,7 @@ def convert_svg_to_slide_shapes(
'summary': {
'converted': converted,
'skipped': skipped,
'promoted_backgrounds': promoted_backgrounds,
'media_files': len(ctx.media_files),
'package_files': len(ctx.package_files),
'relationships': len(ctx.rel_entries),
@@ -623,6 +752,7 @@ def convert_svg_to_slide_shapes(
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
<p:cSld>
{background_xml}
<p:spTree>
<p:nvGrpSpPr>
<p:cNvPr id="1" name=""/>
@@ -2,8 +2,8 @@
"sourceId": "shadcn",
"repo": "https://github.com/shadcn-ui/ui.git",
"ref": "main",
"commit": "d8ace420baa5c8a1abccd75e52570f2a232f193d",
"commit": "68e1f171aa8c6ce2e38cb9a7d1463d6bcf357e91",
"adapter": "claude-skill",
"sourcePath": "skills/shadcn",
"syncedAt": "2026-07-06T16:00:00Z"
"syncedAt": "2026-07-07T16:00:00Z"
}