@open-slide/core 1.5.0 → 1.7.0
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.
- package/dist/{build-DZhbjQpQ.js → build-tLrkKUHr.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-BQdTMho4.d.ts → config-CfMThYN9.d.ts} +1 -1
- package/dist/{config-iKjqaX08.js → config-PwUHqZ_X.js} +246 -2
- package/dist/{dev-BjLGk5nN.js → dev-DpCIRbhT.js} +1 -1
- package/dist/{en-DDGqyNaW.js → en-BDnM5zKJ.js} +4 -0
- package/dist/index.d.ts +29 -4
- package/dist/index.js +20 -4
- package/dist/locale/index.d.ts +1 -1
- package/dist/locale/index.js +13 -1
- package/dist/{preview-jwLWHWkQ.js → preview-BSGlM6Se.js} +1 -1
- package/dist/{types-Dpr8nbih.d.ts → types-B-KrjgX8.d.ts} +5 -0
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite/index.js +1 -1
- package/package.json +1 -1
- package/skills/create-theme/SKILL.md +30 -22
- package/skills/slide-authoring/SKILL.md +186 -0
- package/src/app/components/asset-view.tsx +8 -1
- package/src/app/components/inspector/asset-picker-dialog.tsx +196 -0
- package/src/app/components/inspector/inspect-overlay.tsx +132 -35
- package/src/app/components/inspector/inspector-panel.tsx +19 -256
- package/src/app/components/inspector/inspector-provider.tsx +102 -1
- package/src/app/components/panel/save-card.tsx +4 -4
- package/src/app/components/player.tsx +13 -3
- package/src/app/components/present/overview-grid.tsx +4 -1
- package/src/app/components/slide-transition-layer.tsx +154 -0
- package/src/app/components/style-panel/style-panel.tsx +3 -0
- package/src/app/components/themes/theme-detail.tsx +7 -2
- package/src/app/components/themes/themes-gallery.tsx +4 -1
- package/src/app/components/thumbnail-rail.tsx +10 -2
- package/src/app/lib/assets.ts +2 -0
- package/src/app/lib/export-html.ts +7 -2
- package/src/app/lib/export-pdf.ts +34 -2
- package/src/app/lib/folders.ts +35 -1
- package/src/app/lib/page-context.tsx +38 -0
- package/src/app/lib/sdk.ts +3 -1
- package/src/app/lib/transition.ts +23 -0
- package/src/app/lib/use-prefers-reduced-motion.ts +19 -0
- package/src/app/lib/use-wheel-page-navigation.ts +7 -0
- package/src/app/routes/home-shell.tsx +13 -2
- package/src/app/routes/home.tsx +28 -2
- package/src/app/routes/presenter.tsx +7 -2
- package/src/app/routes/slide.tsx +19 -8
- package/src/locale/en.ts +4 -0
- package/src/locale/ja.ts +4 -0
- package/src/locale/types.ts +5 -0
- package/src/locale/zh-cn.ts +4 -0
- package/src/locale/zh-tw.ts +4 -0
package/dist/cli/bin.js
CHANGED
|
@@ -57,15 +57,15 @@ async function run(argv) {
|
|
|
57
57
|
program.name("open-slide").description("Author slides — we handle the Vite/React stack.").version(version, "-v, --version", "print version").helpOption("-h, --help", "show help").showHelpAfterError(chalk.dim("(run `open-slide --help` for usage)"));
|
|
58
58
|
program.command("dev").description("Start the dev server").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").option("--no-skills-check", "skip the built-in skills drift check").action(async (flags) => {
|
|
59
59
|
if (flags.skillsCheck !== false) await runSkillsDriftCheck(resolveBuiltinSkillsDir());
|
|
60
|
-
const { dev } = await import("../dev-
|
|
60
|
+
const { dev } = await import("../dev-DpCIRbhT.js");
|
|
61
61
|
await dev(flags);
|
|
62
62
|
});
|
|
63
63
|
program.command("build").description("Build a static site").option("--out-dir <dir>", "output directory (defaults to `dist`)").action(async (flags) => {
|
|
64
|
-
const { build } = await import("../build-
|
|
64
|
+
const { build } = await import("../build-tLrkKUHr.js");
|
|
65
65
|
await build(flags);
|
|
66
66
|
});
|
|
67
67
|
program.command("preview").description("Preview the production build").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").action(async (flags) => {
|
|
68
|
-
const { preview } = await import("../preview-
|
|
68
|
+
const { preview } = await import("../preview-BSGlM6Se.js");
|
|
69
69
|
await preview(flags);
|
|
70
70
|
});
|
|
71
71
|
program.command("sync:skills").description("Sync built-in skills from @open-slide/core into this workspace").option("--dry-run", "show what would change without writing").action(async (flags) => {
|
|
@@ -1150,6 +1150,31 @@ function findAssetUsages(source, assetPath) {
|
|
|
1150
1150
|
if (!target?.defaultIdent) return 0;
|
|
1151
1151
|
return collectImgSrcUses(ast, target.defaultIdent).length;
|
|
1152
1152
|
}
|
|
1153
|
+
function findReferencedAssets(source, assetPaths) {
|
|
1154
|
+
const referenced = new Set();
|
|
1155
|
+
if (assetPaths.length === 0) return referenced;
|
|
1156
|
+
const ast = parseSource$2(source);
|
|
1157
|
+
if (!ast) return referenced;
|
|
1158
|
+
const wanted = new Set(assetPaths);
|
|
1159
|
+
const identToPath = new Map();
|
|
1160
|
+
for (const imp of findImports$1(ast)) {
|
|
1161
|
+
if (!imp.defaultIdent) continue;
|
|
1162
|
+
if (wanted.has(imp.source)) identToPath.set(imp.defaultIdent, imp.source);
|
|
1163
|
+
}
|
|
1164
|
+
if (identToPath.size === 0) return referenced;
|
|
1165
|
+
walkJsx(ast, (n) => {
|
|
1166
|
+
if (!t$3.isJSXElement(n)) return;
|
|
1167
|
+
const opening = n.openingElement;
|
|
1168
|
+
if (!t$3.isJSXIdentifier(opening.name) || opening.name.name !== "img") return;
|
|
1169
|
+
const src = findJsxAttr(opening, "src");
|
|
1170
|
+
if (!src?.value || !t$3.isJSXExpressionContainer(src.value)) return;
|
|
1171
|
+
const expr = src.value.expression;
|
|
1172
|
+
if (!t$3.isIdentifier(expr)) return;
|
|
1173
|
+
const p = identToPath.get(expr.name);
|
|
1174
|
+
if (p) referenced.add(p);
|
|
1175
|
+
});
|
|
1176
|
+
return referenced;
|
|
1177
|
+
}
|
|
1153
1178
|
function applyRevertAsset(source, assetPath) {
|
|
1154
1179
|
const ast = parseSource$2(source);
|
|
1155
1180
|
if (!ast) return {
|
|
@@ -1227,6 +1252,62 @@ function validateSlideName(v) {
|
|
|
1227
1252
|
if (trimmed.length < 1 || trimmed.length > 80) return null;
|
|
1228
1253
|
return trimmed;
|
|
1229
1254
|
}
|
|
1255
|
+
function unwrapExpression(node) {
|
|
1256
|
+
let current = node;
|
|
1257
|
+
while (current && (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression")) current = current.expression;
|
|
1258
|
+
return current;
|
|
1259
|
+
}
|
|
1260
|
+
function readMetaTitleInSource(source) {
|
|
1261
|
+
let ast;
|
|
1262
|
+
try {
|
|
1263
|
+
ast = parse(source, {
|
|
1264
|
+
sourceType: "module",
|
|
1265
|
+
plugins: ["typescript", "jsx"],
|
|
1266
|
+
errorRecovery: true
|
|
1267
|
+
});
|
|
1268
|
+
} catch {
|
|
1269
|
+
return { kind: "unsupported" };
|
|
1270
|
+
}
|
|
1271
|
+
const body = ast.program?.body ?? [];
|
|
1272
|
+
for (const stmt of body) {
|
|
1273
|
+
if (stmt.type !== "ExportNamedDeclaration") continue;
|
|
1274
|
+
const decl = stmt.declaration;
|
|
1275
|
+
if (!decl || decl.type !== "VariableDeclaration") continue;
|
|
1276
|
+
const declarations = decl.declarations ?? [];
|
|
1277
|
+
for (const d of declarations) {
|
|
1278
|
+
const id = d.id;
|
|
1279
|
+
if (!id || id.type !== "Identifier" || id.name !== "meta") continue;
|
|
1280
|
+
const init = unwrapExpression(d.init);
|
|
1281
|
+
if (!init || init.type !== "ObjectExpression") return { kind: "unsupported" };
|
|
1282
|
+
const properties = init.properties ?? [];
|
|
1283
|
+
for (const property of properties) {
|
|
1284
|
+
if (property.type !== "ObjectProperty" || property.computed) continue;
|
|
1285
|
+
const key = property.key;
|
|
1286
|
+
const keyName = key?.type === "Identifier" ? key.name : key?.type === "StringLiteral" ? key.value : void 0;
|
|
1287
|
+
if (keyName !== "title") continue;
|
|
1288
|
+
const value = property.value;
|
|
1289
|
+
if (value?.type === "StringLiteral" && typeof value.value === "string") return {
|
|
1290
|
+
kind: "found",
|
|
1291
|
+
title: value.value
|
|
1292
|
+
};
|
|
1293
|
+
if (value?.type === "TemplateLiteral") {
|
|
1294
|
+
const expressions = value.expressions ?? [];
|
|
1295
|
+
const quasis = value.quasis ?? [];
|
|
1296
|
+
const firstValue = quasis[0]?.value;
|
|
1297
|
+
const cooked = firstValue?.cooked;
|
|
1298
|
+
const raw = firstValue?.raw;
|
|
1299
|
+
if (expressions.length === 0 && typeof (cooked ?? raw) === "string") return {
|
|
1300
|
+
kind: "found",
|
|
1301
|
+
title: cooked ?? raw
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
return { kind: "unsupported" };
|
|
1305
|
+
}
|
|
1306
|
+
return { kind: "missing" };
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return { kind: "missing" };
|
|
1310
|
+
}
|
|
1230
1311
|
async function rmSlideDir(slidesRoot, slideId) {
|
|
1231
1312
|
if (!SLIDE_ID_RE$3.test(slideId)) return false;
|
|
1232
1313
|
const dir = path.resolve(slidesRoot, slideId);
|
|
@@ -1241,6 +1322,117 @@ async function rmSlideDir(slidesRoot, slideId) {
|
|
|
1241
1322
|
return false;
|
|
1242
1323
|
}
|
|
1243
1324
|
}
|
|
1325
|
+
async function duplicateSlideDir(slidesRoot, slideId, desiredId) {
|
|
1326
|
+
if (!SLIDE_ID_RE$3.test(slideId)) return {
|
|
1327
|
+
ok: false,
|
|
1328
|
+
status: 400,
|
|
1329
|
+
error: "invalid slideId"
|
|
1330
|
+
};
|
|
1331
|
+
const root = path.resolve(slidesRoot);
|
|
1332
|
+
const srcDir = path.resolve(root, slideId);
|
|
1333
|
+
if (!srcDir.startsWith(root + path.sep)) return {
|
|
1334
|
+
ok: false,
|
|
1335
|
+
status: 400,
|
|
1336
|
+
error: "invalid slideId"
|
|
1337
|
+
};
|
|
1338
|
+
try {
|
|
1339
|
+
await fs.access(path.join(srcDir, "index.tsx"));
|
|
1340
|
+
} catch {
|
|
1341
|
+
return {
|
|
1342
|
+
ok: false,
|
|
1343
|
+
status: 404,
|
|
1344
|
+
error: "slide not found"
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
let newId;
|
|
1348
|
+
if (desiredId !== void 0) {
|
|
1349
|
+
if (!SLIDE_ID_RE$3.test(desiredId)) return {
|
|
1350
|
+
ok: false,
|
|
1351
|
+
status: 400,
|
|
1352
|
+
error: "invalid newId"
|
|
1353
|
+
};
|
|
1354
|
+
newId = desiredId;
|
|
1355
|
+
const dstDir$1 = path.resolve(root, newId);
|
|
1356
|
+
if (!dstDir$1.startsWith(root + path.sep)) return {
|
|
1357
|
+
ok: false,
|
|
1358
|
+
status: 400,
|
|
1359
|
+
error: "invalid newId"
|
|
1360
|
+
};
|
|
1361
|
+
try {
|
|
1362
|
+
await fs.access(dstDir$1);
|
|
1363
|
+
return {
|
|
1364
|
+
ok: false,
|
|
1365
|
+
status: 409,
|
|
1366
|
+
error: "slide already exists"
|
|
1367
|
+
};
|
|
1368
|
+
} catch {}
|
|
1369
|
+
} else {
|
|
1370
|
+
let suffix = 1;
|
|
1371
|
+
while (true) {
|
|
1372
|
+
newId = suffix === 1 ? `${slideId}-copy` : `${slideId}-copy-${suffix}`;
|
|
1373
|
+
try {
|
|
1374
|
+
await fs.access(path.resolve(root, newId));
|
|
1375
|
+
suffix++;
|
|
1376
|
+
} catch {
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
const dstDir = path.resolve(root, newId);
|
|
1382
|
+
if (!dstDir.startsWith(root + path.sep)) return {
|
|
1383
|
+
ok: false,
|
|
1384
|
+
status: 400,
|
|
1385
|
+
error: "invalid newId"
|
|
1386
|
+
};
|
|
1387
|
+
const srcEntry = path.join(srcDir, "index.tsx");
|
|
1388
|
+
let copiedEntrySource;
|
|
1389
|
+
try {
|
|
1390
|
+
const source = await fs.readFile(srcEntry, "utf8");
|
|
1391
|
+
const metaTitle = readMetaTitleInSource(source);
|
|
1392
|
+
if (metaTitle.kind === "unsupported") return {
|
|
1393
|
+
ok: false,
|
|
1394
|
+
status: 422,
|
|
1395
|
+
error: "could not update copied slide title"
|
|
1396
|
+
};
|
|
1397
|
+
const title = metaTitle.kind === "found" ? metaTitle.title : slideId;
|
|
1398
|
+
const updated = updateMetaTitleInSource(source, `${title} (copy)`);
|
|
1399
|
+
if (updated === null) return {
|
|
1400
|
+
ok: false,
|
|
1401
|
+
status: 422,
|
|
1402
|
+
error: "could not update copied slide title"
|
|
1403
|
+
};
|
|
1404
|
+
copiedEntrySource = updated;
|
|
1405
|
+
} catch {
|
|
1406
|
+
return {
|
|
1407
|
+
ok: false,
|
|
1408
|
+
status: 404,
|
|
1409
|
+
error: "slide not found"
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
try {
|
|
1413
|
+
await fs.cp(srcDir, dstDir, {
|
|
1414
|
+
recursive: true,
|
|
1415
|
+
errorOnExist: true,
|
|
1416
|
+
force: false
|
|
1417
|
+
});
|
|
1418
|
+
await fs.writeFile(path.join(dstDir, "index.tsx"), copiedEntrySource, "utf8");
|
|
1419
|
+
return {
|
|
1420
|
+
ok: true,
|
|
1421
|
+
slideId: newId
|
|
1422
|
+
};
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
if (err.code === "EEXIST") return {
|
|
1425
|
+
ok: false,
|
|
1426
|
+
status: 409,
|
|
1427
|
+
error: "slide already exists"
|
|
1428
|
+
};
|
|
1429
|
+
return {
|
|
1430
|
+
ok: false,
|
|
1431
|
+
status: 500,
|
|
1432
|
+
error: String(err.message ?? err)
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1244
1436
|
function resolveSlideEntry(slidesRoot, slideId) {
|
|
1245
1437
|
if (!SLIDE_ID_RE$3.test(slideId)) return null;
|
|
1246
1438
|
const dir = path.resolve(slidesRoot, slideId);
|
|
@@ -1796,10 +1988,38 @@ function registerAssetRoutes(server, ctx) {
|
|
|
1796
1988
|
size: stat.size,
|
|
1797
1989
|
mtime: stat.mtimeMs,
|
|
1798
1990
|
mime: mimeForFilename(name),
|
|
1799
|
-
url: `/__assets/${slideId}/${encodeURIComponent(name)}
|
|
1991
|
+
url: `/__assets/${slideId}/${encodeURIComponent(name)}`,
|
|
1992
|
+
unused: true
|
|
1800
1993
|
});
|
|
1801
1994
|
}
|
|
1802
1995
|
assets.sort((a, b) => a.name.localeCompare(b.name));
|
|
1996
|
+
if (assets.length > 0) {
|
|
1997
|
+
const isGlobal = slideId === GLOBAL_SCOPE;
|
|
1998
|
+
let scanIds;
|
|
1999
|
+
if (isGlobal) try {
|
|
2000
|
+
const dirs = await fs.readdir(ctx.slidesRoot, { withFileTypes: true });
|
|
2001
|
+
scanIds = dirs.filter((e) => e.isDirectory() && SLIDE_ID_RE$3.test(e.name)).map((e) => e.name);
|
|
2002
|
+
} catch {
|
|
2003
|
+
scanIds = [];
|
|
2004
|
+
}
|
|
2005
|
+
else scanIds = SLIDE_ID_RE$3.test(slideId) ? [slideId] : [];
|
|
2006
|
+
const paths = assets.map((a) => isGlobal ? `@assets/${a.name}` : `./assets/${a.name}`);
|
|
2007
|
+
const pathToAsset = new Map(paths.map((p, i) => [p, assets[i]]));
|
|
2008
|
+
for (const sid of scanIds) {
|
|
2009
|
+
const entry = resolveSlideEntry(ctx.slidesRoot, sid);
|
|
2010
|
+
if (!entry) continue;
|
|
2011
|
+
let source;
|
|
2012
|
+
try {
|
|
2013
|
+
source = await fs.readFile(entry, "utf8");
|
|
2014
|
+
} catch {
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
for (const p of findReferencedAssets(source, paths)) {
|
|
2018
|
+
const a = pathToAsset.get(p);
|
|
2019
|
+
if (a) a.unused = false;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
1803
2023
|
return json$2(res, 200, { assets });
|
|
1804
2024
|
}
|
|
1805
2025
|
if (fileMatch) {
|
|
@@ -2404,6 +2624,27 @@ function registerSlideRoutes(server, ctx) {
|
|
|
2404
2624
|
index: pageIndex
|
|
2405
2625
|
});
|
|
2406
2626
|
}
|
|
2627
|
+
const duplicateMatch = url.pathname.match(/^\/([^/]+)\/duplicate$/);
|
|
2628
|
+
if (duplicateMatch && method === "POST") {
|
|
2629
|
+
const requestCheck = validateMutationRequest(req);
|
|
2630
|
+
if (!requestCheck.ok) return json$2(res, requestCheck.status, { error: requestCheck.error });
|
|
2631
|
+
const slideId$1 = duplicateMatch[1];
|
|
2632
|
+
if (!SLIDE_ID_RE$3.test(slideId$1)) return json$2(res, 400, { error: "invalid slideId" });
|
|
2633
|
+
const body = await readBody$2(req);
|
|
2634
|
+
if (body.newId !== void 0 && typeof body.newId !== "string") return json$2(res, 400, { error: "invalid newId" });
|
|
2635
|
+
const duplicated = await duplicateSlideDir(ctx.slidesRoot, slideId$1, body.newId);
|
|
2636
|
+
if (!duplicated.ok) return json$2(res, duplicated.status, { error: duplicated.error });
|
|
2637
|
+
const manifest = await readManifest(ctx.manifestPath);
|
|
2638
|
+
const folderId = manifest.assignments[slideId$1];
|
|
2639
|
+
if (folderId) {
|
|
2640
|
+
manifest.assignments[duplicated.slideId] = folderId;
|
|
2641
|
+
await writeManifest(ctx.manifestPath, manifest);
|
|
2642
|
+
}
|
|
2643
|
+
return json$2(res, 200, {
|
|
2644
|
+
ok: true,
|
|
2645
|
+
slideId: duplicated.slideId
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2407
2648
|
const idMatch = url.pathname.match(/^\/([^/]+)$/);
|
|
2408
2649
|
if (!idMatch) return next();
|
|
2409
2650
|
const slideId = idMatch[1];
|
|
@@ -3102,7 +3343,10 @@ function locTagsPlugin(opts) {
|
|
|
3102
3343
|
transform(code, id) {
|
|
3103
3344
|
const filePath = id.split("?")[0];
|
|
3104
3345
|
if (!filePath.startsWith(slidesRoot + path.sep)) return null;
|
|
3105
|
-
if (!filePath.endsWith(
|
|
3346
|
+
if (!filePath.endsWith(".tsx")) return null;
|
|
3347
|
+
if (filePath.endsWith(".d.ts") || filePath.endsWith(".test.tsx")) return null;
|
|
3348
|
+
const rel = filePath.slice(slidesRoot.length + path.sep.length);
|
|
3349
|
+
if (!rel.includes(path.sep)) return null;
|
|
3106
3350
|
const next = injectLocTags(code);
|
|
3107
3351
|
if (next === null) return null;
|
|
3108
3352
|
return {
|
|
@@ -34,6 +34,7 @@ const en = {
|
|
|
34
34
|
home: {
|
|
35
35
|
appTitle: "open-slide",
|
|
36
36
|
draft: "Draft",
|
|
37
|
+
duplicate: "Duplicate",
|
|
37
38
|
themes: "Themes",
|
|
38
39
|
assets: "Assets",
|
|
39
40
|
folders: "Folders",
|
|
@@ -75,6 +76,8 @@ const en = {
|
|
|
75
76
|
deleteDialogDescriptionSuffix: "This action cannot be undone.",
|
|
76
77
|
toastFolderCreated: "Created folder “{name}”",
|
|
77
78
|
toastFolderCreateFailed: "Failed to create folder",
|
|
79
|
+
toastSlideDuplicated: "Duplicated “{slide}” as {newSlide}",
|
|
80
|
+
toastSlideDuplicateFailed: "Could not duplicate slide",
|
|
78
81
|
toastSlideMoved: "Moved “{slide}” to {folder}",
|
|
79
82
|
toastSlideMoveFailed: "Failed to move slide",
|
|
80
83
|
toastFolderDeleted: "Deleted folder “{name}”",
|
|
@@ -259,6 +262,7 @@ const en = {
|
|
|
259
262
|
one: "{count} file",
|
|
260
263
|
other: "{count} files"
|
|
261
264
|
},
|
|
265
|
+
usageUnused: "Unused",
|
|
262
266
|
searchLogos: "Search logos",
|
|
263
267
|
upload: "Upload",
|
|
264
268
|
dropToUpload: "Drop to upload",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Locale, Plural } from "./types-
|
|
2
|
-
import { OpenSlideConfig } from "./config-
|
|
1
|
+
import { Locale, Plural } from "./types-B-KrjgX8.js";
|
|
2
|
+
import { OpenSlideConfig } from "./config-CfMThYN9.js";
|
|
3
3
|
import { CSSProperties, ComponentType, HTMLAttributes } from "react";
|
|
4
4
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
5
|
|
|
@@ -45,9 +45,33 @@ declare function designToCssVars(d: DesignSystem): Record<string, string>;
|
|
|
45
45
|
declare function cssVarsToString(vars: Record<string, string>): string;
|
|
46
46
|
declare const defaultDesign: DesignSystem;
|
|
47
47
|
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/app/lib/page-context.d.ts
|
|
50
|
+
declare function useSlidePageNumber(): {
|
|
51
|
+
current: number;
|
|
52
|
+
total: number;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/app/lib/transition.d.ts
|
|
57
|
+
type TransitionPhase = {
|
|
58
|
+
keyframes: Keyframe[] | PropertyIndexedKeyframes;
|
|
59
|
+
easing?: string;
|
|
60
|
+
duration?: number;
|
|
61
|
+
delay?: number;
|
|
62
|
+
};
|
|
63
|
+
type SlideTransition = {
|
|
64
|
+
duration: number;
|
|
65
|
+
easing?: string;
|
|
66
|
+
enter?: TransitionPhase;
|
|
67
|
+
exit?: TransitionPhase;
|
|
68
|
+
};
|
|
69
|
+
|
|
48
70
|
//#endregion
|
|
49
71
|
//#region src/app/lib/sdk.d.ts
|
|
50
|
-
type Page = ComponentType
|
|
72
|
+
type Page = ComponentType & {
|
|
73
|
+
transition?: SlideTransition;
|
|
74
|
+
};
|
|
51
75
|
type SlideMeta = {
|
|
52
76
|
title?: string;
|
|
53
77
|
theme?: string;
|
|
@@ -59,9 +83,10 @@ type SlideModule = {
|
|
|
59
83
|
meta?: SlideMeta;
|
|
60
84
|
design?: DesignSystem;
|
|
61
85
|
notes?: (string | undefined)[];
|
|
86
|
+
transition?: SlideTransition;
|
|
62
87
|
};
|
|
63
88
|
declare const CANVAS_WIDTH = 1920;
|
|
64
89
|
declare const CANVAS_HEIGHT = 1080;
|
|
65
90
|
|
|
66
91
|
//#endregion
|
|
67
|
-
export { CANVAS_HEIGHT, CANVAS_WIDTH, DesignFonts, DesignPalette, DesignSystem, DesignTypeScale, ImagePlaceholder, ImagePlaceholderProps, Locale, OpenSlideConfig, Page, Plural, SlideMeta, SlideModule, cssVarsToString, defaultDesign, designToCssVars };
|
|
92
|
+
export { CANVAS_HEIGHT, CANVAS_WIDTH, DesignFonts, DesignPalette, DesignSystem, DesignTypeScale, ImagePlaceholder, ImagePlaceholderProps, Locale, OpenSlideConfig, Page, Plural, SlideMeta, SlideModule, SlideTransition, TransitionPhase, cssVarsToString, defaultDesign, designToCssVars, useSlidePageNumber };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { en } from "./en-
|
|
1
|
+
import { en } from "./en-BDnM5zKJ.js";
|
|
2
2
|
import { cssVarsToString, defaultDesign, designToCssVars } from "./design-cpzS8aud.js";
|
|
3
|
-
import { useRef, useState } from "react";
|
|
3
|
+
import { createContext, useContext, useRef, useState } from "react";
|
|
4
4
|
import { toast } from "sonner";
|
|
5
5
|
import config from "virtual:open-slide/config";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -43,7 +43,8 @@ async function uploadWithAutoRename(slideId, file) {
|
|
|
43
43
|
size: body?.size ?? uploaded.size,
|
|
44
44
|
mtime: body?.mtime ?? Date.now(),
|
|
45
45
|
mime: body?.mime ?? uploaded.type ?? "application/octet-stream",
|
|
46
|
-
url: body?.url ?? `/__assets/${slideId}/${encodeURIComponent(uploaded.name)}
|
|
46
|
+
url: body?.url ?? `/__assets/${slideId}/${encodeURIComponent(uploaded.name)}`,
|
|
47
|
+
unused: body?.unused ?? false
|
|
47
48
|
};
|
|
48
49
|
return {
|
|
49
50
|
ok: true,
|
|
@@ -296,10 +297,25 @@ function PlaceholderIcon() {
|
|
|
296
297
|
});
|
|
297
298
|
}
|
|
298
299
|
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/app/lib/page-context.tsx
|
|
302
|
+
const GLOBAL_KEY = "__open_slide_page_context__";
|
|
303
|
+
const g = globalThis;
|
|
304
|
+
if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = createContext(null);
|
|
305
|
+
const SlidePageContext = g[GLOBAL_KEY];
|
|
306
|
+
function useSlidePageNumber() {
|
|
307
|
+
const ctx = useContext(SlidePageContext);
|
|
308
|
+
if (!ctx) throw new Error("useSlidePageNumber must be called from a slide page rendered by @open-slide/core");
|
|
309
|
+
return {
|
|
310
|
+
current: ctx.index + 1,
|
|
311
|
+
total: ctx.total
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
299
315
|
//#endregion
|
|
300
316
|
//#region src/app/lib/sdk.ts
|
|
301
317
|
const CANVAS_WIDTH = 1920;
|
|
302
318
|
const CANVAS_HEIGHT = 1080;
|
|
303
319
|
|
|
304
320
|
//#endregion
|
|
305
|
-
export { CANVAS_HEIGHT, CANVAS_WIDTH, ImagePlaceholder, cssVarsToString, defaultDesign, designToCssVars };
|
|
321
|
+
export { CANVAS_HEIGHT, CANVAS_WIDTH, ImagePlaceholder, cssVarsToString, defaultDesign, designToCssVars, useSlidePageNumber };
|
package/dist/locale/index.d.ts
CHANGED
package/dist/locale/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { en } from "../en-
|
|
1
|
+
import { en } from "../en-BDnM5zKJ.js";
|
|
2
2
|
|
|
3
3
|
//#region src/locale/format.ts
|
|
4
4
|
function format(template, vars) {
|
|
@@ -48,6 +48,7 @@ const ja = {
|
|
|
48
48
|
home: {
|
|
49
49
|
appTitle: "open-slide",
|
|
50
50
|
draft: "下書き",
|
|
51
|
+
duplicate: "複製",
|
|
51
52
|
themes: "テーマ",
|
|
52
53
|
assets: "アセット",
|
|
53
54
|
folders: "フォルダ",
|
|
@@ -89,6 +90,8 @@ const ja = {
|
|
|
89
90
|
deleteDialogDescriptionSuffix: "この操作は元に戻せません。",
|
|
90
91
|
toastFolderCreated: "フォルダ「{name}」を作成しました",
|
|
91
92
|
toastFolderCreateFailed: "フォルダの作成に失敗しました",
|
|
93
|
+
toastSlideDuplicated: "「{slide}」を {newSlide} として複製しました",
|
|
94
|
+
toastSlideDuplicateFailed: "スライドを複製できませんでした",
|
|
92
95
|
toastSlideMoved: "「{slide}」を {folder} に移動しました",
|
|
93
96
|
toastSlideMoveFailed: "スライドの移動に失敗しました",
|
|
94
97
|
toastFolderDeleted: "フォルダ「{name}」を削除しました",
|
|
@@ -273,6 +276,7 @@ const ja = {
|
|
|
273
276
|
one: "ファイル {count} 件",
|
|
274
277
|
other: "ファイル {count} 件"
|
|
275
278
|
},
|
|
279
|
+
usageUnused: "未使用",
|
|
276
280
|
searchLogos: "ロゴを検索",
|
|
277
281
|
upload: "アップロード",
|
|
278
282
|
dropToUpload: "ドロップでアップロード",
|
|
@@ -421,6 +425,7 @@ const zhCN = {
|
|
|
421
425
|
home: {
|
|
422
426
|
appTitle: "open-slide",
|
|
423
427
|
draft: "草稿",
|
|
428
|
+
duplicate: "复制",
|
|
424
429
|
themes: "主题",
|
|
425
430
|
assets: "素材",
|
|
426
431
|
folders: "文件夹",
|
|
@@ -462,6 +467,8 @@ const zhCN = {
|
|
|
462
467
|
deleteDialogDescriptionSuffix: "此操作无法撤销。",
|
|
463
468
|
toastFolderCreated: "已创建文件夹\"{name}\"",
|
|
464
469
|
toastFolderCreateFailed: "创建文件夹失败",
|
|
470
|
+
toastSlideDuplicated: "已将\"{slide}\"复制为 {newSlide}",
|
|
471
|
+
toastSlideDuplicateFailed: "无法复制幻灯片",
|
|
465
472
|
toastSlideMoved: "已将\"{slide}\"移至 {folder}",
|
|
466
473
|
toastSlideMoveFailed: "移动幻灯片失败",
|
|
467
474
|
toastFolderDeleted: "已删除文件夹\"{name}\"",
|
|
@@ -646,6 +653,7 @@ const zhCN = {
|
|
|
646
653
|
one: "{count} 个文件",
|
|
647
654
|
other: "{count} 个文件"
|
|
648
655
|
},
|
|
656
|
+
usageUnused: "未使用",
|
|
649
657
|
searchLogos: "搜索 Logo",
|
|
650
658
|
upload: "上传",
|
|
651
659
|
dropToUpload: "拖入即可上传",
|
|
@@ -794,6 +802,7 @@ const zhTW = {
|
|
|
794
802
|
home: {
|
|
795
803
|
appTitle: "open-slide",
|
|
796
804
|
draft: "草稿",
|
|
805
|
+
duplicate: "複製",
|
|
797
806
|
themes: "主題",
|
|
798
807
|
assets: "素材",
|
|
799
808
|
folders: "資料夾",
|
|
@@ -835,6 +844,8 @@ const zhTW = {
|
|
|
835
844
|
deleteDialogDescriptionSuffix: "此操作無法復原。",
|
|
836
845
|
toastFolderCreated: "已建立資料夾「{name}」",
|
|
837
846
|
toastFolderCreateFailed: "建立資料夾失敗",
|
|
847
|
+
toastSlideDuplicated: "已將「{slide}」複製為 {newSlide}",
|
|
848
|
+
toastSlideDuplicateFailed: "無法複製投影片",
|
|
838
849
|
toastSlideMoved: "已將「{slide}」移至 {folder}",
|
|
839
850
|
toastSlideMoveFailed: "移動投影片失敗",
|
|
840
851
|
toastFolderDeleted: "已刪除資料夾「{name}」",
|
|
@@ -1019,6 +1030,7 @@ const zhTW = {
|
|
|
1019
1030
|
one: "{count} 個檔案",
|
|
1020
1031
|
other: "{count} 個檔案"
|
|
1021
1032
|
},
|
|
1033
|
+
usageUnused: "未使用",
|
|
1022
1034
|
searchLogos: "搜尋 Logo",
|
|
1023
1035
|
upload: "上傳",
|
|
1024
1036
|
dropToUpload: "拖入即可上傳",
|
|
@@ -38,6 +38,7 @@ type Locale = {
|
|
|
38
38
|
home: {
|
|
39
39
|
appTitle: string;
|
|
40
40
|
draft: string;
|
|
41
|
+
duplicate: string;
|
|
41
42
|
themes: string;
|
|
42
43
|
assets: string;
|
|
43
44
|
folders: string;
|
|
@@ -80,6 +81,9 @@ type Locale = {
|
|
|
80
81
|
/** template: "Created folder “{name}”" */
|
|
81
82
|
toastFolderCreated: string;
|
|
82
83
|
toastFolderCreateFailed: string;
|
|
84
|
+
/** template: "Duplicated “{slide}” as {newSlide}" */
|
|
85
|
+
toastSlideDuplicated: string;
|
|
86
|
+
toastSlideDuplicateFailed: string;
|
|
83
87
|
/** template: "Moved “{slide}” to {folder}" */
|
|
84
88
|
toastSlideMoved: string;
|
|
85
89
|
toastSlideMoveFailed: string;
|
|
@@ -265,6 +269,7 @@ type Locale = {
|
|
|
265
269
|
scopeGlobal: string;
|
|
266
270
|
/** templates: "{count} file" / "{count} files" */
|
|
267
271
|
fileCount: Plural;
|
|
272
|
+
usageUnused: string;
|
|
268
273
|
searchLogos: string;
|
|
269
274
|
upload: string;
|
|
270
275
|
dropToUpload: string;
|
package/dist/vite/index.d.ts
CHANGED
package/dist/vite/index.js
CHANGED