@clawos-dev/clawd 0.2.146 → 0.2.147-beta.312.20be87a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,347 @@
1
+ # HTML Presentation Template
2
+
3
+ Reference architecture for generating slide presentations. Every presentation follows this structure.
4
+
5
+ ## Base HTML Structure
6
+
7
+ ```html
8
+ <!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>Presentation Title</title>
14
+
15
+ <!-- Fonts: use Fontshare or Google Fonts — never system fonts -->
16
+ <link rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">
17
+
18
+ <style>
19
+ /* ===========================================
20
+ CSS CUSTOM PROPERTIES (THEME)
21
+ Change these to change the whole look
22
+ =========================================== */
23
+ :root {
24
+ /* Colors — from chosen style preset */
25
+ --bg-primary: #0a0f1c;
26
+ --bg-secondary: #111827;
27
+ --text-primary: #ffffff;
28
+ --text-secondary: #9ca3af;
29
+ --accent: #00ffcc;
30
+ --accent-glow: rgba(0, 255, 204, 0.3);
31
+
32
+ /* Typography — MUST use clamp() */
33
+ --font-display: 'Clash Display', sans-serif;
34
+ --font-body: 'Satoshi', sans-serif;
35
+ --title-size: clamp(2rem, 6vw, 5rem);
36
+ --subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
37
+ --body-size: clamp(0.75rem, 1.2vw, 1rem);
38
+
39
+ /* Spacing — MUST use clamp() */
40
+ --slide-padding: clamp(1.5rem, 4vw, 4rem);
41
+ --content-gap: clamp(1rem, 2vw, 2rem);
42
+
43
+ /* Animation */
44
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
45
+ --duration-normal: 0.6s;
46
+ }
47
+
48
+ /* ===========================================
49
+ BASE STYLES
50
+ =========================================== */
51
+ * { margin: 0; padding: 0; box-sizing: border-box; }
52
+
53
+ /* --- PASTE viewport-base.css CONTENTS HERE --- */
54
+
55
+ /* ===========================================
56
+ ANIMATIONS
57
+ Trigger via .visible class (added by JS on scroll)
58
+ =========================================== */
59
+ .reveal {
60
+ opacity: 0;
61
+ transform: translateY(30px);
62
+ transition: opacity var(--duration-normal) var(--ease-out-expo),
63
+ transform var(--duration-normal) var(--ease-out-expo);
64
+ }
65
+
66
+ .slide.visible .reveal {
67
+ opacity: 1;
68
+ transform: translateY(0);
69
+ }
70
+
71
+ /* Stagger children for sequential reveal */
72
+ .reveal:nth-child(1) { transition-delay: 0.1s; }
73
+ .reveal:nth-child(2) { transition-delay: 0.2s; }
74
+ .reveal:nth-child(3) { transition-delay: 0.3s; }
75
+ .reveal:nth-child(4) { transition-delay: 0.4s; }
76
+
77
+ /* ... preset-specific styles ... */
78
+ </style>
79
+ </head>
80
+ <body>
81
+ <!-- Optional: Progress bar -->
82
+ <div class="progress-bar"></div>
83
+
84
+ <!-- Optional: Navigation dots -->
85
+ <nav class="nav-dots"><!-- Generated by JS --></nav>
86
+
87
+ <!-- Slides -->
88
+ <section class="slide title-slide">
89
+ <h1 class="reveal">Presentation Title</h1>
90
+ <p class="reveal">Subtitle or author</p>
91
+ </section>
92
+
93
+ <section class="slide">
94
+ <div class="slide-content">
95
+ <h2 class="reveal">Slide Title</h2>
96
+ <p class="reveal">Content...</p>
97
+ </div>
98
+ </section>
99
+
100
+ <!-- More slides... -->
101
+
102
+ <script>
103
+ /* ===========================================
104
+ SLIDE PRESENTATION CONTROLLER
105
+ =========================================== */
106
+ class SlidePresentation {
107
+ constructor() {
108
+ this.slides = document.querySelectorAll('.slide');
109
+ this.currentSlide = 0;
110
+ this.setupIntersectionObserver();
111
+ this.setupKeyboardNav();
112
+ this.setupTouchNav();
113
+ this.setupProgressBar();
114
+ this.setupNavDots();
115
+ }
116
+
117
+ setupIntersectionObserver() {
118
+ // Add .visible class when slides enter viewport
119
+ // Triggers CSS animations efficiently
120
+ }
121
+
122
+ setupKeyboardNav() {
123
+ // Arrow keys, Space, Page Up/Down
124
+ }
125
+
126
+ setupTouchNav() {
127
+ // Touch/swipe support for mobile
128
+ }
129
+
130
+ setupProgressBar() {
131
+ // Update progress bar on scroll
132
+ }
133
+
134
+ setupNavDots() {
135
+ // Generate and manage navigation dots
136
+ }
137
+ }
138
+
139
+ new SlidePresentation();
140
+ </script>
141
+ </body>
142
+ </html>
143
+ ```
144
+
145
+ ## Required JavaScript Features
146
+
147
+ Every presentation must include:
148
+
149
+ 1. **SlidePresentation Class** — Main controller with:
150
+ - Keyboard navigation (arrows, space, page up/down)
151
+ - Touch/swipe support
152
+ - Mouse wheel navigation
153
+ - Progress bar updates
154
+ - Navigation dots
155
+
156
+ 2. **Intersection Observer** — For scroll-triggered animations:
157
+ - Add `.visible` class when slides enter viewport
158
+ - Trigger CSS transitions efficiently
159
+
160
+ 3. **Optional Enhancements** (match to chosen style):
161
+ - Custom cursor with trail
162
+ - Particle system background (canvas)
163
+ - Parallax effects
164
+ - 3D tilt on hover
165
+ - Magnetic buttons
166
+ - Counter animations
167
+
168
+ 4. **Inline Editing** (only if user opted in during Phase 1 — skip entirely if they said No):
169
+ - Edit toggle button (hidden by default, revealed via hover hotzone or `E` key)
170
+ - Auto-save to localStorage
171
+ - Export/save file functionality
172
+ - See "Inline Editing Implementation" section below
173
+
174
+ ## Inline Editing Implementation (Opt-In Only)
175
+
176
+ **If the user chose "No" for inline editing in Phase 1, do NOT generate any edit-related HTML, CSS, or JS.**
177
+
178
+ **Do NOT use CSS `~` sibling selector for hover-based show/hide.** The CSS-only approach (`edit-hotzone:hover ~ .edit-toggle`) fails because `pointer-events: none` on the toggle button breaks the hover chain: user hovers hotzone -> button becomes visible -> mouse moves toward button -> leaves hotzone -> button disappears before click.
179
+
180
+ **Required approach: JS-based hover with 400ms delay timeout.**
181
+
182
+ HTML:
183
+ ```html
184
+ <div class="edit-hotzone"></div>
185
+ <button class="edit-toggle" id="editToggle" title="Edit mode (E)">✏️</button>
186
+ ```
187
+
188
+ CSS (visibility controlled by JS classes only):
189
+ ```css
190
+ /* Do NOT use CSS ~ sibling selector for this!
191
+ pointer-events: none breaks the hover chain.
192
+ Must use JS with delay timeout. */
193
+ .edit-hotzone {
194
+ position: fixed; top: 0; left: 0;
195
+ width: 80px; height: 80px;
196
+ z-index: 10000;
197
+ cursor: pointer;
198
+ }
199
+ .edit-toggle {
200
+ opacity: 0;
201
+ pointer-events: none;
202
+ transition: opacity 0.3s ease;
203
+ z-index: 10001;
204
+ }
205
+ .edit-toggle.show,
206
+ .edit-toggle.active {
207
+ opacity: 1;
208
+ pointer-events: auto;
209
+ }
210
+ ```
211
+
212
+ JS (three interaction methods):
213
+ ```javascript
214
+ // 1. Click handler on the toggle button
215
+ document.getElementById('editToggle').addEventListener('click', () => {
216
+ editor.toggleEditMode();
217
+ });
218
+
219
+ // 2. Hotzone hover with 400ms grace period
220
+ const hotzone = document.querySelector('.edit-hotzone');
221
+ const editToggle = document.getElementById('editToggle');
222
+ let hideTimeout = null;
223
+
224
+ hotzone.addEventListener('mouseenter', () => {
225
+ clearTimeout(hideTimeout);
226
+ editToggle.classList.add('show');
227
+ });
228
+ hotzone.addEventListener('mouseleave', () => {
229
+ hideTimeout = setTimeout(() => {
230
+ if (!editor.isActive) editToggle.classList.remove('show');
231
+ }, 400);
232
+ });
233
+ editToggle.addEventListener('mouseenter', () => {
234
+ clearTimeout(hideTimeout);
235
+ });
236
+ editToggle.addEventListener('mouseleave', () => {
237
+ hideTimeout = setTimeout(() => {
238
+ if (!editor.isActive) editToggle.classList.remove('show');
239
+ }, 400);
240
+ });
241
+
242
+ // 3. Hotzone direct click
243
+ hotzone.addEventListener('click', () => {
244
+ editor.toggleEditMode();
245
+ });
246
+
247
+ // 4. Keyboard shortcut (E key, skip when editing text)
248
+ document.addEventListener('keydown', (e) => {
249
+ if ((e.key === 'e' || e.key === 'E') && !e.target.getAttribute('contenteditable')) {
250
+ editor.toggleEditMode();
251
+ }
252
+ });
253
+ ```
254
+
255
+ ## Image Pipeline (Skip If No Images)
256
+
257
+ If user chose "No images" in Phase 1, skip this entirely. If images were provided, process them before generating HTML.
258
+
259
+ **Dependency:** `pip install Pillow`
260
+
261
+ ### Image Processing
262
+
263
+ ```python
264
+ from PIL import Image, ImageDraw
265
+
266
+ # Circular crop (for logos on modern/clean styles)
267
+ def crop_circle(input_path, output_path):
268
+ img = Image.open(input_path).convert('RGBA')
269
+ w, h = img.size
270
+ size = min(w, h)
271
+ left, top = (w - size) // 2, (h - size) // 2
272
+ img = img.crop((left, top, left + size, top + size))
273
+ mask = Image.new('L', (size, size), 0)
274
+ ImageDraw.Draw(mask).ellipse([0, 0, size, size], fill=255)
275
+ img.putalpha(mask)
276
+ img.save(output_path, 'PNG')
277
+
278
+ # Resize (for oversized images that inflate HTML)
279
+ def resize_max(input_path, output_path, max_dim=1200):
280
+ img = Image.open(input_path)
281
+ img.thumbnail((max_dim, max_dim), Image.LANCZOS)
282
+ img.save(output_path, quality=85)
283
+ ```
284
+
285
+ | Situation | Operation |
286
+ |-----------|-----------|
287
+ | Square logo on rounded aesthetic | `crop_circle()` |
288
+ | Image > 1MB | `resize_max(max_dim=1200)` |
289
+ | Wrong aspect ratio | Manual crop with `img.crop()` |
290
+
291
+ Save processed images with `_processed` suffix. Never overwrite originals.
292
+
293
+ ### Image Placement
294
+
295
+ **Use direct file paths** (not base64) — presentations are viewed locally:
296
+
297
+ ```html
298
+ <img src="assets/logo_round.png" alt="Logo" class="slide-image logo">
299
+ <img src="assets/screenshot.png" alt="Screenshot" class="slide-image screenshot">
300
+ ```
301
+
302
+ ```css
303
+ .slide-image {
304
+ max-width: 100%;
305
+ max-height: min(50vh, 400px);
306
+ object-fit: contain;
307
+ border-radius: 8px;
308
+ }
309
+ .slide-image.screenshot {
310
+ border: 1px solid rgba(255, 255, 255, 0.1);
311
+ border-radius: 12px;
312
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
313
+ }
314
+ .slide-image.logo {
315
+ max-height: min(30vh, 200px);
316
+ }
317
+ ```
318
+
319
+ **Adapt border/shadow colors to match the chosen style's accent.** Never repeat the same image on multiple slides (except logos on title + closing).
320
+
321
+ **Placement patterns:** Logo centered on title slide. Screenshots in two-column layouts with text. Full-bleed images as slide backgrounds with text overlay (use sparingly).
322
+
323
+ ---
324
+
325
+ ## Code Quality
326
+
327
+ **Comments:** Every section needs clear comments explaining what it does and how to modify it.
328
+
329
+ **Accessibility:**
330
+ - Semantic HTML (`<section>`, `<nav>`, `<main>`)
331
+ - Keyboard navigation works fully
332
+ - ARIA labels where needed
333
+ - `prefers-reduced-motion` support (included in viewport-base.css)
334
+
335
+ ## File Structure
336
+
337
+ Single presentations:
338
+ ```
339
+ presentation.html # Self-contained, all CSS/JS inline
340
+ assets/ # Images only, if any
341
+ ```
342
+
343
+ Multiple presentations in one project:
344
+ ```
345
+ [name].html
346
+ [name]-assets/
347
+ ```
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Extract all content from a PowerPoint file (.pptx).
4
+ Returns a JSON structure with slides, text, and images.
5
+
6
+ Usage:
7
+ python extract-pptx.py <input.pptx> [output_dir]
8
+
9
+ Requires: pip install python-pptx
10
+ """
11
+
12
+ import json
13
+ import os
14
+ import sys
15
+ from pptx import Presentation
16
+
17
+
18
+ def extract_pptx(file_path, output_dir="."):
19
+ """
20
+ Extract all content from a PowerPoint file.
21
+ Returns a list of slide data dicts with text, images, and notes.
22
+ """
23
+ prs = Presentation(file_path)
24
+ slides_data = []
25
+
26
+ # Create assets directory for extracted images
27
+ assets_dir = os.path.join(output_dir, "assets")
28
+ os.makedirs(assets_dir, exist_ok=True)
29
+
30
+ for slide_num, slide in enumerate(prs.slides):
31
+ slide_data = {
32
+ "number": slide_num + 1,
33
+ "title": "",
34
+ "content": [],
35
+ "images": [],
36
+ "notes": "",
37
+ }
38
+
39
+ for shape in slide.shapes:
40
+ # Extract text content
41
+ if shape.has_text_frame:
42
+ if shape == slide.shapes.title:
43
+ slide_data["title"] = shape.text
44
+ else:
45
+ slide_data["content"].append(
46
+ {"type": "text", "content": shape.text}
47
+ )
48
+
49
+ # Extract images
50
+ if shape.shape_type == 13: # Picture type
51
+ image = shape.image
52
+ image_bytes = image.blob
53
+ image_ext = image.ext
54
+ image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
55
+ image_path = os.path.join(assets_dir, image_name)
56
+
57
+ with open(image_path, "wb") as f:
58
+ f.write(image_bytes)
59
+
60
+ slide_data["images"].append(
61
+ {
62
+ "path": f"assets/{image_name}",
63
+ "width": shape.width,
64
+ "height": shape.height,
65
+ }
66
+ )
67
+
68
+ # Extract speaker notes
69
+ if slide.has_notes_slide:
70
+ notes_frame = slide.notes_slide.notes_text_frame
71
+ slide_data["notes"] = notes_frame.text
72
+
73
+ slides_data.append(slide_data)
74
+
75
+ return slides_data
76
+
77
+
78
+ if __name__ == "__main__":
79
+ if len(sys.argv) < 2:
80
+ print("Usage: python extract-pptx.py <input.pptx> [output_dir]")
81
+ sys.exit(1)
82
+
83
+ input_file = sys.argv[1]
84
+ output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
85
+
86
+ slides = extract_pptx(input_file, output_dir)
87
+
88
+ # Write extracted data as JSON
89
+ output_path = os.path.join(output_dir, "extracted-slides.json")
90
+ with open(output_path, "w") as f:
91
+ json.dump(slides, f, indent=2)
92
+
93
+ print(f"Extracted {len(slides)} slides to {output_path}")
94
+ for s in slides:
95
+ img_count = len(s["images"])
96
+ print(f" Slide {s['number']}: {s['title'] or '(no title)'} — {img_count} image(s)")
@@ -0,0 +1,153 @@
1
+ /* ===========================================
2
+ VIEWPORT FITTING: MANDATORY BASE STYLES
3
+ Include this ENTIRE file in every presentation.
4
+ These styles ensure slides fit exactly in the viewport.
5
+ =========================================== */
6
+
7
+ /* 1. Lock html/body to viewport */
8
+ html, body {
9
+ height: 100%;
10
+ overflow-x: hidden;
11
+ }
12
+
13
+ html {
14
+ scroll-snap-type: y mandatory;
15
+ scroll-behavior: smooth;
16
+ }
17
+
18
+ /* 2. Each slide = exact viewport height */
19
+ .slide {
20
+ width: 100vw;
21
+ height: 100vh;
22
+ height: 100dvh; /* Dynamic viewport height for mobile browsers */
23
+ overflow: hidden; /* CRITICAL: Prevent ANY overflow */
24
+ scroll-snap-align: start;
25
+ display: flex;
26
+ flex-direction: column;
27
+ position: relative;
28
+ }
29
+
30
+ /* 3. Content container with flex for centering */
31
+ .slide-content {
32
+ flex: 1;
33
+ display: flex;
34
+ flex-direction: column;
35
+ justify-content: center;
36
+ max-height: 100%;
37
+ overflow: hidden; /* Double-protection against overflow */
38
+ padding: var(--slide-padding);
39
+ }
40
+
41
+ /* 4. ALL typography uses clamp() for responsive scaling */
42
+ :root {
43
+ /* Titles scale from mobile to desktop */
44
+ --title-size: clamp(1.5rem, 5vw, 4rem);
45
+ --h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
46
+ --h3-size: clamp(1rem, 2.5vw, 1.75rem);
47
+
48
+ /* Body text */
49
+ --body-size: clamp(0.75rem, 1.5vw, 1.125rem);
50
+ --small-size: clamp(0.65rem, 1vw, 0.875rem);
51
+
52
+ /* Spacing scales with viewport */
53
+ --slide-padding: clamp(1rem, 4vw, 4rem);
54
+ --content-gap: clamp(0.5rem, 2vw, 2rem);
55
+ --element-gap: clamp(0.25rem, 1vw, 1rem);
56
+ }
57
+
58
+ /* 5. Cards/containers use viewport-relative max sizes */
59
+ .card, .container, .content-box {
60
+ max-width: min(90vw, 1000px);
61
+ max-height: min(80vh, 700px);
62
+ }
63
+
64
+ /* 6. Lists auto-scale with viewport */
65
+ .feature-list, .bullet-list {
66
+ gap: clamp(0.4rem, 1vh, 1rem);
67
+ }
68
+
69
+ .feature-list li, .bullet-list li {
70
+ font-size: var(--body-size);
71
+ line-height: 1.4;
72
+ }
73
+
74
+ /* 7. Grids adapt to available space */
75
+ .grid {
76
+ display: grid;
77
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
78
+ gap: clamp(0.5rem, 1.5vw, 1rem);
79
+ }
80
+
81
+ /* 8. Images constrained to viewport */
82
+ img, .image-container {
83
+ max-width: 100%;
84
+ max-height: min(50vh, 400px);
85
+ object-fit: contain;
86
+ }
87
+
88
+ /* ===========================================
89
+ RESPONSIVE BREAKPOINTS
90
+ Aggressive scaling for smaller viewports
91
+ =========================================== */
92
+
93
+ /* Short viewports (< 700px height) */
94
+ @media (max-height: 700px) {
95
+ :root {
96
+ --slide-padding: clamp(0.75rem, 3vw, 2rem);
97
+ --content-gap: clamp(0.4rem, 1.5vw, 1rem);
98
+ --title-size: clamp(1.25rem, 4.5vw, 2.5rem);
99
+ --h2-size: clamp(1rem, 3vw, 1.75rem);
100
+ }
101
+ }
102
+
103
+ /* Very short viewports (< 600px height) */
104
+ @media (max-height: 600px) {
105
+ :root {
106
+ --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
107
+ --content-gap: clamp(0.3rem, 1vw, 0.75rem);
108
+ --title-size: clamp(1.1rem, 4vw, 2rem);
109
+ --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
110
+ }
111
+
112
+ /* Hide non-essential elements */
113
+ .nav-dots, .keyboard-hint, .decorative {
114
+ display: none;
115
+ }
116
+ }
117
+
118
+ /* Extremely short (landscape phones, < 500px height) */
119
+ @media (max-height: 500px) {
120
+ :root {
121
+ --slide-padding: clamp(0.4rem, 2vw, 1rem);
122
+ --title-size: clamp(1rem, 3.5vw, 1.5rem);
123
+ --h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
124
+ --body-size: clamp(0.65rem, 1vw, 0.85rem);
125
+ }
126
+ }
127
+
128
+ /* Narrow viewports (< 600px width) */
129
+ @media (max-width: 600px) {
130
+ :root {
131
+ --title-size: clamp(1.25rem, 7vw, 2.5rem);
132
+ }
133
+
134
+ /* Stack grids vertically */
135
+ .grid {
136
+ grid-template-columns: 1fr;
137
+ }
138
+ }
139
+
140
+ /* ===========================================
141
+ REDUCED MOTION
142
+ Respect user preferences
143
+ =========================================== */
144
+ @media (prefers-reduced-motion: reduce) {
145
+ *, *::before, *::after {
146
+ animation-duration: 0.01ms !important;
147
+ transition-duration: 0.2s !important;
148
+ }
149
+
150
+ html {
151
+ scroll-behavior: auto;
152
+ }
153
+ }
@@ -0,0 +1,77 @@
1
+ 你是老板的「HTML PPT 制作师」—— clawd 体系下的一个 persona,专门把老板的想法 / 文字 / 旧 PPT 变成单文件 HTML 演示稿。
2
+
3
+ # 何时找你
4
+
5
+ - 老板说"做个 PPT / 演示稿 / slides / 幻灯片"
6
+ - 老板要把一段内容(提纲、文章、笔记)变成可演讲的页面
7
+ - 老板把现有 .pptx 扔给你要"转成网页版"
8
+ - 老板要做主题分享、产品介绍、教学讲义的可视化
9
+
10
+ 不在这个 persona 干的事:写普通文档(找 share-html 或 notes)、写代码项目(找 developer)。
11
+
12
+ # 你的核心能力来源
13
+
14
+ `frontend-slides` skill 是你的主要工作方式。**每次接到 PPT 任务时,先 invoke 这个 skill**,照它的规范干:
15
+
16
+ - 单文件 HTML,零依赖,内联 CSS/JS
17
+ - 固定 1920×1080 的 16:9 stage,整体缩放适配视口
18
+ - 设计上避免"AI slop"——字体、配色、动效都要有辨识度
19
+ - 区分 reading deck(信息密度高,给读者看的)和 speaker deck(信息密度低,给现场讲的),开工前先问老板
20
+
21
+ skill 自带 `viewport-base.css` / `bold-template-pack` / 动效模式库等资源,按它的 SKILL.md 走流程即可,不要自己绕开它另起炉灶。
22
+
23
+ # 🩺 `frontend-slides` skill 不在就自己装一份
24
+
25
+ 接到 PPT 任务,直接 invoke `frontend-slides` skill 干活。如果会话开场的可用 skill 列表里**没有它**,或者调用报「找不到这个 skill」——别绕过它硬写 HTML,自己安装:
26
+
27
+ 1. **上游源**:`https://github.com/zarazhangrui/frontend-slides`(MIT 协议公开仓库,作者 Zara Zhang)
28
+ 2. **安装位置**:**当前 persona project 目录**的 `.claude/skills/frontend-slides/` 下(从 cwd / 开场环境信息里推断这个目录在哪,不要写死路径,也**不要**装到全局 `~/.claude/skills/`)
29
+ 3. **怎么装**:
30
+ ```bash
31
+ git clone https://github.com/zarazhangrui/frontend-slides.git <persona-project>/.claude/skills/frontend-slides
32
+ ```
33
+ 网络不通 / git 不可用 → 停下告诉老板,不要瞎搞
34
+ 4. **装完告诉老板**:「skill 已自动补齐,下次会话生效」——本轮会话的 skill 列表是开场锁定的,装完本轮**调不起来**,按 `SKILL.md` 内容照流程手写也行,但要跟老板说清楚情况
35
+
36
+ # 工作方式
37
+
38
+ ## 开工前必问的两件事(缺一个就问一个,缺两个一起问)
39
+
40
+ 1. **deck 类型**:reading(自己读的,文字多)还是 speaking(现场讲的,文字少、视觉重)?—— 这决定了信息密度和版式风格
41
+ 2. **主题/风格倾向**:商业稳重 / 极简文艺 / 科技未来 / 学术严肃 / 活泼可爱…… 老板给不出方向时,你可以基于内容主题**直接选一个最合适的**先做,让老板看完再调
42
+
43
+ 老板只给一段内容、没说要几页的话:你自己判断结构(开场 + 主要观点 + 收尾),别问"您想要几页"——这是你的活。
44
+
45
+ ## 干的过程中
46
+
47
+ - 先按 skill 的 progressive disclosure 读 STYLE_PRESETS、再决定走 bold-template-pack 还是从头写
48
+ - 内容是老板给的就用老板的原话,不要"为了 PPT 好看"擅自重写老板的论点
49
+ - 图片、字体优先 inline / data URI / CDN(不依赖本地资源,老板拿走 HTML 就能用)
50
+ - 动效用 CSS-only,照 skill 的 `animation-patterns.md`
51
+
52
+ ## 干完后
53
+
54
+ 报告格式:
55
+
56
+ ```
57
+ ✅ PPT 已生成:
58
+ <产物绝对路径>/index.html
59
+
60
+ 类型:<reading | speaking>
61
+ 页数:<n>
62
+ 风格:<一句话风格描述>
63
+
64
+ 双击打开即可演讲;要改的话告诉我改哪页/哪部分。
65
+ ```
66
+
67
+ # 红线
68
+
69
+ - **不绕过 frontend-slides skill** —— 不要"我自己写个 HTML 模板更快",skill 的规范(16:9 stage、viewport-base.css、风格规避 slop)是产物质量的保底
70
+ - **不自动重写老板的文案** —— 排版可以你定,文字内容尊重老板原话;要改先问
71
+ - **不依赖外部构建工具** —— 单 HTML 文件,零 npm/webpack/vite,跟 skill 原则一致
72
+
73
+ # 行为规范
74
+
75
+ - 老板把内容扔过来就开干,别先要"详细需求文档"
76
+ - 写完一稿先给老板看效果,他要改再改,不要一上来就追问 17 个细节
77
+ - 老板说"再花哨点 / 再简洁点 / 换个颜色"这种粗调,你自己拿主意调,不要反过来问"具体什么颜色 RGB 是多少"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.146",
3
+ "version": "0.2.147-beta.312.20be87a",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",