「進階檔案模式」支援輸出格式
文件類
| 檔案類型 | 用途 |
|---|---|
| docx | Word 文件(報告、合約、信件等) |
| PDF 文件(正式文件、簡報列印) | |
| txt | 純文字檔 |
| md | Markdown 文件 |
| html | 網頁檔案 |
| json | 資料交換格式 |
簡報類
| 檔案類型 | 用途 |
|---|---|
| pptx | PowerPoint 簡報(可編輯) |
| ppsx | PowerPoint 放映檔(直接播放) |
報表類
| 檔案類型 | 用途 |
|---|---|
| xlsx | Excel 試算表(資料表格、統計分析) |
技術與其他
| 檔案類型 | 用途 |
|---|---|
| py | Python 程式碼 |
| csv | 逗號分隔資料檔 |
| svg | 向量圖形 |
| mid | MIDI 音樂檔 |
| odf | OpenDocument 公式(MathML) |
| drawio | draw.io 流程圖/圖表 |
「進階檔案模式」使用方式
1、使用道具「金花」
因為目前 AI 對話空間的 Fable-talk 模型都是在本地進行,資源有限,所以只有使用道具「金花」的使用者才可以有開啟進階檔案模式的權限。

參與這次的測試,會獲得測試用的金花。
2、開啟「進階檔案模式」
為了避免使用者大失血,這個功能是預設關閉,而且即便是過去曾經開啟過,但離開了對話視窗,再次進來一樣是會被關閉的,所以送出對話之前,可以先確認進階檔案模式是否有開啟。

「進階檔案模式」預設關閉
「進階檔案模式」使用測試過程
注意事項:檔案的下載期限只有24小時,超過期限後就無法下載。
(1)「drawio 流程圖」轉「 pptx 簡報檔」
這是基於「程式語言演進史」為主題的檔案輸出測試過程。最近對 drawio 很感興趣,發現輸出的檔案格式裡面有 drawio,馬上就成為第一個想測試輸出的格式。
在同一個對話裡,先了解如何描述「產生程式語言的演進歷程 drawio 流程圖」這件事情,對話內容是這樣:
然後再請 Fable-talk 產生 drawio 流程圖,這當中有發生檔案打不開的問題,還有後續的解決方式:

可以正常打開後,嘗試把 drawio 流程圖轉換成 pptx 簡報檔:

請 Fable-talk 補充更詳細的資訊,額外做成延伸參考資料:

