diff --git a/.tool-versions b/.tool-versions index 22e80a2..24918a0 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -bun 1.1.18 +bun 1.1.45 diff --git a/next/bun.lockb b/next/bun.lockb index 9cf5f01..31809aa 100755 Binary files a/next/bun.lockb and b/next/bun.lockb differ diff --git a/next/next-env.d.ts b/next/next-env.d.ts index 4f11a03..52e831b 100644 --- a/next/next-env.d.ts +++ b/next/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/next/package.json b/next/package.json index 211d8e8..8797b38 100644 --- a/next/package.json +++ b/next/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { + "assets": "node src/assets/assets-index-gen.mjs", "dev": "bunx next dev", "build": "bunx next build", "start": "bunx next start" @@ -10,10 +11,10 @@ "browserslist": "defaults, not ie <= 11", "dependencies": { "@algolia/autocomplete-core": "^1.17.2", - "@headlessui/react": "^2.1.2", + "@headlessui/react": "^2.2.0", "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^3.0.1", - "@next/mdx": "^14.2.4", + "@next/mdx": "^15.0.4", "@sindresorhus/slugify": "^2.1.1", "@tailwindcss/typography": "^0.5.13", "acorn": "^8.12.1", @@ -22,14 +23,14 @@ "fast-glob": "^3.2.12", "flexsearch": "^0.7.43", "focus-visible": "^5.2.0", - "framer-motion": "^11.2.13", + "framer-motion": "^12.0.0-alpha.2", "mdast-util-to-string": "^4.0.0", "mdx-annotations": "^0.1.4", - "next": "^14.2.4", + "next": "^15.0.4", "postcss-focus-visible": "^9.0.1", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-highlight-words": "^0.20.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-highlight-words": "^0.21.0", "recma-nextjs-static-props": "^2.0.1", "rehype-mdx-title": "^3.1.0", "remark": "^15.0.1", @@ -43,8 +44,9 @@ "zustand": "^4.5.4" }, "devDependencies": { + "@types/node": "22.10.7", "prettier": "^3.3.2", "prettier-plugin-tailwindcss": "^0.6.5", - "typescript": "5.5.3" + "typescript": "^5.7.2" } } diff --git a/next/src/assets-index-gen.mjs b/next/src/assets-index-gen.mjs new file mode 100644 index 0000000..a4bb831 --- /dev/null +++ b/next/src/assets-index-gen.mjs @@ -0,0 +1,69 @@ +import fs from 'fs'; +import path from 'path'; +import sharp from 'sharp'; + +function getAllFiles(dirPath, arrayOfFiles) { + const files = fs.readdirSync(dirPath); + + arrayOfFiles = arrayOfFiles || []; + + files.forEach(function (file) { + if (fs.statSync(dirPath + "/" + file).isDirectory()) { + arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles); + } else { + arrayOfFiles.push(path.join(dirPath, "/", file)); + } + }); + + return arrayOfFiles; +} + +const publicDir = path.join(process.cwd(), 'public'); +const allAssets = getAllFiles(publicDir); +const allAssetEntries = allAssets.reduce((acc, file) => { + const src = path.relative(publicDir, file).replaceAll('\\', '/'); + acc[src] = { + ext: path.extname(file).toLowerCase().slice(1) + }; + return acc; +}, {}); +const allAssetEntriesWithImageSizes = await Promise.all(Object.entries(allAssetEntries).map(async ([src, asset]) => { + if (asset.ext === 'png') { + try { + const fullPath = path.join(publicDir, src); + const metadata = await sharp(fullPath).metadata(); + return [src, { + ...asset, + width: metadata.width, + height: metadata.height + }]; + } catch (error) { + console.error(`Error processing ${src}:`, error); + return [src, asset]; + } + } + return [src, asset]; +})); +const allAssetEntriesWithImageSizesMap = Object.fromEntries(allAssetEntriesWithImageSizes); + +function sortKeys(obj) { + if (typeof obj !== 'object' || obj === null) return obj; + + if (Array.isArray(obj)) { + return obj.map(sortKeys); + } + + return Object.keys(obj).sort().reduce((sorted, key) => { + sorted[key] = sortKeys(obj[key]); + return sorted; + }, {}); +} + +fs.writeFileSync( + path.join(process.cwd(), 'src/assets', 'assets-index.json'), + JSON.stringify(sortKeys({ + assets: allAssetEntriesWithImageSizesMap + }), null, 2) +); + +console.log('Valid assets list generated successfully.'); \ No newline at end of file diff --git a/next/src/assets-index.json b/next/src/assets-index.json new file mode 100644 index 0000000..bc82d64 --- /dev/null +++ b/next/src/assets-index.json @@ -0,0 +1,748 @@ +{ + "assets": { + "command-line/install-win-set-path.png": { + "ext": "png", + "width": 820, + "height": 1120 + }, + "command-line/install-win-set-path.svg": { + "ext": "svg" + }, + "command-line/install-win-system.png": { + "ext": "png", + "width": 431, + "height": 680 + }, + "command-line/log-all-calls-to-dp-open.png": { + "ext": "png", + "width": 229, + "height": 129 + }, + "command-line/log-all-calls-to-dp.png": { + "ext": "png", + "width": 586, + "height": 434 + }, + "command-line/perforce-diff.png": { + "ext": "png", + "width": 790, + "height": 495 + }, + "command-line/perforce-merge.png": { + "ext": "png", + "width": 790, + "height": 495 + }, + "command-line/perforce-view.png": { + "ext": "png", + "width": 790, + "height": 495 + }, + "command-line/sourcetree-external-diff.png": { + "ext": "png", + "width": 439, + "height": 181 + }, + "command-line/sourcetree-options-diff-tab.png": { + "ext": "png", + "width": 681, + "height": 463 + }, + "command-line/sourcetree-tools-options.png": { + "ext": "png", + "width": 495, + "height": 134 + }, + "command-line/tortoisegit-diff.png": { + "ext": "png", + "width": 773, + "height": 612 + }, + "command-line/tortoisegit-merge.png": { + "ext": "png", + "width": 773, + "height": 612 + }, + "command-line/tortoisesvn-diff.png": { + "ext": "png", + "width": 773, + "height": 536 + }, + "command-line/tortoisesvn-merge.png": { + "ext": "png", + "width": 773, + "height": 536 + }, + "config-library/config.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "config-library/delete.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "config-library/folder-diff-builtin-presets.mp4": { + "ext": "mp4" + }, + "config-library/folder-diff-builtin-presets.png": { + "ext": "png", + "width": 508, + "height": 363 + }, + "config-library/folder-diff-content-based.mp4": { + "ext": "mp4" + }, + "config-library/folder-diff-content-based.png": { + "ext": "png", + "width": 508, + "height": 363 + }, + "config-library/save.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "config-library/simulink-diff-config.png": { + "ext": "png", + "width": 874, + "height": 715 + }, + "custom-masks/custom-masks-dialog.mp4": { + "ext": "mp4" + }, + "custom-masks/custom-masks-dialog.png": { + "ext": "png", + "width": 586, + "height": 443 + }, + "custom-masks/custom-masks-right-click.png": { + "ext": "png", + "width": 324, + "height": 246 + }, + "error-logging/error-log.png": { + "ext": "png", + "width": 618, + "height": 394 + }, + "error-logging/error-typical.png": { + "ext": "png", + "width": 620, + "height": 387 + }, + "error-logging/stacktrace-dialog.png": { + "ext": "png", + "width": 545, + "height": 421 + }, + "favicon.ico": { + "ext": "ico" + }, + "folder/3way.png": { + "ext": "png", + "width": 581, + "height": 413 + }, + "folder/differ.png": { + "ext": "png", + "width": 589, + "height": 397 + }, + "folder/viewer.png": { + "ext": "png", + "width": 589, + "height": 397 + }, + "generate-report/simulink-generate-report-config.png": { + "ext": "png", + "width": 359, + "height": 269 + }, + "generate-report/simulink-generate-report-first-page-word.png": { + "ext": "png", + "width": 953, + "height": 821 + }, + "generate-report/simulink-generate-report-image-per-difference.png": { + "ext": "png", + "width": 954, + "height": 759 + }, + "image/Adjacent.png": { + "ext": "png", + "width": 32, + "height": 16 + }, + "image/ImageViewerDiffer-compare-modes.png": { + "ext": "png", + "width": 97, + "height": 109 + }, + "image/ImageViewerDiffer-config.png": { + "ext": "png", + "width": 600, + "height": 400 + }, + "image/ImageViewerDiffer-flip1.png": { + "ext": "png", + "width": 142, + "height": 253 + }, + "image/ImageViewerDiffer-flip2.png": { + "ext": "png", + "width": 785, + "height": 350 + }, + "image/ImageViewerDiffer-image3.png": { + "ext": "png", + "width": 735, + "height": 336 + }, + "image/ImageViewerDiffer-image4.png": { + "ext": "png", + "width": 735, + "height": 336 + }, + "image/ImageViewerDiffer-image5.png": { + "ext": "png", + "width": 615, + "height": 336 + }, + "image/ImageViewerDiffer-image6.png": { + "ext": "png", + "width": 735, + "height": 342 + }, + "image/ImageViewerDiffer-image7.png": { + "ext": "png", + "width": 735, + "height": 263 + }, + "image/ImageViewerDiffer-viewer.png": { + "ext": "png", + "width": 878, + "height": 572 + }, + "image/ImageViewerDiffer-wipe-horiz.png": { + "ext": "png", + "width": 785, + "height": 350 + }, + "image/ImageViewerDiffer-wipe-vert.png": { + "ext": "png", + "width": 785, + "height": 350 + }, + "image/InstallerAdvanced.png": { + "ext": "png", + "width": 750, + "height": 525 + }, + "image/MatchPoints.png": { + "ext": "png", + "width": 32, + "height": 16 + }, + "image/Overlap.png": { + "ext": "png", + "width": 32, + "height": 16 + }, + "image/ZoomFitBoth.png": { + "ext": "png", + "width": 32, + "height": 16 + }, + "installation/install-mac-dmg.png": { + "ext": "png", + "width": 704, + "height": 567 + }, + "keyring/command-console-icon.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "keyring/credential-request-key-selected.png": { + "ext": "png", + "width": 466, + "height": 130 + }, + "keyring/credential-request-otherurl.png": { + "ext": "png", + "width": 466, + "height": 130 + }, + "keyring/credential-request-two-options.png": { + "ext": "png", + "width": 554, + "height": 130 + }, + "keyring/credential-request-use-keyring.png": { + "ext": "png", + "width": 466, + "height": 130 + }, + "keyring/credential-request.png": { + "ext": "png", + "width": 466, + "height": 130 + }, + "keyring/keyring-add-key-typed.png": { + "ext": "png", + "width": 354, + "height": 145 + }, + "keyring/keyring-add-key.png": { + "ext": "png", + "width": 354, + "height": 145 + }, + "keyring/keyring-add-testcanary.png": { + "ext": "png", + "width": 286, + "height": 145 + }, + "keyring/keyring-change-master-password.png": { + "ext": "png", + "width": 370, + "height": 440 + }, + "keyring/keyring-not-setup.png": { + "ext": "png", + "width": 312, + "height": 62 + }, + "keyring/keyring-setup.png": { + "ext": "png", + "width": 312, + "height": 404 + }, + "library-links/library-link-browser.png": { + "ext": "png", + "width": 586, + "height": 443 + }, + "library-links/library-link-diff.png": { + "ext": "png", + "width": 574, + "height": 155 + }, + "library-links/library-link-menu.png": { + "ext": "png", + "width": 322, + "height": 272 + }, + "library-links/library-link-options.svg": { + "ext": "svg" + }, + "library-links/library-link-subdiff-options.png": { + "ext": "png", + "width": 784, + "height": 398 + }, + "library-links/library-link-subdiff-options.svg": { + "ext": "svg" + }, + "library-links/library-link-subdiff-setup.png": { + "ext": "png", + "width": 901, + "height": 636 + }, + "license-manager/activated-float_2x.png": { + "ext": "png", + "width": 1290, + "height": 574 + }, + "license-manager/activated-locked_2x.png": { + "ext": "png", + "width": 1290, + "height": 574 + }, + "license-manager/copy-paste-finish_2x.png": { + "ext": "png", + "width": 1290, + "height": 696 + }, + "license-manager/copy-paste-start_2x.png": { + "ext": "png", + "width": 2344, + "height": 1510 + }, + "license-manager/emergency-activation_2x.png": { + "ext": "png", + "width": 1060, + "height": 694 + }, + "license-manager/get-license_2x.png": { + "ext": "png", + "width": 1290, + "height": 542 + }, + "license-manager/hamburger_2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "license-manager/how-to-open_2x.png": { + "ext": "png", + "width": 908, + "height": 592 + }, + "license-manager/missing-license_2x.png": { + "ext": "png", + "width": 1798, + "height": 870 + }, + "license-manager/more-options_2x.png": { + "ext": "png", + "width": 1290, + "height": 542 + }, + "license-manager/proxy-dialog_2x.png": { + "ext": "png", + "width": 1456, + "height": 596 + }, + "mat-file/mat-differ-addition.png": { + "ext": "png", + "width": 791, + "height": 182 + }, + "mat-file/mat-differ.png": { + "ext": "png", + "width": 806, + "height": 544 + }, + "mat-file/mat-viewer-struct-and-cell.png": { + "ext": "png", + "width": 340, + "height": 242 + }, + "mat-file/mat-viewer.png": { + "ext": "png", + "width": 573, + "height": 473 + }, + "quickstart/DiffPlugLogo_16.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "quickstart/command-console.mp4": { + "ext": "mp4" + }, + "quickstart/command-console.png": { + "ext": "png", + "width": 589, + "height": 394 + }, + "quickstart/diff-list.mp4": { + "ext": "mp4" + }, + "quickstart/diff-list.png": { + "ext": "png", + "width": 589, + "height": 394 + }, + "quickstart/diff-schemas.mp4": { + "ext": "mp4" + }, + "quickstart/diff-schemas.png": { + "ext": "png", + "width": 587, + "height": 394 + }, + "quickstart/entity-console.mp4": { + "ext": "mp4" + }, + "quickstart/entity-console.png": { + "ext": "png", + "width": 589, + "height": 394 + }, + "signal-tracing/trace-input-extend.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-input-extended-1.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-input-extended-2.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-input-start.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-input-traced.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-output-followed.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-output-start.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "signal-tracing/trace-output-traced.png": { + "ext": "png", + "width": 901, + "height": 639 + }, + "simulink/icons/arrow_up.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/arrow_up@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/ignore_one.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/ignore_one@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/log.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/log@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/reference_16.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/reference_16@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/split_monitors.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/split_monitors@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/split_vertical.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/split_vertical@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/toggle_colors.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/toggle_colors@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/zoom_100.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/zoom_100@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/zoom_fit.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/zoom_fit@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/zoom_in.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/zoom_in@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/icons/zoom_out.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "simulink/icons/zoom_out@2x.png": { + "ext": "png", + "width": 32, + "height": 32 + }, + "simulink/simulink-differ-config-btn.png": { + "ext": "png", + "width": 346, + "height": 82 + }, + "simulink/simulink-differ-config-gainonly.png": { + "ext": "png", + "width": 550, + "height": 389 + }, + "simulink/simulink-differ-config.png": { + "ext": "png", + "width": 550, + "height": 380 + }, + "simulink/simulink-differ-simulink.png": { + "ext": "png", + "width": 898, + "height": 633 + }, + "simulink/simulink-differ-stateflow-ignore-result.png": { + "ext": "png", + "width": 898, + "height": 633 + }, + "simulink/simulink-differ-stateflow-ignore.png": { + "ext": "png", + "width": 420, + "height": 84 + }, + "simulink/simulink-differ-stateflow.png": { + "ext": "png", + "width": 898, + "height": 633 + }, + "simulink/simulink-viewer-eml.png": { + "ext": "png", + "width": 898, + "height": 632 + }, + "simulink/simulink-viewer-matdata.png": { + "ext": "png", + "width": 898, + "height": 633 + }, + "simulink/simulink-viewer-simulink.png": { + "ext": "png", + "width": 898, + "height": 632 + }, + "simulink/simulink-viewer-stateflow.png": { + "ext": "png", + "width": 898, + "height": 633 + }, + "text/CustomSyntax-results-all.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-results-endofline.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-results-keyword.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-results-multiline.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-results-none.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-results-singleline.png": { + "ext": "png", + "width": 194, + "height": 100 + }, + "text/CustomSyntax-rules-endofline.png": { + "ext": "png", + "width": 462, + "height": 100 + }, + "text/CustomSyntax-rules-keyword.png": { + "ext": "png", + "width": 462, + "height": 100 + }, + "text/CustomSyntax-rules-multiline.png": { + "ext": "png", + "width": 462, + "height": 100 + }, + "text/CustomSyntax-rules-singleline.png": { + "ext": "png", + "width": 462, + "height": 100 + }, + "text/CustomSyntax-syntaxmanager-annotated.png": { + "ext": "png", + "width": 887, + "height": 655 + }, + "text/CustomSyntaxManager-syntaxmanager.svg": { + "ext": "svg" + }, + "text/differ.png": { + "ext": "png", + "width": 896, + "height": 631 + }, + "text/viewer.png": { + "ext": "png", + "width": 896, + "height": 631 + }, + "track-in-matlab/track-in-matlab-demo.mp4": { + "ext": "mp4" + }, + "track-in-matlab/track-in-matlab-demo.png": { + "ext": "png", + "width": 1298, + "height": 644 + } + } +} \ No newline at end of file diff --git a/next/src/assets.mjs b/next/src/assets.mjs new file mode 100644 index 0000000..9bb8cf1 --- /dev/null +++ b/next/src/assets.mjs @@ -0,0 +1,42 @@ +import assetsIndex from './assets-index.json' assert { type: 'json' }; + +export function assetPathFor(asset) { + if (asset.startsWith('/')) { + throw Error(`Asset should not start with '/': ${asset}`) + } + if (asset.indexOf("://") != -1) { + throw Error(`Asset should not contain '://': ${asset}`) + } + + if (!assetsIndex.assets[asset]) { + throw Error(`No such asset found: ${asset}`) + } + + const assetPrefix = process.env.NEXT_PUBLIC_BUILD_PREFIX || ''; + + // TODO: Implement asset fingerprinting + // const fingerprint = getAssetFingerprint(asset); + + // TODO: Track asset usage + // trackAssetUsage(asset); + + return `${assetPrefix}/${asset}`; +} + +function assertNumber(n) { + if (typeof n !== 'number' || isNaN(n)) { + throw new Error(`Expected to be a number: ${n}`); + } + return n +} +function isHiDpi(asset) { + return asset.indexOf("_2x.") != -1 +} +export function assetW(asset) { + const raw = assertNumber(assetsIndex.assets[asset].width) + return isHiDpi(asset) ? raw / 2 : raw +} +export function assetH(asset) { + const raw = assertNumber(assetsIndex.assets[asset].height) + return isHiDpi(asset) ? raw / 2 : raw +} \ No newline at end of file diff --git a/next/src/assets/assets-index-gen.mjs b/next/src/assets/assets-index-gen.mjs new file mode 100644 index 0000000..fa9fea6 --- /dev/null +++ b/next/src/assets/assets-index-gen.mjs @@ -0,0 +1,56 @@ +import fs from 'fs'; +import path from 'path'; +import sharp from 'sharp'; + +function getAllFiles(dirPath, arrayOfFiles) { + const files = fs.readdirSync(dirPath); + + arrayOfFiles = arrayOfFiles || []; + + files.forEach(function (file) { + if (fs.statSync(dirPath + "/" + file).isDirectory()) { + arrayOfFiles = getAllFiles(dirPath + "/" + file, arrayOfFiles); + } else { + arrayOfFiles.push(path.join(dirPath, "/", file)); + } + }); + + return arrayOfFiles; +} + +const publicDir = path.join(process.cwd(), 'public'); +const allAssets = getAllFiles(publicDir); +const allAssetEntries = allAssets.reduce((acc, file) => { + const src = path.relative(publicDir, file).replaceAll('\\', '/'); + acc[src] = { + ext: path.extname(file).toLowerCase().slice(1) + }; + return acc; +}, {}); +const allAssetEntriesWithImageSizes = await Promise.all(Object.entries(allAssetEntries).map(async ([src, asset]) => { + if (asset.ext === 'png') { + try { + const fullPath = path.join(publicDir, src); + const metadata = await sharp(fullPath).metadata(); + return [src, { + ...asset, + width: metadata.width, + height: metadata.height + }]; + } catch (error) { + console.error(`Error processing ${src}:`, error); + return [src, asset]; + } + } + return [src, asset]; +})); +const allAssetEntriesWithImageSizesMap = Object.fromEntries(allAssetEntriesWithImageSizes); + +fs.writeFileSync( + path.join(process.cwd(), 'src/assets', 'assets-index.json'), + JSON.stringify({ + assets: allAssetEntriesWithImageSizesMap + }, null, 2) +); + +console.log('Valid assets list generated successfully.'); \ No newline at end of file diff --git a/next/src/assets/assets-index.json b/next/src/assets/assets-index.json new file mode 100644 index 0000000..fa044c6 --- /dev/null +++ b/next/src/assets/assets-index.json @@ -0,0 +1,552 @@ +{ + "assets": { + "branches/reflog/reflog-branch.mp4": { + "ext": "mp4" + }, + "branches/reflog/reflog-branch.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/reflog/reflog-delete.mp4": { + "ext": "mp4" + }, + "branches/reflog/reflog-delete.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/reflog/reflog-head.mp4": { + "ext": "mp4" + }, + "branches/reflog/reflog-head.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/save-for-later/save-all-for-later.png": { + "ext": "png", + "width": 206, + "height": 95 + }, + "branches/save-for-later/save-for-later.mp4": { + "ext": "mp4" + }, + "branches/save-for-later/save-for-later.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/sticky-notes-and-paintbrushes/branch-create-fork.mp4": { + "ext": "mp4" + }, + "branches/sticky-notes-and-paintbrushes/branch-create-fork.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/sticky-notes-and-paintbrushes/branch-drag.mp4": { + "ext": "mp4" + }, + "branches/sticky-notes-and-paintbrushes/branch-drag.png": { + "ext": "png", + "width": 509, + "height": 309 + }, + "branches/sticky-notes-and-paintbrushes/branch-hover-vs-click.mp4": { + "ext": "mp4" + }, + "branches/sticky-notes-and-paintbrushes/branch-hover-vs-click.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "branches/sticky-notes-and-paintbrushes/local-branch-remote-branch-tag.png": { + "ext": "png", + "width": 210, + "height": 18 + }, + "branches/sticky-notes-and-paintbrushes/local-branch.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "branches/sticky-notes-and-paintbrushes/remote-branch.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "branches/sticky-notes-and-paintbrushes/simple-branch.png": { + "ext": "png", + "width": 120, + "height": 134 + }, + "branches/sticky-notes-and-paintbrushes/tag.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "epilogue/departures/no-index.png": { + "ext": "png", + "width": 253, + "height": 205 + }, + "epilogue/departures/stash.png": { + "ext": "png", + "width": 323, + "height": 49 + }, + "intro/clone/clone.mp4": { + "ext": "mp4" + }, + "intro/clone/clone.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/command-console.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "intro/commit/commit-browse-commits.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-browse-commits.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-browse-files.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-browse-files.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-edit.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-edit.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-goto-wc.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-goto-wc.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-make.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-make.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-open-folder.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-open-folder.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-set-username.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-set-username.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/commit/commit-who-when-what-why.mp4": { + "ext": "mp4" + }, + "intro/commit/commit-who-when-what-why.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/diff-list-icon.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "intro/init/init.mp4": { + "ext": "mp4" + }, + "intro/init/init.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "intro/open-working-copy.mp4": { + "ext": "mp4" + }, + "intro/open-working-copy.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "share/branches/case-both-actions.png": { + "ext": "png", + "width": 485, + "height": 94 + }, + "share/branches/case-both.png": { + "ext": "png", + "width": 135, + "height": 94 + }, + "share/branches/case-local-new-actions.png": { + "ext": "png", + "width": 485, + "height": 50 + }, + "share/branches/case-local-new.png": { + "ext": "png", + "width": 116, + "height": 56 + }, + "share/branches/case-local-only-actions.png": { + "ext": "png", + "width": 485, + "height": 28 + }, + "share/branches/case-local-only.png": { + "ext": "png", + "width": 82, + "height": 18 + }, + "share/branches/case-remote-new-actions.png": { + "ext": "png", + "width": 485, + "height": 72 + }, + "share/branches/case-remote-new.png": { + "ext": "png", + "width": 135, + "height": 56 + }, + "share/branches/case-remote-only-actions.png": { + "ext": "png", + "width": 485, + "height": 72 + }, + "share/branches/case-remote-only.png": { + "ext": "png", + "width": 116, + "height": 18 + }, + "share/branches/case-synced-actions.png": { + "ext": "png", + "width": 485, + "height": 28 + }, + "share/branches/case-synced.png": { + "ext": "png", + "width": 180, + "height": 18 + }, + "share/branches/share-branches.mp4": { + "ext": "mp4" + }, + "share/branches/share-branches.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "share/remotes/remotes-add.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "share/remotes/remotes-browse.mp4": { + "ext": "mp4" + }, + "share/remotes/remotes-browse.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "share/remotes/remotes-local.mp4": { + "ext": "mp4" + }, + "share/remotes/remotes-local.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "share/tags/tag-dialog-annotated.png": { + "ext": "png", + "width": 564, + "height": 230 + }, + "share/tags/tag-dialog-lightweight.png": { + "ext": "png", + "width": 564, + "height": 230 + }, + "share/tags/tag-view-annotated.png": { + "ext": "png", + "width": 365, + "height": 91 + }, + "share/tags/tag-view-lightweight.png": { + "ext": "png", + "width": 365, + "height": 91 + }, + "time-travel/cherry-pick-and-rebase/TODO.png": { + "ext": "png", + "width": 120, + "height": 134 + }, + "time-travel/cherry-pick-and-rebase/after-rebase.png": { + "ext": "png", + "width": 125, + "height": 153 + }, + "time-travel/cherry-pick-and-rebase/apply_delta.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/cherry-pick-and-rebase/apply_file.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/cherry-pick-and-rebase/before-rebase.png": { + "ext": "png", + "width": 125, + "height": 153 + }, + "time-travel/cherry-pick-and-rebase/blank_file.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/cherry-pick-and-rebase/blank_folder.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/cherry-pick-and-rebase/commit.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/cherry-pick-and-rebase/rebase-actual.mp4": { + "ext": "mp4" + }, + "time-travel/cherry-pick-and-rebase/rebase-actual.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/cherry-pick-and-rebase/rebase-by-cherry-picking.mp4": { + "ext": "mp4" + }, + "time-travel/cherry-pick-and-rebase/rebase-by-cherry-picking.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/cherry-pick-and-rebase/rebase-compare-to-merge.mp4": { + "ext": "mp4" + }, + "time-travel/cherry-pick-and-rebase/rebase-compare-to-merge.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/cherry-pick-and-rebase/rebase.png": { + "ext": "png", + "width": 16, + "height": 16 + }, + "time-travel/merge/merge-brainstorm.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-brainstorm.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/merge/merge-conflict.png": { + "ext": "png", + "width": 241, + "height": 78 + }, + "time-travel/merge/merge-create.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-create.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/merge/merge-pick-parent.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-pick-parent.png": { + "ext": "png", + "width": 620, + "height": 620 + }, + "time-travel/merge/merge-squash-vs-summary.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-squash-vs-summary.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/merge/merge-squash.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-squash.png": { + "ext": "png", + "width": 620, + "height": 620 + }, + "time-travel/merge/merge-summary.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-summary.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/merge/merge-undo.mp4": { + "ext": "mp4" + }, + "time-travel/merge/merge-undo.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/patch/TODO.png": { + "ext": "png", + "width": 120, + "height": 134 + }, + "time-travel/patch/animals-patch.png": { + "ext": "png", + "width": 620, + "height": 285 + }, + "time-travel/patch/animals-split.png": { + "ext": "png", + "width": 620, + "height": 298 + }, + "time-travel/patch/patch-c-after.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-c-before.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-c-diff.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-conflict-before.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-docs-after.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-docs-before.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-docs-diff.png": { + "ext": "png", + "width": 136, + "height": 78 + }, + "time-travel/patch/patch-dog-then-whale.png": { + "ext": "png", + "width": 620, + "height": 558 + }, + "time-travel/patch/patch-whale-then-dog.png": { + "ext": "png", + "width": 620, + "height": 558 + }, + "time-travel/patch/zip-animals-patch-shared.png": { + "ext": "png", + "width": 620, + "height": 320 + }, + "time-travel/patch/zip-animals-patch.png": { + "ext": "png", + "width": 620, + "height": 285 + }, + "time-travel/patch/zip-animals-split.png": { + "ext": "png", + "width": 620, + "height": 298 + }, + "time-travel/rewrite-history/rewrite-reorder-and-omit.mp4": { + "ext": "mp4" + }, + "time-travel/rewrite-history/rewrite-reorder-and-omit.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/rewrite-history/rewrite-setup.mp4": { + "ext": "mp4" + }, + "time-travel/rewrite-history/rewrite-setup.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/rewrite-history/rewrite-split-and-combine.mp4": { + "ext": "mp4" + }, + "time-travel/rewrite-history/rewrite-split-and-combine.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/rewrite-history/rewrite-summary-merge.mp4": { + "ext": "mp4" + }, + "time-travel/rewrite-history/rewrite-summary-merge.png": { + "ext": "png", + "width": 620, + "height": 460 + }, + "time-travel/rewrite-history/squash.mp4": { + "ext": "mp4" + }, + "time-travel/rewrite-history/squash.png": { + "ext": "png", + "width": 620, + "height": 460 + } + } +} \ No newline at end of file diff --git a/next/src/assets/assets.mjs b/next/src/assets/assets.mjs new file mode 100644 index 0000000..9abb85d --- /dev/null +++ b/next/src/assets/assets.mjs @@ -0,0 +1,48 @@ +import assetsIndex from './assets-index.json' assert { type: 'json' }; + +export function assetPathFor(asset) { + if (asset.startsWith('/')) { + throw Error(`Asset should not start with '/': ${asset}`) + } + if (asset.indexOf("://") != -1) { + throw Error(`Asset should not contain '://': ${asset}`) + } + + if (!assetsIndex.assets[asset]) { + throw Error(`No such asset found: ${asset}`) + } + + const assetPrefix = process.env.NEXT_PUBLIC_BUILD_PREFIX || ''; + + // TODO: Implement asset fingerprinting + // const fingerprint = getAssetFingerprint(asset); + + // TODO: Track asset usage + // trackAssetUsage(asset); + + return `${assetPrefix}/${asset}`; +} + +function assertNumber(n) { + if (typeof n !== 'number' || isNaN(n)) { + throw new Error(`Expected to be a number: ${n}`); + } + return n +} +function isHiDpi(asset) { + return asset.indexOf("_2x.") != -1 +} +export function assetW(asset) { + if (!assetsIndex.assets[asset]) { + throw new Error(`Cannot get width - no such asset found: ${asset}`) + } + const raw = assertNumber(assetsIndex.assets[asset].width) + return isHiDpi(asset) ? raw / 2 : raw +} +export function assetH(asset) { + if (!assetsIndex.assets[asset]) { + throw new Error(`Cannot get width - no such asset found: ${asset}`) + } + const raw = assertNumber(assetsIndex.assets[asset].height) + return isHiDpi(asset) ? raw / 2 : raw +} \ No newline at end of file diff --git a/next/src/components/Image.tsx b/next/src/components/Image.tsx deleted file mode 100644 index f4a087e..0000000 --- a/next/src/components/Image.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import NextImage from 'next/image' - -interface ImageProps { - className: string - src: string - alt: string - width?: number - height?: number -} - -/** - * Seems like you should be able to use the `next/image` inside a - * .mdx file, but it doesn't work - */ -export function Image(props: ImageProps) { - return -} diff --git a/next/src/components/Inline.tsx b/next/src/components/Inline.tsx new file mode 100644 index 0000000..6e9ed67 --- /dev/null +++ b/next/src/components/Inline.tsx @@ -0,0 +1,14 @@ +import { assetPathFor, assetW, assetH } from '../assets/assets.mjs' + +interface InlineProps { + src: string + alt: string +} + +/** + * Seems like you should be able to use the `next/image` inside a + * .mdx file, but it doesn't work + */ +export function Inline(props: InlineProps) { + return {props.alt} +} diff --git a/next/src/components/Tables/BranchCasesTable.tsx b/next/src/components/Tables/BranchCasesTable.tsx index 90ef686..37735b8 100644 --- a/next/src/components/Tables/BranchCasesTable.tsx +++ b/next/src/components/Tables/BranchCasesTable.tsx @@ -1,4 +1,4 @@ -import Image from 'next/image' +import { Inline } from '@/components/Inline' export function BranchCasesTable() { return ( @@ -13,128 +13,62 @@ export function BranchCasesTable() { - Synced + The local and remote branches are synchronized. - Synced actions + - Local only + You have a branch that the server doesn't have. - Local only actions + - Remote only + The server has a branch that you don't have. - Remote only actions + - Local has new + You made some commits and haven't uploaded them yet. - Local has new actions + - Remote has new + The server has some new commits which you haven't accepted yet. - Remote has new actions + - Both have new + Both you and the server have some new commits. Congratulations! @@ -144,13 +78,7 @@ export function BranchCasesTable() { section. - Both have new actions + diff --git a/next/src/components/Tables/BranchTable.tsx b/next/src/components/Tables/BranchTable.tsx index c7f0f4d..8631a19 100644 --- a/next/src/components/Tables/BranchTable.tsx +++ b/next/src/components/Tables/BranchTable.tsx @@ -1,4 +1,4 @@ -import Image from 'next/image' +import { Inline } from '@/components/Inline' export function BranchTable() { return ( @@ -12,13 +12,7 @@ export function BranchTable() { - Branch{' '} + {' '} Local branch @@ -28,13 +22,7 @@ export function BranchTable() { - Remote Branch{' '} + {' '} Remote branch @@ -45,13 +33,7 @@ export function BranchTable() { - Tag{' '} + {' '} Tag diff --git a/next/src/components/Tables/RecipeFollowTable.tsx b/next/src/components/Tables/RecipeFollowTable.tsx index 1bd13ca..5e422e0 100644 --- a/next/src/components/Tables/RecipeFollowTable.tsx +++ b/next/src/components/Tables/RecipeFollowTable.tsx @@ -1,4 +1,4 @@ -import Image from 'next/image' +import { Inline } from '@/components/Inline' export function RecipeFollowTable() { return ( @@ -14,53 +14,23 @@ export function RecipeFollowTable() { - Docs before + - Docs after + - C before + - C after + - Conflict before + Unclear diff --git a/next/src/components/Tables/RecipeInferTable.tsx b/next/src/components/Tables/RecipeInferTable.tsx index 913f3ce..31731ca 100644 --- a/next/src/components/Tables/RecipeInferTable.tsx +++ b/next/src/components/Tables/RecipeInferTable.tsx @@ -1,4 +1,4 @@ -import Image from 'next/image' +import { Inline } from '@/components/Inline' export function RecipeInferTable() { return ( @@ -13,60 +13,24 @@ export function RecipeInferTable() { - Docs before + - Docs after + - Docs diff + - C before + - C after + - C diff + diff --git a/next/src/components/Tables/TagsTable.tsx b/next/src/components/Tables/TagsTable.tsx index 0f61e2e..963ee73 100644 --- a/next/src/components/Tables/TagsTable.tsx +++ b/next/src/components/Tables/TagsTable.tsx @@ -1,4 +1,4 @@ -import Image from 'next/image' +import { Inline } from '@/components/Inline' export function TagsTable() { return ( @@ -14,43 +14,19 @@ export function TagsTable() { Lightweight - Lightweight tag dialog + - Lightweight tag right-click + Annotated - Annotated tag dialog + - Annotated tag right-click + diff --git a/next/src/components/Video/Video.tsx b/next/src/components/Video/Video.tsx index 44bd7d2..d941dab 100644 --- a/next/src/components/Video/Video.tsx +++ b/next/src/components/Video/Video.tsx @@ -2,10 +2,17 @@ import { CSSProperties, useEffect, useState } from 'react' import { useVideoRef } from './useVideoRef' import { throttle } from '../../lib/throttle' import { PlayButton } from '../icons/PlayButton' +import { assetPathFor, assetH, assetW } from '../../assets/assets.mjs' type VideoProps = { - poster: string - source: string // must be mp4 video + mp4: string +} + +function mp4_to_png(mp4: string): string { + if (!mp4.endsWith(".mp4")) { + throw Error("Expected to end with `.mp4`, was " + mp4) + } + return mp4.slice(0, mp4.length - "mp4".length) + "png" } export function Video(props: VideoProps) { @@ -78,41 +85,47 @@ export function Video(props: VideoProps) { }, []) return ( -
- {showOverlay && ( -
- +
+ + {showOverlay && ( +
+ +
+ )} +
+
- )} -
-
-
- + +
) } diff --git a/next/src/components/Video/useVideoRef.tsx b/next/src/components/Video/useVideoRef.tsx index 2b20e88..e900571 100644 --- a/next/src/components/Video/useVideoRef.tsx +++ b/next/src/components/Video/useVideoRef.tsx @@ -12,17 +12,16 @@ export function useVideoRef() { setVideoBox(boundingBox) }, []) - function handleResize() { + const handleResize = useCallback(() => { videoRefCallback(videoRef.current!) - } + }, [videoRefCallback]) - // Recalculate the videoBox when the window is resized useEffect(() => { window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) } - }, []) + }, [handleResize]) return { videoBox, videoRef, videoRefCallback } } diff --git a/next/src/lib/throttle.ts b/next/src/lib/throttle.ts index d93bcd5..577334f 100644 --- a/next/src/lib/throttle.ts +++ b/next/src/lib/throttle.ts @@ -4,35 +4,15 @@ // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. -export function throttle(func: any , wait: number, options: {leading?: boolean, trailing?: boolean}) { - var context: Function | null, args: any, result: any; - var timeout: number | null = null; - var previous = 0; - if (!options) options = {}; - var later = function() { - previous = options.leading === false ? 0 : Date.now(); - timeout = null; - result = func.apply(context, args); - if (!timeout) context = args = null; - }; - return function(this: Function) { - var now = Date.now(); - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0 || remaining > wait) { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - previous = now; - result = func.apply(context, args); - if (!timeout) context = args = null; - } else if (!timeout && options.trailing !== false) { - timeout = window.setTimeout(later, remaining); +export function throttle(callback: Function, limit: number) { + var waiting = false; // Initially, we're not waiting + return function throttled() { // We return a throttled function + if (!waiting) { // If we're not waiting + callback.apply(throttled, arguments); // Execute users function + waiting = true; // Prevent future invocations + setTimeout(function () { // After a period of time + waiting = false; // And allow future invocations + }, limit); } - return result; - }; - }; - \ No newline at end of file + } +} \ No newline at end of file diff --git a/next/src/mdx/rehype.mjs b/next/src/mdx/rehype.mjs index a57309a..0b45ff4 100644 --- a/next/src/mdx/rehype.mjs +++ b/next/src/mdx/rehype.mjs @@ -6,25 +6,35 @@ import { toString } from 'mdast-util-to-string' import * as acorn from 'acorn' import { slugifyWithCounter } from '@sindresorhus/slugify' +// Move highlighter initialization outside the plugin +let highlighter +let theme + +// Initialize these in an async function +async function initializeHighlighter() { + if (!highlighter) { + theme = await shiki.loadTheme('themes/github-light.json') + highlighter = await shiki.getHighlighter({ theme }) + } + return highlighter +} + function rehypeParseCodeBlocks() { return (tree) => { visit(tree, 'element', (node, _nodeIndex, parentNode) => { if (node.tagName === 'code' && node.properties.className) { parentNode.properties.language = node.properties.className[0]?.replace( /^language-/, - '' + '', ) } }) } } -let highlighter - function rehypeShiki() { return async (tree) => { - highlighter = - highlighter ?? (await shiki.getHighlighter({ theme: 'css-variables' })) + const highlighter = await initializeHighlighter() visit(tree, 'element', (node) => { if (node.tagName === 'pre' && node.children[0]?.tagName === 'code') { @@ -36,7 +46,7 @@ function rehypeShiki() { if (node.properties.language) { let tokens = highlighter.codeToThemedTokens( textNode.value, - node.properties.language + node.properties.language, ) textNode.value = shiki.renderToHtml(tokens, { diff --git a/next/src/mdx/remark.mjs b/next/src/mdx/remark.mjs index e523464..a743508 100644 --- a/next/src/mdx/remark.mjs +++ b/next/src/mdx/remark.mjs @@ -1,4 +1,21 @@ import { mdxAnnotations } from 'mdx-annotations' +import { visit } from 'unist-util-visit' import remarkGfm from 'remark-gfm' +import { assetPathFor, assetW, assetH } from '../assets/assets.mjs' -export const remarkPlugins = [mdxAnnotations.remark, remarkGfm] +export function remarkAssetPrefix() { + return (tree, file) => { + visit(tree, 'image', (node) => { + const assetKey = node.url + node.url = assetPathFor(assetKey) + node.data = { + hProperties: { + width: assetW(assetKey), + height: assetH(assetKey) + } + } + }) + } +} + +export const remarkPlugins = [mdxAnnotations.remark, remarkGfm, remarkAssetPrefix] diff --git a/next/src/pages/branches/reflog.mdx b/next/src/pages/branches/reflog.mdx index 35bbe03..669a940 100644 --- a/next/src/pages/branches/reflog.mdx +++ b/next/src/pages/branches/reflog.mdx @@ -11,10 +11,7 @@ If you lose the tip of a chain of commits in git, they are effectively "deleted" 3. Move the branch back by one commit, thus losing the "tip" that pointed to your life's work. 4. Mission accomplished! -