Skip to content

Commit da1973f

Browse files
committed
✨ Add PPTX generation tool + Enhance session execution for follow-up turns + Update settings for PPTX theme + Localize new settings + Refactor skill script execution
1 parent 183821c commit da1973f

63 files changed

Lines changed: 2986 additions & 296 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/components/ArtifactPreviewCard.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const WIDGET_ICONS: Record<CodeBlockType, IconName> = {
9090
svg: 'MediaImage',
9191
diagram: 'CubeScan',
9292
marpit: 'Presentation',
93+
pptx: 'Presentation',
9394
html: 'Html5',
9495
generic: 'Code',
9596
}
@@ -99,6 +100,7 @@ const WIDGET_LABELS: Record<CodeBlockType, string> = {
99100
svg: 'SVG',
100101
diagram: 'Diagram',
101102
marpit: 'Presentation',
103+
pptx: 'PowerPoint',
102104
html: 'HTML',
103105
generic: 'Code',
104106
}

src/components/InspectorPanel.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,6 @@ const ArtifactBody = memo(
195195
className="prose dark:prose-invert prose-sm max-w-none"
196196
/>
197197
</div>
198-
199-
<div className="flex justify-between text-tiny text-default-400">
200-
<span>
201-
Created: {new Date(artifact.createdAt).toLocaleDateString()}
202-
</span>
203-
<span>{artifact.content.length.toLocaleString()} chars</span>
204-
</div>
205198
</div>
206199
)
207200
},