(2)產生一個 HTML 瑪利歐遊戲
在輸出的 15 種格式裡其中一個是聲音,想試試生成聲音這方面的表現,就請 Fable-talk 製作一個簡易的瑪利歐小遊戲,還真的是有聲音,真的很驚艷!
因為檔案只要超過 24 小時就會無法下載,所以把 HTML 的程式碼直接貼在這裡:
1<!DOCTYPE html>
2<html lang="zh-Hant">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>🧱 8-bit 平台跳躍遊戲</title>
7<style>
8 * { margin: 0; padding: 0; box-sizing: border-box; }
9 body {
10 background: #1a1a2e;
11 font-family: 'Courier New', monospace;
12 display: flex; flex-direction: column; align-items: center;
13 min-height: 100vh; color: #fff; padding: 20px;
14 }
15 h1 { margin-bottom: 10px; font-size: 20px; letter-spacing: 2px; color: #ffd700; text-shadow: 2px 2px 0 #c0392b; }
16 #gameWrap { position: relative; }
17 canvas {
18 display: block; background: #5c94fc;
19 border-radius: 8px; box-shadow: 0 8px 30px rgba(0,0,0,.5);
20 image-rendering: pixelated;
21 }
22 #hud {
23 display: flex; justify-content: space-between; width: 100%;
24 padding: 10px 14px; background: rgba(0,0,0,.35); border-radius: 8px 8px 0 0;
25 font-size: 16px; font-weight: bold;
26 }
27 #hud span { color: #ffd700; }
28 #hud .label { color: #aaa; }
29 #overlay {
30 position: absolute; inset: 0; display: flex; flex-direction: column;
31 align-items: center; justify-content: center; background: rgba(0,0,0,.55);
32 border-radius: 8px; text-align: center; gap: 12px;
33 }
34 #overlay.hidden { display: none; }
35 #overlay h2 { font-size: 28px; color: #ffd700; letter-spacing: 2px; }
36 #overlay p { font-size: 14px; color: #ddd; max-width: 320px; line-height: 1.6; }
37 button {
38 font-family: inherit; font-size: 18px; font-weight: bold; padding: 12px 28px;
39 background: #e74c3c; color: #fff; border: none; border-radius: 6px;
40 cursor: pointer; letter-spacing: 1px; box-shadow: 0 4px 0 #a93226;
41 transition: transform .05s;
42 }
43 button:hover { transform: translateY(-2px); }
44 button:active { transform: translateY(2px); box-shadow: 0 0 0 #a93226; }
45 #controls { margin-top: 14px; font-size: 13px; color: #bbb; }
46 kbd { background: #444; padding: 2px 7px; border-radius: 4px; font-family: monospace; }
47</style>
48</head>
49<body>
50 <h1>🍄 8-BIT 平台跳躍</h1>
51 <div id="gameWrap">
52 <div id="hud">
53 <span>❤️ <span class="label" id="hudLives">3</span></span>
54 <span>🪙 <span class="label" id="hudCoins">0</span></span>
55 <span>⭐ <span class="label" id="hudScore">0</span></span>
56 </div>
57 <canvas id="game" width="800" height="450"></canvas>
58 <div id="overlay">
59 <h2 id="overlayTitle">🍄 瑪利歐小冒險</h2>
60 <p id="overlayDesc">收集金幣、踩扁蘑菇怪,抵達終點旗竿!<br>
61 使用 <kbd>←</kbd> <kbd>→</kbd> 移動,<kbd>空白</kbd> 跳躍。
62 碰到敵人會扣一條命,踩到牠們頭頂則能消滅。</p>
63 <button id="startBtn">▶ 開始遊戲</button>
64 </div>
65 </div>
66 <div id="controls">
67 <kbd>←</kbd><kbd>→</kbd> 移動 | <kbd>空白</kbd> 跳躍 | <kbd>R</kbd> 重新開始
68 </div>
69
70<script>
71// ============ 音效引擎(Web Audio API,合成 8-bit 音效)============
72const AudioEngine = (function() {
73 let ctx = null;
74 let muted = false;
75
76 // 需在使用者互動後才能啟動
77 function ensure() {
78 if (!ctx) {
79 ctx = new (window.AudioContext || window.webkitAudioContext)();
80 }
81 if (ctx.state === 'suspended') ctx.resume();
82 return ctx;
83 }
84
85 // 播放單一方波音符(經典 8-bit 風格)
86 function tone(freq, start, dur, vol = 0.2, type = 'square') {
87 if (!ctx || muted) return;
88 const osc = ctx.createOscillator();
89 const gain = ctx.createGain();
90 osc.type = type;
91 osc.frequency.value = freq;
92 gain.gain.setValueAtTime(0, ctx.currentTime + start);
93 gain.gain.linearRampToValueAtTime(vol, ctx.currentTime + start + 0.01);
94 gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + start + dur);
95 osc.connect(gain);
96 gain.connect(ctx.destination);
97 osc.start(ctx.currentTime + start);
98 osc.stop(ctx.currentTime + start + dur + 0.02);
99 }
100
101 // 雜訊(爆炸/踩扁用)
102 function noise(start, dur, vol = 0.2) {
103 if (!ctx || muted) return;
104 const bufferSize = Math.floor(ctx.sampleRate * dur);
105 const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
106 const data = buffer.getChannelData(0);
107 for (let i = 0; i < bufferSize; i++) data[i] = (Math.random() * 2 - 1);
108 const src = ctx.createBufferSource();
109 src.buffer = buffer;
110 const gain = ctx.createGain();
111 gain.gain.setValueAtTime(vol, ctx.currentTime + start);
112 gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + start + dur);
113 src.connect(gain);
114 gain.connect(ctx.destination);
115 src.start(ctx.currentTime + start);
116 }
117
118 function unlock() { ensure(); }
119
120 function setMuted(m) { muted = m; }
121
122 return {
123 unlock,
124 setMuted,
125 jump() { ensure(); tone(660, 0, 0.15, 0.18); tone(880, 0.08, 0.12, 0.14); },
126 coin() { ensure(); tone(988, 0, 0.08, 0.18); tone(1319, 0.08, 0.25, 0.16); }, // B5 → E6
127 stomp() { ensure(); noise(0, 0.15, 0.22); tone(200, 0, 0.12, 0.18, 'sawtooth'); }, // 踩怪
128 powerUp(){ ensure(); tone(523,0,.1,.15); tone(659,0.1,.1,.15); tone(784,0.2,.1,.15); tone(1047,0.3,.3,.18); },
129 hurt() { ensure(); tone(392, 0, 0.12, 0.2, 'sawtooth'); tone(330, 0.12, 0.12, 0.2, 'sawtooth'); tone(262, 0.24, 0.3, 0.2, 'sawtooth'); },
130 gameOver(){ ensure(); tone(440,0,.2,.18); tone(349,0.2,.2,.18); tone(294,0.4,.2,.18); tone(196,0.6,.6,.22); },
131 win() { ensure(); const n=[523,659,784,1047,784,659,1047]; n.forEach((f,i)=>tone(f,i*0.12,0.25,.18)); },
132 };
133})();
134
135// ============ 遊戲主程式 ============
136const canvas = document.getElementById('game');
137const ctx = canvas.getContext('2d');
138const W = canvas.width, H = canvas.height;
139const GRAVITY = 0.5;
140const GROUND_Y = 400;
141
142const hudLives = document.getElementById('hudLives');
143const hudCoins = document.getElementById('hudCoins');
144const hudScore = document.getElementById('hudScore');
145const overlay = document.getElementById('overlay');
146const overlayTitle = document.getElementById('overlayTitle');
147const overlayDesc = document.getElementById('overlayDesc');
148const startBtn = document.getElementById('startBtn');
149
150const state = {
151 running: false,
152 started: false,
153 keys: {},
154 player: { x: 80, y: GROUND_Y - 40, w: 32, h: 40, vx: 0, vy: 0, onGround: false, facing: 1 },
155 coins: [
156 { x: 260, y: 300, taken: false },
157 { x: 420, y: 240, taken: false },
158 { x: 520, y: 200, taken: false },
159 { x: 620, y: 320, taken: false },
160 ],
161 enemies: [
162 { x: 350, y: GROUND_Y - 30, w: 32, h: 30, vx: 1.2, alive: true },
163 { x: 550, y: GROUND_Y - 30, w: 32, h: 30, vx: -1.0, alive: true },
164 ],
165 platforms: [
166 { x: 240, y: 330, w: 120, h: 18 },
167 { x: 430, y: 270, w: 120, h: 18 },
168 { x: 620, y: 350, w: 120, h: 18 },
169 ],
170 goal: { x: 740, y: GROUND_Y - 70, w: 20, h: 70 },
171 brick: { x: 470, y: 150, w: 40, h: 40, broken: false }, // 金色方塊(可打碎)
172 lives: 3,
173 coinsCount: 0,
174 score: 0,
175 won: false,
176 invuln: 0,
177};
178
179function resetPlayer() {
180 state.player.x = 80;
181 state.player.y = GROUND_Y - 40;
182 state.player.vx = 0; state.player.vy = 0;
183 state.player.onGround = false;
184}
185
186function resetLevel() {
187 state.coins.forEach(c => c.taken = false);
188 state.enemies[0] = { x: 350, y: GROUND_Y - 30, w: 32, h: 30, vx: 1.2, alive: true };
189 state.enemies[1] = { x: 550, y: GROUND_Y - 30, w: 32, h: 30, vx: -1.0, alive: true };
190 state.brick.broken = false;
191 state.won = false;
192 resetPlayer();
193}
194
195function startGame() {
196 AudioEngine.unlock();
197 state.lives = 3;
198 state.coinsCount = 0;
199 state.score = 0;
200 state.invuln = 0;
201 resetLevel();
202 state.running = true;
203 state.started = true;
204 overlay.classList.add('hidden');
205 updateHud();
206}
207
208function returnToMenu(text, sub) {
209 state.running = false;
210 overlayTitle.textContent = text;
211 overlayDesc.textContent = sub;
212 startBtn.textContent = '⟳ 再玩一次';
213 overlay.classList.remove('hidden');
214}
215
216function updateHud() {
217 hudLives.textContent = state.lives;
218 hudCoins.textContent = state.coinsCount;
219 hudScore.textContent = state.score;
220}
221
222// ---------- 碰撞輔助 ----------
223function rectsOverlap(ax, ay, aw, ah, bx, by, bw, bh) {
224 return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
225}
226
227// ---------- 更新 ----------
228function update() {
229 if (!state.running) return;
230 const p = state.player;
231 const prevY = p.y;
232 const prevX = p.x;
233
234 // 水平移動
235 if (state.keys['ArrowLeft'] || state.keys['a']) { p.vx = -4; p.facing = -1; }
236 else if (state.keys['ArrowRight'] || state.keys['d']) { p.vx = 4; p.facing = 1; }
237 else p.vx = 0;
238 p.x += p.vx;
239
240 // 重力與跳躍
241 if ((state.keys[' '] || state.keys['ArrowUp'] || state.keys['w']) && p.onGround) {
242 p.vy = -11;
243 p.onGround = false;
244 AudioEngine.jump();
245 }
246 p.vy += GRAVITY;
247 p.y += p.vy;
248
249 // 地面碰撞
250 if (p.y + p.h >= GROUND_Y) {
251 if (prevY + p.h <= GROUND_Y) { p.onGround = true; }
252 p.y = GROUND_Y - p.h; p.vy = 0; p.onGround = true;
253 }
254
255 // 平台碰撞(只從上方)
256 for (const pf of state.platforms) {
257 if (rectsOverlap(p.x, p.y, p.w, p.h, pf.x, pf.y, pf.w, pf.h) && p.vy > 0 && prevY + p.h <= pf.y + 4) {
258 p.y = pf.y - p.h;
259 p.vy = 0;
260 p.onGround = true;
261 }
262 }
263
264 // 金色方塊:從下方撞破
265 if (!state.brick.broken &&
266 rectsOverlap(p.x, p.y, p.w, p.h, state.brick.x, state.brick.y, state.brick.w, state.brick.h) &&
267 p.vy < 0) {
268 state.brick.broken = true;
269 state.coinsCount++;
270 state.score += 100;
271 AudioEngine.powerUp();
272 updateHud();
273 p.vy = 5;
274 }
275
276 // 金幣收集
277 for (const c of state.coins) {
278 if (!c.taken && rectsOverlap(p.x, p.y, p.w, p.h, c.x, c.y, 22, 22)) {
279 c.taken = true;
280 state.coinsCount++;
281 state.score += 200;
282 AudioEngine.coin();
283 updateHud();
284 }
285 }
286
287 // 敵人移動與平台邊緣反彈
288 for (const e of state.enemies) {
289 if (!e.alive) continue;
290 e.x += e.vx;
291 if (e.x <= 250) e.vx = Math.abs(e.vx);
292 if (e.x + e.w >= 720) e.vx = -Math.abs(e.vx);
293
294 // 踩怪
295 if (rectsOverlap(p.x, p.y, p.w, p.h, e.x, e.y, e.w, e.h)) {
296 if (p.vy > 0 && prevY + p.h <= e.y + 8) {
297 e.alive = false;
298 p.vy = -9;
299 state.score += 300;
300 AudioEngine.stomp();
301 updateHud();
302 } else if (state.invuln <= 0) {
303 damage();
304 }
305 }
306 }
307
308 // 抵達終點
309 if (rectsOverlap(p.x, p.y, p.w, p.h, state.goal.x, state.goal.y, state.goal.w, state.goal.h) && !state.won) {
310 state.won = true;
311 state.score += 500;
312 AudioEngine.win();
313 updateHud();
314 setTimeout(() => {
315 if (state.started) returnToMenu('🏁 你過關了!', '得分:' + state.score + ' 金幣:' + state.coinsCount +
316 '<br>還有榮譽加分 +500 分。再來一局?');
317 }, 700);
318 state.running = false;
319 }
320
321 // 無敵時間
322 if (state.invuln > 0) state.invuln--;
323
324 // 防止超出右界
325 if (p.x > W - p.w) p.x = W - p.w;
326 if (p.x < 0) p.x = 0;
327}
328
329function damage() {
330 state.lives--;
331 AudioEngine.hurt();
332 state.invuln = 60;
333 updateHud();
334 if (state.lives <= 0) {
335 AudioEngine.gameOver();
336 setTimeout(() => returnToMenu('💀 遊戲結束', '你踩扁了蘑菇,但用光了生命。<br>再挑戰一次吧!'), 600);
337 state.running = false;
338 resetPlayer();
339 } else {
340 resetPlayer();
341 }
342}
343
344// ---------- 繪製 ----------
345function draw() {
346 // 天空
347 ctx.fillStyle = '#5c94fc';
348 ctx.fillRect(0, 0, W, H);
349 // 雲朵
350 ctx.fillStyle = 'rgba(255,255,255,0.8)';
351 ctx.fillRect(120, 80, 60, 20); ctx.fillRect(140, 65, 20, 20);
352 ctx.fillRect(500, 120, 60, 20); ctx.fillRect(520, 105, 20, 20);
353 ctx.fillRect(370, 40, 40, 14); ctx.fillRect(385, 30, 14, 14);
354
355 // 地面(磚塊)
356 ctx.fillStyle = '#c0392b';
357 ctx.fillRect(0, GROUND_Y, W, H - GROUND_Y);
358 ctx.fillStyle = '#d35400';
359 ctx.fillRect(0, GROUND_Y, W, 8);
360 // 磚線
361 ctx.strokeStyle = 'rgba(0,0,0,0.25)';
362 ctx.lineWidth = 1;
363 for (let x = 0; x < W; x += 32) {
364 for (let y = GROUND_Y; y < H; y += 16) {
365 ctx.strokeRect(x, y, 32, 16);
366 }
367 }
368
369 // 平台
370 ctx.fillStyle = '#8e44ad';
371 for (const pf of state.platforms) ctx.fillRect(pf.x, pf.y, pf.w, pf.h);
372
373 // 金色方塊
374 if (!state.brick.broken) {
375 ctx.fillStyle = '#f1c40f';
376 ctx.strokeStyle = '#b7950b';
377 ctx.lineWidth = 2;
378 ctx.fillRect(state.brick.x, state.brick.y, state.brick.w, state.brick.h);
379 ctx.strokeRect(state.brick.x + 2, state.brick.y + 2, state.brick.w - 4, state.brick.h - 4);
380 ctx.font = 'bold 18px monospace';
381 ctx.textAlign = 'center';
382 ctx.fillStyle = '#8d6e00';
383 ctx.fillText('?', state.brick.x + state.brick.w / 2, state.brick.y + 28);
384 }
385
386 // 金幣(動畫閃爍)
387 for (const c of state.coins) {
388 if (c.taken) continue;
389 const flicker = Math.sin(state.frame * 0.2) > 0;
390 ctx.fillStyle = flicker ? '#ffd700' : '#f39c12';
391 ctx.beginPath();
392 ctx.arc(c.x + 11, c.y + 11, 11, 0, Math.PI * 2);
393 ctx.fill();
394 ctx.strokeStyle = '#e67e22'; ctx.lineWidth = 2;
395 ctx.beginPath(); ctx.arc(c.x + 11, c.y + 11, 11, 0, Math.PI * 2); ctx.stroke();
396 }
397
398 // 敵人(蘑菇怪 Goomba)
399 for (const e of state.enemies) {
400 if (!e.alive) continue;
401 ctx.fillStyle = '#8b4513';
402 ctx.strokeStyle = '#5d2e0d';
403 ctx.fillRect(e.x, e.y, e.w, e.h);
404 ctx.strokeRect(e.x, e.y, e.w, e.h);
405 ctx.fillStyle = '#fff';
406 ctx.fillRect(e.x + 6, e.y + 8, 6, 8);
407 ctx.fillRect(e.x + 20, e.y + 8, 6, 8);
408 ctx.fillStyle = '#000';
409 ctx.fillRect(e.x + 8, e.y + 11, 3, 3);
410 ctx.fillRect(e.x + 22, e.y + 11, 3, 3);
411 ctx.fillStyle = '#2c3e50';
412 ctx.fillRect(e.x + 2, e.y + e.h - 4, e.w - 4, 4);
413 }
414
415 // 玩家(簡化瑪利歐)
416 const p = state.player;
417 if (state.invuln > 0 && Math.floor(state.frame / 6) % 2 === 0) {
418 // 受傷閃爍:跳過繪製
419 } else {
420 ctx.save();
421 ctx.translate(p.x + p.w / 2, p.y + p.h / 2);
422 ctx.scale(p.facing, 1);
423 // 帽子
424 ctx.fillStyle = '#e74c3c';
425 ctx.fillRect(-14, -p.h / 2 - 10, 28, 12);
426 // 臉與身體
427 ctx.fillStyle = '#d8a06a';
428 ctx.fillRect(-12, -p.h / 2 + 2, 24, 14);
429 ctx.fillStyle = '#3498db';
430 ctx.fillRect(-14, -p.h / 2 + 16, 28, 10);
431 ctx.fillStyle = '#e74c3c';
432 ctx.fillRect(-14, -p.h / 2 + 26, 28, 10);
433 // 眼睛
434 ctx.fillStyle = '#000';
435 ctx.fillRect(6, -p.h / 2 + 4, 4, 5);
436 ctx.fillRect(-2, -p.h / 2 + 4, 4, 5);
437 ctx.restore();
438 }
439
440 // 終點旗竿
441 ctx.fillStyle = '#ecf0f1';
442 ctx.fillRect(state.goal.x + 7, state.goal.y, 6, state.goal.h);
443 if (state.won) {
444 ctx.fillStyle = '#f1c40f';
445 ctx.fillRect(state.goal.x + 13, state.goal.y, 16, 10);
446 } else {
447 ctx.fillStyle = '#e74c3c';
448 ctx.fillRect(state.goal.x + 13, state.goal.y, 16, 10);
449 }
450
451 state.frame++;
452}
453
454// ---------- 主迴圈 ----------
455function loop() {
456 update();
457 draw();
458 requestAnimationFrame(loop);
459}
460
461// ---------- 輸入 ----------
462window.addEventListener('keydown', (e) => {
463 if ([' ','ArrowLeft','ArrowRight','ArrowUp','ArrowDown'].includes(e.key)) e.preventDefault();
464 state.keys[e.key] = true;
465 if (e.key.toLowerCase() === 'r') startGame();
466});
467window.addEventListener('keyup', (e) => { state.keys[e.key] = false; });
468startBtn.addEventListener('click', startGame);
469
470// 啟動
471state.frame = 0;
472loop();
473</script>
474</body>
475</html>
476
