Files
m3e-canvas/scripts/vendor.sh
T
pastilhasandClaude Fable 5 a0349cc7a3 the officer shell: designs per user, and the handoff desk
manifest, schema, router and two panels. the editor is hosted and
saving; the last step is not finished and the readme says so.

designs are rows scoped per user, with userId in every WHERE rather
than checked after the read — another person's design is 'not found',
so the id space cannot be probed. the Doc is stored whole as jsonb
because upstream adds fields to it constantly and none of them should
be a migration here.

the handoff is the reason the plugin exists and it is the part that
does not work: /api/chat has NO session-creation route, a session is
constructed by the browser opening a websocket, so a plugin has nothing
to POST to. onExport returns false and the clipboard still runs, which
is upstream's own behaviour — degraded, not broken. docs/handoff.md
names the one platform change that would finish it, and argues it
should be generic rather than ours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 12:16:35 +01:00

123 lines
5.5 KiB
Bash
Executable File

#!/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"
# Upstream's own tests are not taken. They import `vitest`, which this plugin has no runner for and
# no reason to install — and a test that cannot run is worse than no test, because it reads as
# coverage. Upstream runs them in its own CI, which is where they belong.
find "$DEST" \( -name '*.test.ts' -o -name '*.test.tsx' -o -name '*.spec.ts' -o -name '*.spec.tsx' \) -delete
# 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
# Officer's own changes to the vendored code. Kept OUT of this script so the copy above is exactly
# upstream, and applied here so one command reproduces the whole state.
python3 "$HERE/scripts/apply-host.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"