src/components/PptxThemePicker.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* PptxThemePicker — visual grid for selecting the PPTX presentation theme.
3+
*
4+
* Shows an "Auto" option that inherits the current color theme,
5+
* plus all platform color themes as explicit overrides.
6+
*/
7+
8+
import { PPTX_THEMES, PPTX_THEME_AUTO } from '@/lib/pptx-themes'
9+
import { userSettings } from '@/stores/userStore'
10+
import { Icon } from '@/components'
11+
import clsx from 'clsx'
12+
13+
export function PptxThemePicker() {
14+
const pptxTheme = userSettings((s) => s.pptxTheme) ?? PPTX_THEME_AUTO
15+
const setPptxTheme = userSettings((s) => s.setPptxTheme)
16+
17+
const isAutoActive = pptxTheme === PPTX_THEME_AUTO
18+
19+
return (
20+
<div className="grid grid-cols-4 sm:grid-cols-6 gap-3">
21+
{/* Auto option */}
22+
<button
23+
type="button"
24+
onClick={() => setPptxTheme(PPTX_THEME_AUTO)}
25+
className={clsx(
26+
'relative flex flex-col items-center gap-1.5 rounded-xl p-2 transition-all cursor-pointer',
27+
'hover:scale-105 focus:outline-none focus:ring-2 focus:ring-primary',
28+
isAutoActive
29+
? 'ring-2 ring-primary bg-default-100'
30+
: 'hover:bg-default-50',
31+
)}
32+
>
33+
<div className="w-10 h-10 rounded-lg border border-default-200 shadow-sm flex items-center justify-center bg-default-100">
34+
<Icon name="Sparks" size="sm" className="text-default-500" />
35+
</div>
36+
<span className="text-[11px] font-medium text-default-600 truncate max-w-full leading-tight">
37+
Auto
38+
</span>
39+
{isAutoActive && (
40+
<div className="absolute -top-1 -right-1 w-4 h-4 rounded-full bg-primary flex items-center justify-center">
41+
<Icon name="Check" size="sm" className="text-white" />
42+
</div>
43+
)}
44+
</button>
45+
46+
{/* Explicit theme options */}
47+
{PPTX_THEMES.map((theme) => {
48+
const isActive = theme.id === pptxTheme
49+
return (
50+
<button
51+
key={theme.id}
52+
type="button"
53+
onClick={() => setPptxTheme(theme.id)}
54+
className={clsx(
55+
'relative flex flex-col items-center gap-1.5 rounded-xl p-2 transition-all cursor-pointer',
56+
'hover:scale-105 focus:outline-none focus:ring-2 focus:ring-primary',
57+
isActive
58+
? 'ring-2 ring-primary bg-default-100'
59+
: 'hover:bg-default-50',
60+
)}
61+
>
62+
{/* Mini slide swatch: accent bg (left) + content bg (right) */}
63+
<div
64+
className="w-10 h-10 rounded-lg overflow-hidden border border-default-200 shadow-sm flex"
65+
style={{ backgroundColor: `#${theme.contentBg}` }}
66+
>
67+
<div
68+
className="w-1/2 h-full"
69+
style={{ backgroundColor: `#${theme.accentBg}` }}
70+
/>
71+
</div>
72+
73+
<span className="text-[11px] font-medium text-default-600 truncate max-w-full leading-tight">
74+
{theme.label}
75+
</span>
76+
77+
{isActive && (
78+
<div className="absolute -top-1 -right-1 w-4 h-4 rounded-full bg-primary flex items-center justify-center">
79+
<Icon name="Check" size="sm" className="text-white" />
80+
</div>
81+
)}
82+
83+
{theme.isDark && (
84+
<div className="absolute top-0.5 left-0.5">
85+
<Icon name="HalfMoon" size="sm" className="text-default-400" />
86+
</div>
87+
)}
88+
</button>
89+
)
90+
})}
91+
</div>
92+
)
93+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
const pres = new pptxgen();
2+
pres.layout = 'LAYOUT_16x9';
3+
pres.title = 'My Presentation';
4+
pres.author = 'Author\'s name';
5+
6+
// Slide 1 — Title
7+
let slide1 = pres.addSlide();
8+
slide1.background = { color: '003B75' };
9+
slide1.addText('My Presentation', {
10+
x: 0.5, y: 1.2, w: 9, h: 1.5,
11+
fontSize: 36, fontFace: 'Arial',
12+
color: 'FFFFFF', bold: true, align: 'center',
13+
});
14+
slide1.addShape(pres.ShapeType.line, {
15+
x: 3, y: 2.7, w: 4, h: 0,
16+
line: { color: 'CCDDEE', width: 1 },
17+
});
18+
slide1.addText('Author\'s name, Company name', {
19+
x: 0.5, y: 3, w: 9, h: 0.8,
20+
fontSize: 18, fontFace: 'Arial',
21+
color: 'CCDDEE', align: 'center',
22+
});
23+
slide1.addText('Sept 14, 2025 — Amazing Event, Location', {
24+
x: 0.5, y: 4.8, w: 9, h: 0.5,
25+
fontSize: 10, fontFace: 'Arial',
26+
color: '8899AA', align: 'center',
27+
});
28+
29+
// Slide 2 — Agenda
30+
let slide2 = pres.addSlide();
31+
slide2.background = { color: 'FFFFFF' };
32+
slide2.addText('Agenda:', {
33+
x: 0.5, y: 0.3, w: 9, h: 0.8,
34+
fontSize: 28, fontFace: 'Arial',
35+
color: '003B75', bold: true,
36+
});
37+
slide2.addText([
38+
{ text: '1. Key item #1', options: { breakLine: true } },
39+
{ text: '2. Key item #2', options: { breakLine: true } },
40+
{ text: '3. Key item #3', options: { breakLine: true } },
41+
{ text: '4. Key item #4', options: {} },
42+
], {
43+
x: 0.8, y: 1.5, w: 8, h: 3,
44+
fontSize: 18, fontFace: 'Arial',
45+
color: '333333', lineSpacingMultiple: 1.5,
46+
});
47+
48+
// Slide 3 — Section: Key item #1
49+
let slide3 = pres.addSlide();
50+
slide3.background = { color: '003B75' };
51+
slide3.addText([
52+
{ text: 'Key', options: { bold: true } },
53+
{ text: ' item ' },
54+
{ text: '#1', options: { italic: true } },
55+
], {
56+
x: 0.5, y: 2, w: 9, h: 1.5,
57+
fontSize: 32, fontFace: 'Arial',
58+
color: 'FFFFFF', align: 'center',
59+
});
60+
61+
// Slide 4 — A very important section
62+
let slide4 = pres.addSlide();
63+
slide4.background = { color: 'FFFFFF' };
64+
slide4.addText([
65+
{ text: 'A very ' },
66+
{ text: 'important', options: { bold: true } },
67+
{ text: ' section' },
68+
], {
69+
x: 0.5, y: 0.3, w: 9, h: 0.8,
70+
fontSize: 24, fontFace: 'Arial',
71+
color: '003B75',
72+
});
73+
74+
// Slide 5 — Features
75+
let slide5 = pres.addSlide();
76+
slide5.background = { color: 'FFFFFF' };
77+
slide5.addText('Features', {
78+
x: 0.5, y: 0.3, w: 9, h: 0.8,
79+
fontSize: 24, fontFace: 'Arial',
80+
color: '003B75',
81+
});
82+
slide5.addText([
83+
{ text: 'Pagination', options: { bold: true } },
84+
{ text: ': Slide-based navigation', options: { breakLine: true } },
85+
{ text: 'Formatting', options: { bold: true } },
86+
{ text: ': Support for Markdown', options: { breakLine: true } },
87+
{ text: 'Math', options: { bold: true } },
88+
{ text: ': Support for LaTeX, between $…$', options: { breakLine: true } },
89+
{ text: 'Themes', options: { bold: true } },
90+
{ text: ': Customizable appearance' },
91+
], {
92+
x: 0.8, y: 1.5, w: 8, h: 3,
93+
fontSize: 18, fontFace: 'Arial',
94+
color: '333333', bullet: true, lineSpacingMultiple: 1.5,
95+
});
96+
97+
// Slide 6 — Citations support (inline)
98+
let slide6 = pres.addSlide();
99+
slide6.background = { color: 'FFFFFF' };
100+
slide6.addText([
101+
{ text: 'Citations', options: { bold: true } },
102+
{ text: ' support' },
103+
], {
104+
x: 0.5, y: 0.3, w: 9, h: 0.8,
105+
fontSize: 24, fontFace: 'Arial',
106+
color: '003B75',
107+
});
108+
slide6.addText('Inline:', {
109+
x: 0.8, y: 1.3, w: 8, h: 0.5,
110+
fontSize: 18, fontFace: 'Arial',
111+
color: '333333',
112+
});
113+
slide6.addShape(pres.ShapeType.rect, {
114+
x: 0.8, y: 2, w: 8, h: 2,
115+
fill: { color: 'F5F5F5' },
116+
line: { color: '003B75', width: 0.5 },
117+
rectRadius: 0.05,
118+
});
119+
slide6.addText([
120+
{ text: 'Mass–energy equivalence equation:\n', options: {} },
121+
{ text: 'E = mc²', options: { bold: true, fontSize: 22 } },
122+
{ text: '\n\n— Einstein, 1905', options: { italic: true } },
123+
], {
124+
x: 1.2, y: 2.1, w: 7.2, h: 1.8,
125+
fontSize: 16, fontFace: 'Arial',
126+
color: '333333',
127+
});
128+
129+
// Slide 7 — Full-slide quote
130+
let slide7 = pres.addSlide();
131+
slide7.background = { color: '003B75' };
132+
slide7.addText('Mass–energy equivalence equation:', {
133+
x: 1, y: 1.5, w: 8, h: 0.8,
134+
fontSize: 20, fontFace: 'Arial',
135+
color: 'CCDDEE', italic: true,
136+
});
137+
slide7.addText('E = mc²', {
138+
x: 1, y: 2.3, w: 8, h: 1.2,
139+
fontSize: 40, fontFace: 'Arial',
140+
color: 'FFFFFF', bold: true, align: 'center',
141+
});
142+
slide7.addText('Einstein, 1905', {
143+
x: 1, y: 3.5, w: 8, h: 0.8,
144+
fontSize: 16, fontFace: 'Arial',
145+
color: 'CCDDEE', italic: true, align: 'center',
146+
});
147+
148+
// Slide 8 — Section: Key item #2
149+
let slide8 = pres.addSlide();
150+
slide8.background = { color: '003B75' };
151+
slide8.addText([
152+
{ text: 'Key', options: { bold: true } },
153+
{ text: ' item ' },
154+
{ text: '#2', options: { italic: true } },
155+
], {
156+
x: 0.5, y: 2, w: 9, h: 1.5,
157+
fontSize: 32, fontFace: 'Arial',
158+
color: 'FFFFFF', align: 'center',
159+
});
160+
161+
// Slide 9 — What's next?
162+
let slide9 = pres.addSlide();
163+
slide9.background = { color: 'FFFFFF' };
164+
slide9.addText([
165+
{ text: 'What\'s ' },
166+
{ text: 'next?', options: { bold: true } },
167+
], {
168+
x: 0.5, y: 2, w: 9, h: 1.5,
169+
fontSize: 28, fontFace: 'Arial',
170+
color: '003B75', align: 'center',
171+
});
172+
173+
// Slide 10 — Thank you
174+
let slide10 = pres.addSlide();
175+
slide10.background = { color: '003B75' };
176+
slide10.addText('Thank you', {
177+
x: 0.5, y: 2, w: 9, h: 1.5,
178+
fontSize: 36, fontFace: 'Arial',
179+
color: 'FFFFFF', bold: true, align: 'center',
180+
});

0 commit comments

Comments
 (0)