vendor m3e-canvas 5c83ac6, with the import rewrite scripted

upstream imports through a @/ alias, and this platform already binds
@/* to src/apps/officer-web and @/components/* to its own shared
components — so an unrewritten import resolves to the WRONG module
rather than failing. the rewrite is mechanical and lives in
scripts/vendor.sh so a re-vendor produces a readable diff.

lib/ai.ts and AiPanel are vendored despite being unwanted: deleting them
meant editing vendored code, and Canvas.tsx references them throughout.
a faithful copy carrying an unused module beats a doctored one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 12:00:17 +01:00
co-authored by Claude Fable 5
commit 240d63940d
39 changed files with 17299 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# Re-vendor the M3E Canvas editor from upstream.
#
# ./scripts/vendor.sh [<git-ref>] default: main
#
# ── Why a script rather than a copy ──
#
# The vendored copy is NOT byte-identical to upstream, and it cannot be. Upstream imports through a
# `@/` path alias, and this platform already binds `@/*` to `src/apps/officer-web` and
# `@/components/*` to its own shared components. Left alone, `import { M3Node } from
# "@/components/M3Node"` inside the vendored editor would silently resolve to Officer's components
# directory — a wrong module rather than a build error, which is the worst way to be broken.
#
# So every import is rewritten to a relative path. That transformation is MECHANICAL and lives here,
# which is what keeps the copy reviewable: `git diff` after a re-vendor shows upstream's real changes,
# because the rewrite produces the same output for the same input every time.
#
# Nothing else is edited. Officer's own changes live outside `web/vendor/`, reached through
# `web/adapter.ts`.
set -euo pipefail
REF="${1:-main}"
UPSTREAM="https://github.com/lnkiai/m3e-canvas.git"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DEST="$HERE/web/vendor"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
echo "cloning $UPSTREAM @ $REF"
git clone -q --depth 1 --branch "$REF" "$UPSTREAM" "$WORK/src"
SHA="$(git -C "$WORK/src" rev-parse HEAD)"
WHEN="$(git -C "$WORK/src" log -1 --format=%ad --date=short)"
rm -rf "$DEST"
mkdir -p "$DEST"
# lib/ and components/ only. `app/` is upstream's Next.js route and is deliberately NOT taken:
# app/page.tsx becomes web/vendor/Canvas.tsx below, and app/layout.tsx has no meaning here.
cp -r "$WORK/src/lib" "$DEST/lib"
cp -r "$WORK/src/components" "$DEST/components"
cp "$WORK/src/app/page.tsx" "$DEST/Canvas.tsx"
cp "$WORK/src/app/globals.css" "$DEST/globals.css"
cp "$WORK/src/LICENSE" "$DEST/LICENSE"
# lib/ai.ts and components/AiPanel.tsx ARE vendored, despite being the parts Officer least needs.
#
# The first attempt deleted them — they keep model-provider API keys in localStorage and call the
# provider from the browser, which Officer has no use for and would not do that way. But Canvas.tsx
# references them in about a dozen places, so deleting meant editing vendored code, and the rule one
# paragraph up says not to. A faithful copy that carries an unused module beats a doctored one that
# cannot be re-vendored.
#
# They stay inert: the panel does nothing without a key, and Officer's shell does not surface the
# entry point. See ../README.md, "What we do not use".
# ── the mechanical rewrite ──
# `@/lib/x` -> relative path to vendor/lib/x
# `@/components/x` -> relative path to vendor/components/x
python3 - "$DEST" <<'PY'
import os, re, sys
dest = sys.argv[1]
for root, _, files in os.walk(dest):
for name in files:
if not name.endswith((".ts", ".tsx")):
continue
path = os.path.join(root, name)
depth = os.path.relpath(root, dest)
prefix = "./" if depth == "." else "../" * len(depth.split(os.sep))
src = open(path, encoding="utf-8").read()
out = re.sub(r'(["\'])@/(lib|components)/', lambda m: f"{m.group(1)}{prefix}{m.group(2)}/", src)
if out != src:
open(path, "w", encoding="utf-8").write(out)
PY
cat > "$DEST/PROVENANCE.md" <<PROV
# Vendored from lnkiai/m3e-canvas
| | |
| --- | --- |
| upstream | $UPSTREAM |
| ref | \`$REF\` |
| commit | \`$SHA\` |
| dated | $WHEN |
| licence | MIT — see \`LICENSE\` beside this file |
| vendored | $(date -u +%Y-%m-%d) by \`scripts/vendor.sh\` |
**Do not edit anything in this directory.** Officer's changes live outside it, reached through
\`web/adapter.ts\`. Editing here makes the next \`vendor.sh\` run either clobber the change or
produce a diff nobody can read.
## What was taken, and what was not
Taken: \`lib/\`, \`components/\`, upstream's \`app/page.tsx\` as \`Canvas.tsx\`, and \`globals.css\`.
Not taken: \`app/layout.tsx\` — a Next.js route shell with no meaning here.
\`lib/ai.ts\` and \`components/AiPanel.tsx\` ARE taken even though Officer does not use them. They
keep model-provider API keys in \`localStorage\`, which is not how anything here should work — but
\`Canvas.tsx\` references them throughout, so removing them would mean editing vendored code. A
faithful copy carrying an unused module beats a doctored one that cannot be re-vendored. They are
inert without a key, and the shell does not surface them.
## The one edit \`vendor.sh\` makes
Every \`@/lib/…\` and \`@/components/…\` import is rewritten to a relative path. This platform already
binds \`@/*\` to \`src/apps/officer-web\` and \`@/components/*\` to its own shared components, so an
unrewritten import resolves to the WRONG module rather than failing — silent breakage.
The rewrite is deterministic, so a re-vendor produces a readable diff of upstream's real changes.
PROV
echo "vendored $SHA ($WHEN) -> web/vendor/"
echo " $(find "$DEST" -name '*.ts' -o -name '*.tsx' | wc -l) source files"