@gogitcms/editor 0.23.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/app/index.html +38 -0
- package/app/src/App.tsx +2665 -0
- package/app/src/apollo.ts +119 -0
- package/app/src/auth.ts +248 -0
- package/app/src/config.ts +88 -0
- package/app/src/main.tsx +18 -0
- package/app/src/media.ts +151 -0
- package/app/src/navigation.tsx +321 -0
- package/app/src/plugins.ts +11 -0
- package/app/src/previewHost.tsx +263 -0
- package/app/src/previewToken.ts +68 -0
- package/app/src/queries.ts +591 -0
- package/app/src/virtual-cms-plugins.d.ts +6 -0
- package/app/vendor/analytics/src/__tests__/config.test.ts +91 -0
- package/app/vendor/analytics/src/config.ts +94 -0
- package/app/vendor/analytics/src/events.ts +56 -0
- package/app/vendor/analytics/src/index.ts +23 -0
- package/app/vendor/analytics/src/provider.tsx +196 -0
- package/app/vendor/design-system/src/ThemeProvider.tsx +86 -0
- package/app/vendor/design-system/src/__tests__/ApplyChangesModal.test.tsx +148 -0
- package/app/vendor/design-system/src/__tests__/BranchImport.test.tsx +46 -0
- package/app/vendor/design-system/src/__tests__/Button.test.tsx +45 -0
- package/app/vendor/design-system/src/__tests__/ChangeRequestSummary.test.tsx +57 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.changes.test.tsx +611 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.collab.test.tsx +322 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.collabsync.test.tsx +264 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.contentslot.test.tsx +53 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.discriminator.test.tsx +142 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.drafts.test.tsx +271 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.fields.test.tsx +117 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.media.test.tsx +140 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.mixedcollab.test.tsx +63 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.mixedvalues.test.tsx +38 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.pagination.test.tsx +62 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.previewtab.test.tsx +212 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.reorder.test.tsx +45 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.search.test.tsx +135 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.selectvalue.test.tsx +185 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.staged.test.tsx +132 -0
- package/app/vendor/design-system/src/__tests__/ContentBrowser.usermenu.test.tsx +56 -0
- package/app/vendor/design-system/src/__tests__/MediaBrowser.test.tsx +353 -0
- package/app/vendor/design-system/src/__tests__/MediaField.test.tsx +185 -0
- package/app/vendor/design-system/src/__tests__/Notifications.test.tsx +69 -0
- package/app/vendor/design-system/src/__tests__/Onboarding.test.tsx +287 -0
- package/app/vendor/design-system/src/__tests__/cssTokens.test.ts +201 -0
- package/app/vendor/design-system/src/__tests__/fieldComponents.test.ts +43 -0
- package/app/vendor/design-system/src/__tests__/reorder.test.ts +58 -0
- package/app/vendor/design-system/src/components/ApplyChangesModal.tsx +348 -0
- package/app/vendor/design-system/src/components/BranchImport.tsx +157 -0
- package/app/vendor/design-system/src/components/BranchMenu.tsx +192 -0
- package/app/vendor/design-system/src/components/Button.tsx +131 -0
- package/app/vendor/design-system/src/components/ChangeDetail.tsx +472 -0
- package/app/vendor/design-system/src/components/ChangeRequestSummary.tsx +173 -0
- package/app/vendor/design-system/src/components/CollabField.tsx +388 -0
- package/app/vendor/design-system/src/components/ContentBrowser.tsx +5073 -0
- package/app/vendor/design-system/src/components/Icon.tsx +28 -0
- package/app/vendor/design-system/src/components/Icon.web.tsx +31 -0
- package/app/vendor/design-system/src/components/Input.tsx +106 -0
- package/app/vendor/design-system/src/components/MediaBrowser.tsx +766 -0
- package/app/vendor/design-system/src/components/MediaField.tsx +670 -0
- package/app/vendor/design-system/src/components/MediaPreview.tsx +91 -0
- package/app/vendor/design-system/src/components/MediaPreview.web.tsx +169 -0
- package/app/vendor/design-system/src/components/NavRow.tsx +105 -0
- package/app/vendor/design-system/src/components/Notifications.tsx +301 -0
- package/app/vendor/design-system/src/components/Onboarding.tsx +751 -0
- package/app/vendor/design-system/src/components/ProjectMenu.tsx +124 -0
- package/app/vendor/design-system/src/components/Segment.tsx +87 -0
- package/app/vendor/design-system/src/components/Skeleton.tsx +216 -0
- package/app/vendor/design-system/src/components/Spinner.tsx +44 -0
- package/app/vendor/design-system/src/components/Text.tsx +85 -0
- package/app/vendor/design-system/src/components/documentDrafts.ts +213 -0
- package/app/vendor/design-system/src/components/layout.tsx +284 -0
- package/app/vendor/design-system/src/components/primitives.tsx +143 -0
- package/app/vendor/design-system/src/components/reorder.ts +40 -0
- package/app/vendor/design-system/src/fieldComponents.ts +63 -0
- package/app/vendor/design-system/src/icons.ts +102 -0
- package/app/vendor/design-system/src/index.ts +198 -0
- package/app/vendor/design-system/src/media.ts +229 -0
- package/app/vendor/design-system/src/theme.ts +116 -0
- package/app/vendor/design-system/src/web/Button.tsx +110 -0
- package/app/vendor/design-system/src/web/Icon.tsx +52 -0
- package/app/vendor/design-system/src/web/Input.tsx +39 -0
- package/app/vendor/design-system/src/web/index.ts +43 -0
- package/app/vendor/design-system/src/web/primitives.tsx +119 -0
- package/app/vendor/markdown-editor/src/MarkdownEditor.tsx +248 -0
- package/app/vendor/markdown-editor/src/Toolbar.tsx +157 -0
- package/app/vendor/markdown-editor/src/field.tsx +67 -0
- package/app/vendor/markdown-editor/src/flavors/commonmark/index.ts +27 -0
- package/app/vendor/markdown-editor/src/flavors/gfm/index.ts +30 -0
- package/app/vendor/markdown-editor/src/flavors/gfm/parser.ts +73 -0
- package/app/vendor/markdown-editor/src/flavors/gfm/schema.ts +70 -0
- package/app/vendor/markdown-editor/src/flavors/gfm/serializer.ts +82 -0
- package/app/vendor/markdown-editor/src/flavors/gfm/taskList.ts +39 -0
- package/app/vendor/markdown-editor/src/flavors/registry.ts +13 -0
- package/app/vendor/markdown-editor/src/flavors/shared/inputrules.ts +79 -0
- package/app/vendor/markdown-editor/src/flavors/shared/keymap.ts +89 -0
- package/app/vendor/markdown-editor/src/flavors/shared/placeholder.ts +24 -0
- package/app/vendor/markdown-editor/src/flavors/shared/plugins.ts +58 -0
- package/app/vendor/markdown-editor/src/flavors/types.ts +22 -0
- package/app/vendor/markdown-editor/src/formats/registry.ts +45 -0
- package/app/vendor/markdown-editor/src/icons.tsx +120 -0
- package/app/vendor/markdown-editor/src/index.ts +23 -0
- package/app/vendor/markdown-editor/src/theme.ts +249 -0
- package/app/vendor/markdown-editor/src/types.ts +58 -0
- package/app/vendor/plugin-sdk/src/__tests__/documentActions.test.ts +108 -0
- package/app/vendor/plugin-sdk/src/__tests__/loader.test.ts +75 -0
- package/app/vendor/plugin-sdk/src/__tests__/registry.test.ts +148 -0
- package/app/vendor/plugin-sdk/src/index.ts +33 -0
- package/app/vendor/plugin-sdk/src/loader.ts +45 -0
- package/app/vendor/plugin-sdk/src/navkeys.ts +17 -0
- package/app/vendor/plugin-sdk/src/react.tsx +155 -0
- package/app/vendor/plugin-sdk/src/registry.ts +337 -0
- package/app/vendor/plugin-sdk/src/types.ts +230 -0
- package/app/vendor/realtime/src/__tests__/pure.test.ts +58 -0
- package/app/vendor/realtime/src/__tests__/seeded-room.base64 +1 -0
- package/app/vendor/realtime/src/__tests__/seeding.test.ts +70 -0
- package/app/vendor/realtime/src/collab.ts +308 -0
- package/app/vendor/realtime/src/hooks.ts +57 -0
- package/app/vendor/realtime/src/index.ts +12 -0
- package/app/vendor/realtime/src/pure.ts +43 -0
- package/app/vite.config.mjs +59 -0
- package/bin/gogitcms-editor.mjs +168 -0
- package/npm-shrinkwrap.json +5696 -0
- package/package.json +74 -0
- package/src/commands/build.mjs +100 -0
- package/src/commands/dev.mjs +186 -0
- package/src/commands/init.mjs +144 -0
- package/src/commands/login.mjs +116 -0
- package/src/commands/status.mjs +68 -0
- package/src/lib/api.mjs +122 -0
- package/src/lib/appRoot.mjs +51 -0
- package/src/lib/baked.json +3 -0
- package/src/lib/config.mjs +108 -0
- package/src/lib/credentials.mjs +91 -0
- package/src/lib/defaults.mjs +26 -0
- package/src/lib/graphql.mjs +28 -0
- package/src/lib/localServer.mjs +206 -0
- package/src/lib/open.mjs +23 -0
- package/src/lib/plugins.mjs +164 -0
- package/src/lib/tokenBroker.mjs +85 -0
- package/src/tui/prompts.mjs +92 -0
- package/src/tui/screen.mjs +282 -0
- package/src/tui/theme.mjs +40 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { createDocCollab, type DocCollab, type CollabUser } from "./collab";
|
|
3
|
+
|
|
4
|
+
export type UseDocCollabOptions = {
|
|
5
|
+
// Base WebSocket URL, e.g. "wss://host/collab/ws". When empty/undefined the
|
|
6
|
+
// hook stays inert (returns null) — collaboration is simply off.
|
|
7
|
+
wsBase?: string;
|
|
8
|
+
room: string;
|
|
9
|
+
user: CollabUser | null | undefined;
|
|
10
|
+
getToken: () => string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// useDocCollab owns one DocCollab for a document, recreating it when the room
|
|
14
|
+
// changes and destroying it on unmount. Returns null while inputs are missing.
|
|
15
|
+
export function useDocCollab(opts: UseDocCollabOptions): DocCollab | null {
|
|
16
|
+
const { wsBase, room, user } = opts;
|
|
17
|
+
// Keep the latest token getter without forcing recreation of the connection.
|
|
18
|
+
const getToken = useRef(opts.getToken);
|
|
19
|
+
getToken.current = opts.getToken;
|
|
20
|
+
|
|
21
|
+
const [collab, setCollab] = useState<DocCollab | null>(null);
|
|
22
|
+
const enabled = !!wsBase && !!room && !!user;
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
// Visible diagnostic: shows whether a session is being created and why not.
|
|
26
|
+
console.log(`[collab] useDocCollab room=${room} enabled=${enabled}`, {
|
|
27
|
+
wsBase,
|
|
28
|
+
hasUser: !!user,
|
|
29
|
+
});
|
|
30
|
+
if (!enabled) {
|
|
31
|
+
setCollab(null);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let instance: DocCollab;
|
|
35
|
+
try {
|
|
36
|
+
instance = createDocCollab({
|
|
37
|
+
wsBase: wsBase!,
|
|
38
|
+
room,
|
|
39
|
+
user: user!,
|
|
40
|
+
getToken: () => getToken.current(),
|
|
41
|
+
});
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(`[collab] failed to start session for room=${room}`, err);
|
|
44
|
+
setCollab(null);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
setCollab(instance);
|
|
48
|
+
return () => {
|
|
49
|
+
instance.destroy();
|
|
50
|
+
setCollab(null);
|
|
51
|
+
};
|
|
52
|
+
// user identity is stable per session; recreate only on connection identity.
|
|
53
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
54
|
+
}, [enabled, wsBase, room]);
|
|
55
|
+
|
|
56
|
+
return collab;
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Public API for @gogitcms/realtime — the collaborative-editing (Yjs) client.
|
|
2
|
+
export { createDocCollab, colorForId } from "./collab";
|
|
3
|
+
export type {
|
|
4
|
+
DocCollab,
|
|
5
|
+
CollabText,
|
|
6
|
+
CollabPeer,
|
|
7
|
+
CollabUser,
|
|
8
|
+
CollabStatus,
|
|
9
|
+
CreateDocCollabOptions,
|
|
10
|
+
} from "./collab";
|
|
11
|
+
export { useDocCollab } from "./hooks";
|
|
12
|
+
export type { UseDocCollabOptions } from "./hooks";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Pure, dependency-free helpers (no yjs), so they can be unit-tested in isolation.
|
|
2
|
+
|
|
3
|
+
// colorForId derives a deterministic, pleasant color from a stable id (e.g. a
|
|
4
|
+
// user id), so a given user shows the same presence color everywhere without
|
|
5
|
+
// server coordination.
|
|
6
|
+
export function colorForId(id: string): string {
|
|
7
|
+
let h = 0;
|
|
8
|
+
for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) >>> 0;
|
|
9
|
+
const hue = h % 360;
|
|
10
|
+
return `hsl(${hue} 70% 45%)`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// computeSplice returns the minimal single edit (delete a middle run, insert a
|
|
14
|
+
// middle run) that turns `cur` into `next`, preserving the shared prefix and
|
|
15
|
+
// suffix. Applying only the changed middle to a Y.Text lets concurrent remote
|
|
16
|
+
// edits outside that range survive, instead of replacing the whole value.
|
|
17
|
+
export function computeSplice(
|
|
18
|
+
cur: string,
|
|
19
|
+
next: string,
|
|
20
|
+
): { index: number; deleteCount: number; insert: string } {
|
|
21
|
+
const start = commonPrefix(cur, next);
|
|
22
|
+
const suffix = commonSuffix(cur, next, start);
|
|
23
|
+
const endCur = cur.length - suffix;
|
|
24
|
+
const endNext = next.length - suffix;
|
|
25
|
+
return { index: start, deleteCount: Math.max(0, endCur - start), insert: next.slice(start, endNext) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// commonPrefix returns the length of the shared leading run of a and b.
|
|
29
|
+
function commonPrefix(a: string, b: string): number {
|
|
30
|
+
const n = Math.min(a.length, b.length);
|
|
31
|
+
let i = 0;
|
|
32
|
+
while (i < n && a[i] === b[i]) i++;
|
|
33
|
+
return i;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// commonSuffix returns the length of the shared trailing run of a and b, not
|
|
37
|
+
// overlapping the first `floor` characters already matched as a prefix.
|
|
38
|
+
function commonSuffix(a: string, b: string, floor: number): number {
|
|
39
|
+
const max = Math.min(a.length, b.length) - floor;
|
|
40
|
+
let i = 0;
|
|
41
|
+
while (i < max && a[a.length - 1 - i] === b[b.length - 1 - i]) i++;
|
|
42
|
+
return i;
|
|
43
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Generated by scripts/prepack.mjs — do not edit.
|
|
2
|
+
// Mirrors apps/frontend/vite.config.ts, with the workspace @gogitcms/* packages
|
|
3
|
+
// resolved to the copies vendored alongside this file.
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { defineConfig } from "vite";
|
|
7
|
+
import react from "@vitejs/plugin-react";
|
|
8
|
+
|
|
9
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const vendor = (name) => path.join(here, "vendor", name, "src", "index.ts");
|
|
11
|
+
|
|
12
|
+
// The staged main.tsx boots plugins from virtual:cms-plugins. gogitcms-editor's dev
|
|
13
|
+
// and build commands provide the real module (from the project's cms.config.js
|
|
14
|
+
// `plugins`) as an `enforce: "pre"` inline plugin, which resolves the id
|
|
15
|
+
// before this one. This fallback only supplies an empty list for a bare
|
|
16
|
+
// `vite build` on the staged app (e.g. the smoke test), so that still compiles.
|
|
17
|
+
const emptyCmsPlugins = {
|
|
18
|
+
name: "cms-plugins-empty",
|
|
19
|
+
resolveId(id) {
|
|
20
|
+
return id === "virtual:cms-plugins" ? "\0virtual:cms-plugins" : undefined;
|
|
21
|
+
},
|
|
22
|
+
load(id) {
|
|
23
|
+
return id === "\0virtual:cms-plugins" ? "export const plugins = [];\n" : undefined;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
plugins: [react(), emptyCmsPlugins],
|
|
29
|
+
define: { global: "window", __DEV__: JSON.stringify(false) },
|
|
30
|
+
resolve: {
|
|
31
|
+
alias: {
|
|
32
|
+
"react-native": "react-native-web",
|
|
33
|
+
"@gogitcms/analytics": vendor("analytics"),
|
|
34
|
+
"@gogitcms/design-system": vendor("design-system"),
|
|
35
|
+
"@gogitcms/markdown-editor": vendor("markdown-editor"),
|
|
36
|
+
"@gogitcms/plugin-sdk": vendor("plugin-sdk"),
|
|
37
|
+
"@gogitcms/realtime": vendor("realtime"),
|
|
38
|
+
},
|
|
39
|
+
extensions: [".web.tsx", ".web.ts", ".tsx", ".ts", ".js"],
|
|
40
|
+
},
|
|
41
|
+
optimizeDeps: {
|
|
42
|
+
// Force these CJS-in-an-ESM-world packages to be pre-bundled, so Vite never
|
|
43
|
+
// serves them raw (a raw CJS module references `module`/`exports`, which
|
|
44
|
+
// don't exist in the browser — "module is not defined").
|
|
45
|
+
// * @react-navigation/native: type:commonjs with an ESM-syntax main; when
|
|
46
|
+
// served raw its CJS leaf use-latest-callback breaks default interop.
|
|
47
|
+
// * @apollo/client + rehackt: Apollo's React shim `rehackt` is CJS, and
|
|
48
|
+
// whether Vite folds it into the Apollo chunk or serves it standalone
|
|
49
|
+
// varies by install/discovery order; force both so it is always bundled.
|
|
50
|
+
include: ["@react-navigation/native", "@apollo/client", "@apollo/client > rehackt"],
|
|
51
|
+
// Web variants must lead here too: the esbuild pre-bundler resolves imports
|
|
52
|
+
// with this list (not resolve.extensions above), so without .web.tsx/.web.ts
|
|
53
|
+
// it would scan native-only files like Icon.tsx and try to pull in
|
|
54
|
+
// react-native-svg, which the web build never actually loads.
|
|
55
|
+
esbuildOptions: {
|
|
56
|
+
resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".js", ".ts", ".tsx"],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// gogitcms-editor — terminal UI for setting up and running the Go·Git CMS editor.
|
|
3
|
+
//
|
|
4
|
+
// Subcommands map one-to-one onto the steps of the onboarding design. Running it
|
|
5
|
+
// bare prints the help.
|
|
6
|
+
|
|
7
|
+
import { Screen } from "../src/tui/screen.mjs";
|
|
8
|
+
import { NotInteractiveError } from "../src/tui/prompts.mjs";
|
|
9
|
+
import { login } from "../src/commands/login.mjs";
|
|
10
|
+
import { init } from "../src/commands/init.mjs";
|
|
11
|
+
import { dev } from "../src/commands/dev.mjs";
|
|
12
|
+
import { build } from "../src/commands/build.mjs";
|
|
13
|
+
import { logout, nextHint, status } from "../src/commands/status.mjs";
|
|
14
|
+
import * as config from "../src/lib/config.mjs";
|
|
15
|
+
import * as credentials from "../src/lib/credentials.mjs";
|
|
16
|
+
|
|
17
|
+
// Flags shared by every command, plus the few that are command-specific. Values
|
|
18
|
+
// are strings unless listed as boolean.
|
|
19
|
+
const BOOLEAN_FLAGS = new Set(["open", "yes", "help", "version", "no-tty", "local"]);
|
|
20
|
+
const ALIASES = { p: "port", o: "out", h: "help", y: "yes", v: "version" };
|
|
21
|
+
|
|
22
|
+
function parseArgs(argv) {
|
|
23
|
+
const opts = {};
|
|
24
|
+
const rest = [];
|
|
25
|
+
for (let i = 0; i < argv.length; i++) {
|
|
26
|
+
const arg = argv[i];
|
|
27
|
+
if (!arg.startsWith("-")) {
|
|
28
|
+
rest.push(arg);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const [flag, inline] = arg.replace(/^--?/, "").split("=");
|
|
32
|
+
const name = ALIASES[flag] || flag;
|
|
33
|
+
if (BOOLEAN_FLAGS.has(name)) {
|
|
34
|
+
opts[camel(name)] = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
opts[camel(name)] = inline ?? argv[++i];
|
|
38
|
+
}
|
|
39
|
+
return { opts, rest };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const camel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
43
|
+
|
|
44
|
+
const COMMANDS = { login, init, dev, build, status, logout };
|
|
45
|
+
|
|
46
|
+
const HELP = `
|
|
47
|
+
gogitcms-editor — set up and run the Go·Git CMS editor
|
|
48
|
+
|
|
49
|
+
gogitcms-editor login authenticate this machine
|
|
50
|
+
gogitcms-editor init create cms.config.mjs for this project
|
|
51
|
+
gogitcms-editor dev start the local editor
|
|
52
|
+
gogitcms-editor build export the static editor to ./dist
|
|
53
|
+
gogitcms-editor status show setup progress
|
|
54
|
+
gogitcms-editor logout forget this machine's stored session
|
|
55
|
+
|
|
56
|
+
Options
|
|
57
|
+
--local edit the files in this directory, with no account
|
|
58
|
+
or server (dev only; runs the bundled gitcms-local
|
|
59
|
+
binary from the platform optional dependency)
|
|
60
|
+
--root <dir> working copy to serve in --local mode (default .)
|
|
61
|
+
--local-port <port> port for the local content server in --local mode
|
|
62
|
+
(default 4321)
|
|
63
|
+
--api-url <url> CMS server to talk to (env GITCMS_API_URL)
|
|
64
|
+
--workspace <id|slug> workspace for init, when not choosing interactively
|
|
65
|
+
--repository <id|owner/name> repository for init
|
|
66
|
+
-y, --yes don't ask before overwriting an existing config
|
|
67
|
+
-p, --port <port> dev server port (default 5173)
|
|
68
|
+
--host <host> dev server bind address (default localhost)
|
|
69
|
+
--open open the browser once dev is ready
|
|
70
|
+
-o, --out <dir> build output directory (default dist)
|
|
71
|
+
--base <path> path the editor is served from, overriding the
|
|
72
|
+
config's BASE_PATH (e.g. /cms/); applies to
|
|
73
|
+
init, dev and build
|
|
74
|
+
--no-tty plain output, no menus or animation
|
|
75
|
+
-h, --help show this help
|
|
76
|
+
`;
|
|
77
|
+
|
|
78
|
+
async function main() {
|
|
79
|
+
const { opts, rest } = parseArgs(process.argv.slice(2));
|
|
80
|
+
if (opts.noTty) process.env.GITCMS_NO_TTY = "1";
|
|
81
|
+
|
|
82
|
+
if (opts.version) {
|
|
83
|
+
const { default: pkg } = await import("../package.json", { with: { type: "json" } });
|
|
84
|
+
console.log(pkg.version);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
// No command is not an error, but there is nothing to guess at either: show
|
|
88
|
+
// what the CLI can do and let the user pick.
|
|
89
|
+
const name = rest[0];
|
|
90
|
+
if (!name || opts.help || name === "help") {
|
|
91
|
+
console.log(HELP.trim());
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
if (!COMMANDS[name]) {
|
|
95
|
+
console.error(`Unknown command: ${name}\nRun gogitcms-editor help for the list.`);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const screen = new Screen();
|
|
100
|
+
// Seed the status line from what is already on disk, so the very first frame
|
|
101
|
+
// reports the real state instead of a default that flips a moment later.
|
|
102
|
+
await primeStatus(screen, opts);
|
|
103
|
+
|
|
104
|
+
// Ctrl-C anywhere outside a prompt (the long device-code poll, a running dev
|
|
105
|
+
// server) lands here. Without it the process dies mid-frame, leaving the
|
|
106
|
+
// status bar drawn and the terminal's colours unreset. A command that owns
|
|
107
|
+
// something closable claims the signal via screen.onInterrupt and returns
|
|
108
|
+
// normally instead, so the finally below still restores the terminal.
|
|
109
|
+
const onSignal = () => {
|
|
110
|
+
if (screen.onInterrupt) {
|
|
111
|
+
const claimed = screen.onInterrupt;
|
|
112
|
+
screen.onInterrupt = null;
|
|
113
|
+
void claimed();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
screen.finish();
|
|
117
|
+
process.exit(130);
|
|
118
|
+
};
|
|
119
|
+
process.on("SIGINT", onSignal);
|
|
120
|
+
process.on("SIGTERM", onSignal);
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
return await COMMANDS[name](screen, opts);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
screen.stopSpinner();
|
|
126
|
+
if (e instanceof NotInteractiveError) {
|
|
127
|
+
screen.fail(e.message);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
screen.fail(e?.message || String(e));
|
|
131
|
+
if (process.env.GITCMS_DEBUG) console.error(e);
|
|
132
|
+
return 1;
|
|
133
|
+
} finally {
|
|
134
|
+
screen.finish();
|
|
135
|
+
// These outlive the command otherwise, and a listener on the process keeps
|
|
136
|
+
// nothing alive but does keep firing.
|
|
137
|
+
process.off("SIGINT", onSignal);
|
|
138
|
+
process.off("SIGTERM", onSignal);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function primeStatus(screen, opts) {
|
|
143
|
+
try {
|
|
144
|
+
const cfg = await config.load();
|
|
145
|
+
// Local mode has no account, so the sign-in step is satisfied by definition —
|
|
146
|
+
// showing "not logged in" would be reporting a blocker that doesn't exist.
|
|
147
|
+
if (opts.local || cfg?.MODE === "local") {
|
|
148
|
+
screen.setDone(0, true);
|
|
149
|
+
screen.setDone(1, Boolean(cfg));
|
|
150
|
+
screen.setStatus({ auth: "local files", authed: true, hint: "run gogitcms-editor dev --local" });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const apiUrl = opts.apiUrl || cfg?.API_URL;
|
|
154
|
+
const creds = apiUrl ? await credentials.load(apiUrl) : (await credentials.loadAny())?.[1];
|
|
155
|
+
const done = [Boolean(creds), Boolean(cfg), false];
|
|
156
|
+
done.forEach((d, i) => screen.setDone(i, d));
|
|
157
|
+
screen.setStatus({
|
|
158
|
+
auth: creds?.email || "not logged in",
|
|
159
|
+
authed: Boolean(creds),
|
|
160
|
+
hint: nextHint(done),
|
|
161
|
+
});
|
|
162
|
+
} catch {
|
|
163
|
+
// A malformed config must not stop the CLI from running — the command that
|
|
164
|
+
// needs it will report the problem in context.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
process.exitCode = await main();
|