@multiplatform.one/cli 6.7.0 → 7.0.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.
Files changed (43) hide show
  1. package/README.md +83 -8
  2. package/lib/bin/multiplatformOne.mjs +21 -7
  3. package/lib/commands/adoptApp.mjs +127 -0
  4. package/lib/commands/init.mjs +1 -1
  5. package/lib/commands/initApp.mjs +43 -15
  6. package/lib/commands/updateApp.mjs +84 -15
  7. package/package.json +2 -2
  8. package/scripts/frappe-app-name.py +125 -0
  9. package/scripts/frappe-app-name.spec.ts +191 -0
  10. package/scripts/frappe-bootstrap.sh +8 -30
  11. package/src/bin/multiplatformOne.ts +78 -13
  12. package/src/commands/adoptApp.spec.ts +208 -0
  13. package/src/commands/adoptApp.ts +185 -0
  14. package/src/commands/initApp.spec.ts +152 -12
  15. package/src/commands/initApp.ts +95 -21
  16. package/src/commands/updateApp.spec.ts +314 -2
  17. package/src/commands/updateApp.ts +164 -33
  18. package/templates/app/apps/__NAME__/package.json +1 -1
  19. package/templates/app/features/__NAME__/package.json +1 -1
  20. package/templates/pieces/gnome/universal/README.md.partial +5 -5
  21. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/anchor.tsx +39 -0
  22. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/main.tsx +4 -2
  23. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/components.ts +26 -0
  24. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/forms.ts +26 -0
  25. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/frappe-ui.ts +17 -0
  26. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/one.ts +3 -0
  27. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/theme.ts +21 -0
  28. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/tamagui-barrel.ts +21 -0
  29. package/templates/pieces/gnome/universal/apps/__NAME__/vite.config.gnome.ts +49 -7
  30. package/templates/pieces/keycloak/universal/README.md.partial +13 -0
  31. package/templates/pieces/keycloak/universal/docker/compose.keycloak.yaml +19 -3
  32. package/templates/pieces/keycloak/universal/env.example.partial +4 -1
  33. package/templates/pieces/vscode/universal/apps/__NAME__/package.json.partial +1 -1
  34. package/templates/pieces/webext/universal/apps/__NAME__/package.json.partial +2 -2
  35. package/templates/universal/apps/__NAME__/package.json +2 -2
  36. package/templates/universal/packages/themes/package.json +2 -2
  37. package/types/bin/multiplatformOne.d.ts.map +1 -1
  38. package/types/commands/adoptApp.d.ts +25 -0
  39. package/types/commands/adoptApp.d.ts.map +1 -0
  40. package/types/commands/initApp.d.ts +33 -5
  41. package/types/commands/initApp.d.ts.map +1 -1
  42. package/types/commands/updateApp.d.ts +12 -2
  43. package/types/commands/updateApp.d.ts.map +1 -1
@@ -11,8 +11,9 @@
11
11
  *
12
12
  * `git merge-tree --write-tree --merge-base=<base>` merges template
13
13
  * evolution with your customizations; conflicts land in the worktree with
14
- * normal conflict markers. An `.updateignore` file (one pathspec per line)
15
- * pins matching paths to your HEAD version, exactly like the old script.
14
+ * normal conflict markers. An `.updateignore` file (one exact file or
15
+ * directory prefix per line not globs; `dir/**` is normalized, other
16
+ * glob shapes are refused) pins matching paths to your HEAD version.
16
17
  */
17
18
 
18
19
  import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
@@ -20,13 +21,33 @@ import { tmpdir } from "node:os";
20
21
  import { basename, join } from "node:path";
21
22
  import spawn from "nano-spawn";
22
23
  import { generateVscodeConfig } from "../generateVscode";
23
- import { cliVersion, initApp, readProvenance, writeProvenance, type InitAppPiece } from "./initApp";
24
+ import {
25
+ PROVENANCE_FILE,
26
+ cliVersion,
27
+ initApp,
28
+ packageJsonName,
29
+ readProvenance,
30
+ validateName,
31
+ writeProvenance,
32
+ type InitAppPiece,
33
+ type InitAppTemplate,
34
+ type MpoProvenance,
35
+ type ProvenanceTemplate,
36
+ } from "./initApp";
24
37
 
25
38
  export interface UpdateAppOptions {
26
39
  /** Skip pnpm install after the merge. */
27
40
  skipInstall?: boolean;
28
41
  /** Override the semver range written for @multiplatform.one/* deps. */
29
42
  version?: string;
43
+ /**
44
+ * Pre-provenance bootstrap: when .mpo.json is missing, assume the project
45
+ * was scaffolded by this exact CLI version, write provenance first, then
46
+ * proceed with the normal update.
47
+ */
48
+ assumeVersion?: string;
49
+ /** Template recorded by the --assume-version bootstrap (default universal). */
50
+ assumeTemplate?: InitAppTemplate;
30
51
  }
31
52
 
32
53
  async function git(projectDir: string, args: string[], env?: Record<string, string>) {
@@ -67,20 +88,27 @@ async function commitTreeFromDir(
67
88
  async function scaffoldBaseline(
68
89
  version: string,
69
90
  name: string,
70
- template: "universal" | "app",
91
+ template: ProvenanceTemplate,
71
92
  mpoVersion: string,
72
93
  pieces: InitAppPiece[],
73
94
  ): Promise<{ dir: string; cleanup: () => void }> {
74
95
  const parent = mkdtempSync(join(tmpdir(), "mpo-update-"));
75
96
  const cleanup = () => rmSync(parent, { recursive: true, force: true });
76
97
  const current = cliVersion();
77
- const templateFlag = template === "app" ? ["--web"] : ["--universal"];
98
+ const templateFlag =
99
+ template === "none" ? ["--pieces-only"] : template === "app" ? ["--web"] : ["--universal"];
78
100
  // The consumer scaffolder (initApp + templates) shipped in 6.3.0 —
79
101
  // earlier CLIs only had the monorepo-clone init and cannot regenerate a
80
- // scaffold baseline.
102
+ // scaffold baseline. Pieces-only scaffolds (`mpo init --pieces-only`, the
103
+ // baseline of ADOPTED projects) shipped in 6.6.0 alongside `mpo adopt`;
104
+ // adopted provenance is always written by ≥6.6 CLIs, so the gate only
105
+ // trips on hand-written .mpo.json files.
81
106
  const [major = 0, minor = 0] = version.split(".").map(Number);
82
- const scaffoldCapable = major > 6 || (major === 6 && minor >= 3);
83
- // Composable pieces (--frappe/--keycloak/--tauri/--vscode/--webext)
107
+ const scaffoldCapable =
108
+ template === "none"
109
+ ? major > 6 || (major === 6 && minor >= 6)
110
+ : major > 6 || (major === 6 && minor >= 3);
111
+ // Composable pieces (--frappe/--keycloak/--gnome/--vscode/--webext)
84
112
  // shipped in 6.5.0 — older published CLIs reject the flags. A 6.3/6.4
85
113
  // baseline is scaffolded WITHOUT pieces: piece files then appear as
86
114
  // ours-only additions relative to that base, which three-way merge keeps
@@ -94,10 +122,18 @@ async function scaffoldBaseline(
94
122
  );
95
123
  }
96
124
  if (!scaffoldCapable) {
97
- console.warn(
98
- `⚠️ @multiplatform.one/cli@${version} predates the consumer scaffolder (6.3.0); ` +
99
- "using the current CLI's template as the merge base.",
100
- );
125
+ // The local generator is exact for the running version — the capability
126
+ // gate only matters when an OLDER published CLI must be dlx'd.
127
+ if (version !== current) {
128
+ console.warn(
129
+ template === "none"
130
+ ? `⚠️ @multiplatform.one/cli@${version} predates pieces-only baselines (6.6.0); ` +
131
+ "using the current CLI's piece templates as the merge base " +
132
+ "(the merge degrades to two-way: template changes since that version won't land)."
133
+ : `⚠️ @multiplatform.one/cli@${version} predates the consumer scaffolder (6.3.0); ` +
134
+ "using the current CLI's template as the merge base.",
135
+ );
136
+ }
101
137
  } else if (version !== current) {
102
138
  // Older published CLIs reject flags they don't know (--skip-git), so
103
139
  // only the stable flag set is passed; a scaffold-side git repo is
@@ -123,7 +159,10 @@ async function scaffoldBaseline(
123
159
  { cwd: parent, stdio: "inherit" },
124
160
  );
125
161
  const dir = join(parent, name);
126
- if (existsSync(join(dir, "package.json"))) return { dir, cleanup };
162
+ // Pieces-only trees may not contain a root package.json (not every
163
+ // piece ships a root fragment) — provenance is their reliable marker.
164
+ const marker = template === "none" ? PROVENANCE_FILE : "package.json";
165
+ if (existsSync(join(dir, marker))) return { dir, cleanup };
127
166
  console.warn(`⚠️ dlx scaffold for @multiplatform.one/cli@${version} produced no project`);
128
167
  break;
129
168
  } catch {
@@ -357,6 +396,52 @@ async function autoResolveDependencyConflicts(
357
396
  }
358
397
  }
359
398
 
399
+ /**
400
+ * Pre-provenance bootstrap (`mpo update --assume-version <cliVersion>`):
401
+ * projects scaffolded by CLIs older than the provenance file (≤6.3.0) have
402
+ * no .mpo.json. The user asserts which CLI version (and template) produced
403
+ * the project; provenance is written and committed, then the normal update
404
+ * proceeds against that baseline.
405
+ */
406
+ async function bootstrapProvenance(
407
+ projectDir: string,
408
+ options: UpdateAppOptions,
409
+ ): Promise<MpoProvenance> {
410
+ const version = options.assumeVersion!;
411
+ if (!/^\d+\.\d+\.\d+(?:-[\w.]+)?$/u.test(version)) {
412
+ throw new Error(`--assume-version must be an exact CLI version (e.g. 6.3.0), got "${version}"`);
413
+ }
414
+ const rawName = packageJsonName(projectDir);
415
+ if (!rawName) {
416
+ throw new Error(
417
+ `Could not derive the project name from package.json — create ${PROVENANCE_FILE} by hand ` +
418
+ "with { template, cliVersion, name } instead",
419
+ );
420
+ }
421
+ const provenance: MpoProvenance = {
422
+ template: options.assumeTemplate ?? "universal",
423
+ cliVersion: version,
424
+ name: validateName(rawName),
425
+ mpoVersion: `^${version}`,
426
+ pieces: [],
427
+ };
428
+ console.log(
429
+ `\nBootstrapping provenance: assuming this project was scaffolded by ` +
430
+ `cli@${version} (${provenance.template} template).`,
431
+ );
432
+ writeProvenance(projectDir, provenance);
433
+ await git(projectDir, ["add", "--", PROVENANCE_FILE]);
434
+ await git(projectDir, [
435
+ "commit",
436
+ "--quiet",
437
+ "-m",
438
+ `chore: record assumed mpo scaffold provenance (cli@${version})`,
439
+ ]).catch(() => {
440
+ console.warn("⚠️ git commit failed (missing git identity?) — provenance left staged.");
441
+ });
442
+ return provenance;
443
+ }
444
+
360
445
  export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
361
446
  const projectDir = await gitOut(process.cwd(), ["rev-parse", "--show-toplevel"]).catch(() => {
362
447
  throw new Error("mpo update must run inside a git repository");
@@ -370,13 +455,23 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
370
455
  throw new Error("mpo update requires a clean working tree (staged changes present)");
371
456
  });
372
457
 
373
- const provenance = readProvenance(projectDir);
458
+ let provenance = readProvenance(projectDir);
459
+ if (options.assumeVersion) {
460
+ if (provenance) {
461
+ throw new Error(
462
+ `--assume-version is for projects without ${PROVENANCE_FILE} — ` +
463
+ `this project already has provenance (cli@${provenance.cliVersion})`,
464
+ );
465
+ }
466
+ provenance = await bootstrapProvenance(projectDir, options);
467
+ }
374
468
  if (!provenance) {
375
469
  throw new Error(
376
- ".mpo.json not found — this project predates scaffold provenance. " +
377
- "Create it with { template, cliVersion, name } matching how the project " +
378
- "was generated (cliVersion = the @multiplatform.one/cli that scaffolded it), " +
379
- "then re-run `mpo update`.",
470
+ `${PROVENANCE_FILE} not found — this project predates scaffold provenance.\n` +
471
+ " - scaffolded by an old CLI (pre-6.3)? bootstrap it:\n" +
472
+ " mpo update --assume-version <cliVersion> [--template universal|app]\n" +
473
+ " - never scaffolded (hand-adopted pieces)? bring it under management:\n" +
474
+ " mpo adopt --<piece> [...]",
380
475
  );
381
476
  }
382
477
 
@@ -387,22 +482,40 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
387
482
  : `^${options.version}`
388
483
  : `^${currentVersion}`;
389
484
 
485
+ const adopted = provenance.template === "none";
486
+ const firstReconcile = adopted && provenance.reconciled === false;
487
+
390
488
  console.log(`\nmpo update: ${provenance.cliVersion} → ${currentVersion}`);
391
- console.log(` template: ${provenance.template}`);
489
+ console.log(` template: ${provenance.template}${adopted ? " (adopted)" : ""}`);
392
490
  if (provenance.pieces.length) console.log(` pieces: ${provenance.pieces.join(", ")}`);
393
491
  console.log(` project: ${provenance.name}\n`);
492
+ if (firstReconcile) {
493
+ console.log("⚠️ FIRST UPDATE AFTER ADOPTION: piece files are reconciled against the");
494
+ console.log(" piece templates from an empty merge base. Diffs and conflicts on");
495
+ console.log(" hand-copied piece files are EXPECTED — resolve the markers and");
496
+ console.log(" commit. Later updates merge three-way and stay quiet.\n");
497
+ }
394
498
 
395
499
  // 1. Regenerate the ORIGINAL scaffold (merge base). Pieces recorded in
396
500
  // provenance are reproduced in BOTH baselines so piece files merge as
397
- // template content, not as phantom diffs.
398
- console.log(`Scaffolding merge base (cli@${provenance.cliVersion})...`);
399
- const base = await scaffoldBaseline(
400
- provenance.cliVersion,
401
- provenance.name,
402
- provenance.template,
403
- provenance.mpoVersion,
404
- provenance.pieces,
405
- );
501
+ // template content, not as phantom diffs. An adopted project's first
502
+ // update has NO original scaffold — the tool never wrote a file there —
503
+ // so its honest merge base is the empty tree.
504
+ let base: { dir: string; cleanup: () => void };
505
+ if (firstReconcile) {
506
+ console.log("Using an empty merge base (first update after adoption)...");
507
+ const dir = mkdtempSync(join(tmpdir(), "mpo-update-"));
508
+ base = { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
509
+ } else {
510
+ console.log(`Scaffolding merge base (cli@${provenance.cliVersion})...`);
511
+ base = await scaffoldBaseline(
512
+ provenance.cliVersion,
513
+ provenance.name,
514
+ provenance.template,
515
+ provenance.mpoVersion,
516
+ provenance.pieces,
517
+ );
518
+ }
406
519
 
407
520
  // 2. Generate the CURRENT scaffold (theirs).
408
521
  console.log(`Scaffolding update target (cli@${currentVersion})...`);
@@ -465,8 +578,10 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
465
578
 
466
579
  // The merged .mpo.json can interleave ours/theirs lines (add/add
467
580
  // two-way merge) — the post-merge provenance is always written
468
- // authoritatively below.
469
- const finalProvenance = {
581
+ // authoritatively below. `reconciled: false` is intentionally NOT
582
+ // carried over: a successful update reconciles the adopted project,
583
+ // so later updates scaffold their merge base from the recorded version.
584
+ const finalProvenance: MpoProvenance = {
470
585
  template: provenance.template,
471
586
  cliVersion: currentVersion,
472
587
  name: provenance.name,
@@ -474,15 +589,30 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
474
589
  pieces: provenance.pieces,
475
590
  };
476
591
 
477
- // .updateignore pathspecs (+ pnpm-lock.yaml) pin to the pre-update HEAD.
592
+ // .updateignore entries (+ pnpm-lock.yaml) pin to the pre-update HEAD.
478
593
  // Pins are applied to the MERGED TREE before committing, so a clean
479
594
  // update never leaves the tree dirty.
595
+ //
596
+ // Entries are EXACT FILES or DIRECTORY PREFIXES — the pin machinery
597
+ // rides `git ls-tree -- <path>`, which does prefix matching, not
598
+ // fnmatch. A silently dead ignore rule is the worst failure mode, so
599
+ // the one natural glob spelling (`dir/**`, `dir/*`) is normalized to
600
+ // its directory and anything else glob-shaped is refused loudly.
480
601
  const pins = ["pnpm-lock.yaml"];
481
602
  const updateignore = join(projectDir, ".updateignore");
482
603
  if (existsSync(updateignore)) {
483
604
  for (const line of readFileSync(updateignore, "utf-8").split("\n")) {
484
605
  const pattern = line.trim();
485
- if (pattern && !pattern.startsWith("#")) pins.push(pattern);
606
+ if (!pattern || pattern.startsWith("#")) continue;
607
+ const normalized = pattern.replace(/\/\*{1,2}$/, "");
608
+ if (/[*?[\]]/.test(normalized)) {
609
+ throw new Error(
610
+ `.updateignore: "${pattern}" looks like a glob, but entries are ` +
611
+ "exact files or directory prefixes (a glob here would silently " +
612
+ "pin nothing). List the file or directory instead.",
613
+ );
614
+ }
615
+ pins.push(normalized.replace(/\/+$/, ""));
486
616
  }
487
617
  }
488
618
  mergedTree = await pinPathsToHead(projectDir, mergedTree, head, pins);
@@ -514,7 +644,8 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
514
644
  if (!conflicts.length && mergedTree === headTree) {
515
645
  if (
516
646
  provenance.cliVersion !== finalProvenance.cliVersion ||
517
- provenance.mpoVersion !== finalProvenance.mpoVersion
647
+ provenance.mpoVersion !== finalProvenance.mpoVersion ||
648
+ provenance.reconciled === false
518
649
  ) {
519
650
  writeProvenance(projectDir, finalProvenance);
520
651
  await git(projectDir, ["add", "--", ".mpo.json"]);
@@ -18,7 +18,7 @@
18
18
  "react": "^19.2.5",
19
19
  "react-dom": "^19.2.5",
20
20
  "react-native-web": "^0.21.2",
21
- "tamagui": "2.0.0-rc.41"
21
+ "tamagui": "2.7.6"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/react": "~19.2.14",
@@ -20,7 +20,7 @@
20
20
  "react": "^19.2.5",
21
21
  "react-dom": "^19.2.5",
22
22
  "react-native-web": "^0.21.2",
23
- "tamagui": "2.0.0-rc.41"
23
+ "tamagui": "2.7.6"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/react": "~19.2.14",
@@ -1,10 +1,9 @@
1
1
  ## GNOME desktop (GTK4 / GJS)
2
2
 
3
- `mpo init --gnome` added a native GNOME target. Unlike the Tauri target,
4
- which ships the web build inside a system webview, this renders **real
5
- GTK4 widgets** through [react-gnome](https://github.com/react-gjs) — your
6
- Tamagui components become `Gtk.Widget`s laid out by Yoga, with no DOM
7
- anywhere in the process.
3
+ `mpo init --gnome` added a native GNOME target. This is not the web build in
4
+ a frame: it renders **real GTK4 widgets** through
5
+ [react-gnome](https://github.com/react-gjs) — your Tamagui components become
6
+ `Gtk.Widget`s laid out by Yoga, with no DOM anywhere in the process.
8
7
 
9
8
  ### Prerequisites
10
9
 
@@ -46,6 +45,7 @@ get a blank screenshot that looks like a broken app.
46
45
  | `apps/__NAME__/gnome/main.tsx` | Entry — Gtk init, Yoga, providers, window |
47
46
  | `apps/__NAME__/gnome/polyfills.ts` | Browser-API globals. **Must be the first import** |
48
47
  | `apps/__NAME__/gnome/tamagui-barrel.ts` | The granular `@tamagui/*` set this app reaches |
48
+ | `apps/__NAME__/gnome/anchor.tsx` | The meta-package's `Anchor`, replicated — it has no granular home |
49
49
  | `apps/__NAME__/gnome/shims/` | Barrels narrowed to their GJS-safe subgraph |
50
50
  | `apps/__NAME__/vite.config.gnome.ts` | Separate library build → `dist/gnome/main.js` |
51
51
 
@@ -0,0 +1,39 @@
1
+ // GJS replica of the tamagui meta-package's Anchor view.
2
+ //
3
+ // Anchor is defined INSIDE the `tamagui` package (src/views/Anchor.tsx),
4
+ // not in any granular @tamagui/* package the barrel could re-export, and
5
+ // tamagui's export map hides ./src/*. The component is ten lines: a
6
+ // styled SizableText that opens its href through React Native's Linking
7
+ // on press. Replicate exactly that — Linking comes from the react-native
8
+ // alias, where @react-gnome/react-native backs it with Gtk.show_uri.
9
+
10
+ import { styled } from "@tamagui/core";
11
+ import type { SizableTextProps } from "@tamagui/text";
12
+ import { SizableText } from "@tamagui/text";
13
+ import { Linking } from "react-native";
14
+
15
+ export interface AnchorExtraProps {
16
+ href?: string;
17
+ target?: string;
18
+ rel?: string;
19
+ }
20
+
21
+ export type AnchorProps = SizableTextProps & AnchorExtraProps;
22
+
23
+ const AnchorFrame = styled(SizableText, {
24
+ name: "Anchor",
25
+ role: "link",
26
+ });
27
+
28
+ export const Anchor = AnchorFrame.styleable<AnchorExtraProps>(
29
+ ({ href, target: _target, rel: _rel, ...props }, ref) => (
30
+ <AnchorFrame
31
+ {...props}
32
+ onPress={(event) => {
33
+ props.onPress?.(event);
34
+ if (href !== undefined) void Linking.openURL(href);
35
+ }}
36
+ ref={ref as never}
37
+ />
38
+ ),
39
+ );
@@ -82,11 +82,13 @@ function GnomeApp(): React.ReactElement {
82
82
  const app = new Adw.Application({ applicationId: "one.multiplatform.__NAME_APPID__", flags: 0 });
83
83
 
84
84
  app.connect("activate", () => {
85
+ // RGN_WIDTH/RGN_HEIGHT override the window size (same family as
86
+ // RGN_WAIT) so headless captures at other geometries need no rebuild.
85
87
  const window = new AdwApplicationWindowElement({
86
88
  title: "__NAME_PASCAL__",
87
89
  application: app,
88
- defaultWidth: 900,
89
- defaultHeight: 640,
90
+ defaultWidth: Number.parseInt(GLib.getenv("RGN_WIDTH") ?? "900", 10),
91
+ defaultHeight: Number.parseInt(GLib.getenv("RGN_HEIGHT") ?? "640", 10),
90
92
  });
91
93
 
92
94
  render(
@@ -16,6 +16,24 @@
16
16
 
17
17
  export * from "@multiplatform.one/components/src/layouts/page";
18
18
 
19
+ // Async list/table chrome — the wrapper real screens put around their
20
+ // fetches. Its skeleton + error icons stay unrendered until used; the
21
+ // module graph (forms Button/Skeleton via the shims, three tree-shaken
22
+ // phosphor icons) is GJS-inert at init.
23
+ export * from "@multiplatform.one/components/src/layouts/AsyncBoundary";
24
+
25
+ // Chip lives in the FORMS package — the real components barrel re-exports
26
+ // it. Deep-import the forms source directly (the same module instance the
27
+ // forms shim exports) rather than bouncing through the aliased bare name.
28
+ export { Chip, ChipFrame, ChipText, chipIconSize } from "@multiplatform.one/forms/src/Chip";
29
+ export type {
30
+ ChipColor,
31
+ ChipFrameProps,
32
+ ChipProps,
33
+ ChipSize,
34
+ ChipVariant,
35
+ } from "@multiplatform.one/forms/src/Chip";
36
+
19
37
  export {
20
38
  Button,
21
39
  Card,
@@ -34,3 +52,11 @@ export {
34
52
  YStack,
35
53
  isWeb,
36
54
  } from "../tamagui-barrel";
55
+
56
+ // The confirm dialog and the row overflow menu. ConfirmDialog rides
57
+ // Dialog/AnimatePresence; DropdownMenu rides Popover + phosphor's
58
+ // CheckIcon — Popover/Dialog resolve through the gnome tamagui-barrel
59
+ // with vite-plugin-gnome's portal/sheet patches, and phosphor resolves to
60
+ // the SvgXml glyph shim.
61
+ export * from "@multiplatform.one/components/src/feedback/ConfirmDialog";
62
+ export * from "@multiplatform.one/components/src/DropdownMenu";
@@ -16,3 +16,29 @@ export { Input } from "@multiplatform.one/forms/src/fields/Input";
16
16
  export { TextArea } from "@multiplatform.one/forms/src/fields/TextArea";
17
17
  export { Spinner } from "@multiplatform.one/forms/src/Spinner";
18
18
  export { zIndex } from "@multiplatform.one/forms/src/shared/zIndex";
19
+
20
+ // The first components real screens reach for — status chips, loading
21
+ // skeletons, a search field. Chip and Skeleton LIVE here in forms
22
+ // (components re-exports them), and the components package's own
23
+ // Chip/Skeleton modules import the bare package name — which is aliased
24
+ // to this file — so these exports serve both the screens' direct imports
25
+ // and those internal re-export hops.
26
+ export { Chip, ChipFrame, ChipText, chipIconSize } from "@multiplatform.one/forms/src/Chip";
27
+ export type {
28
+ ChipColor,
29
+ ChipFrameProps,
30
+ ChipProps,
31
+ ChipSize,
32
+ ChipVariant,
33
+ } from "@multiplatform.one/forms/src/Chip";
34
+ export { SearchInput } from "@multiplatform.one/forms/src/fields/SearchInput";
35
+ export { Skeleton, SkeletonCircle, SkeletonText } from "@multiplatform.one/forms/src/Skeleton";
36
+ export type {
37
+ SkeletonFrameProps,
38
+ SkeletonProps,
39
+ SkeletonVariant,
40
+ } from "@multiplatform.one/forms/src/Skeleton";
41
+
42
+ // Select stays inside the core + stacks subgraph on the native path.
43
+ export { Select } from "@multiplatform.one/forms/src/fields/Select";
44
+ export type { SelectOption, SelectProps } from "@multiplatform.one/forms/src/fields/Select";
@@ -0,0 +1,17 @@
1
+ // GNOME-target shim for @multiplatform.one/frappe-ui.
2
+ //
3
+ // The real barrel re-exports the whole desk surface — FrappeDashboard,
4
+ // FrappeKanban, the field registry — none of which has a GJS story, and
5
+ // its native-condition build has shipped self-inconsistent (index.native
6
+ // re-exporting names FrappeDashboard.native does not export), which fails
7
+ // the bundle before GJS-safety is even the question.
8
+ //
9
+ // Screens on this target consume exactly one thing from the package:
10
+ // FrappeProvider / useFrappeConfig, so re-export the provider module
11
+ // alone. It is GJS-inert — React context plus a type-only import of
12
+ // @multiplatform.one/frappe.
13
+ //
14
+ // Deep `…/src/*` path rather than the bare package name, which is
15
+ // aliased to this file and would loop.
16
+
17
+ export * from "@multiplatform.one/frappe-ui/src/FrappeProvider";
@@ -30,6 +30,9 @@ import { View } from "../tamagui-barrel";
30
30
  // useEffect and useCallback dependency arrays, so this one must be too.
31
31
  const ROUTER = {
32
32
  push: (href: Href) => stackNavigator.push(href),
33
+ // One's navigate is push-unless-already-there — and the method real
34
+ // screens actually call. Back it with the same push seam.
35
+ navigate: (href: Href) => stackNavigator.push(href),
33
36
  replace: (href: Href) => stackNavigator.replace(href),
34
37
  back: () => stackNavigator.back(),
35
38
  setParams: (params: NavParams) => stackNavigator.setParams(params),
@@ -12,13 +12,34 @@
12
12
  // Deep `…/src/*` paths rather than the bare package name, which is
13
13
  // aliased to this file and would loop.
14
14
 
15
+ // DEV guardrails (devWarn + warn* helpers). The forms package's shared/
16
+ // devWarn re-exports these from THIS barrel, and its Button/Input/Chip
17
+ // import them at module scope. Pure console plumbing, no platform deps.
18
+ export * from "@multiplatform.one/theme/src/devWarn";
15
19
  export * from "@multiplatform.one/theme/src/font";
20
+ // Shared menu-row geometry — DropdownMenu (components shim) reads it at
21
+ // module scope. Pure.
22
+ export * from "@multiplatform.one/theme/src/menuRow";
16
23
  export * from "@multiplatform.one/theme/src/theme/createDefaultThemeConfig";
17
24
  export * from "@multiplatform.one/theme/src/theme/createThemes";
25
+ // Focus-visible ring guarantee (pure color math over ./colorRules).
26
+ export * from "@multiplatform.one/theme/src/theme/focusState";
27
+ // Hairline width — resolves to hairline.native.ts (RN StyleSheet, which
28
+ // the react-native alias backs with the GTK compat package).
29
+ export * from "@multiplatform.one/theme/src/theme/hairline";
30
+ // Intent wrapper — DropdownMenu and ConfirmDialog color their rows and
31
+ // actions through it. Pure.
32
+ export * from "@multiplatform.one/theme/src/theme/Intent";
18
33
  export * from "@multiplatform.one/theme/src/theme/intents";
19
34
  export * from "@multiplatform.one/theme/src/theme/knobs";
35
+ // Press-target and hairline constants (MIN_PRESS_TARGET, hairline, …) the
36
+ // forms Chip and SearchInput read at module scope. Pure numbers/objects.
37
+ export * from "@multiplatform.one/theme/src/theme/layoutTokens";
20
38
  export * from "@multiplatform.one/theme/src/theme/PresetContext";
21
39
  export * from "@multiplatform.one/theme/src/theme/presets";
40
+ // radiusClassProps — imported by layouts/page and the forms Skeleton at
41
+ // module scope since the 6.5/6.6 radius-identity wave. Pure.
42
+ export * from "@multiplatform.one/theme/src/theme/radiusClass";
22
43
  export * from "@multiplatform.one/theme/src/theme/recipes";
23
44
  export * from "@multiplatform.one/theme/src/theme/resolveKnobs";
24
45
  export * from "@multiplatform.one/theme/src/theme/shared";
@@ -56,6 +56,16 @@ export { useComposedRefs } from "@tamagui/compose-refs";
56
56
  export { useControllableState } from "@tamagui/use-controllable-state";
57
57
  export { useWindowDimensions } from "@tamagui/use-window-dimensions";
58
58
 
59
+ // The forms InputParts/ErrorSummary subgraph (pulled in by the shims'
60
+ // Chip + SearchInput) reaches these. group and helpers-tamagui stay
61
+ // inside the safe core + stacks subgraph; Anchor lives in the tamagui
62
+ // META package (no granular home, src unexported), so it's replicated in
63
+ // ./anchor.tsx.
64
+ export { Anchor } from "./anchor";
65
+ export type { AnchorProps } from "./anchor";
66
+ export { XGroup, YGroup } from "@tamagui/group";
67
+ export { useGetThemedIcon } from "@tamagui/helpers-tamagui";
68
+
59
69
  // Type-only names referenced by `import type { … } from "tamagui"` across
60
70
  // the workspace. Erased at build time; listed for tsc and editors.
61
71
  export type {
@@ -75,3 +85,14 @@ export type { StackProps, XStackProps, YStackProps } from "@tamagui/stacks";
75
85
  export type { ButtonProps } from "@tamagui/button";
76
86
  export type { InputProps } from "@tamagui/input";
77
87
  export type { LabelProps } from "@tamagui/label";
88
+
89
+ // Dialog/popover chrome: ConfirmDialog (components shim) rides Dialog +
90
+ // AnimatePresence; DropdownMenu rides Popover. Both native builds stay
91
+ // GJS-safe through @multiplatform.one/vite-plugin-gnome's portal/sheet
92
+ // patches (portal repropagation, sheet relative-position) — that is the
93
+ // header's popover cliff, handled.
94
+ export { Dialog } from "@tamagui/dialog";
95
+ export { Popover } from "@tamagui/popover";
96
+ export { AnimatePresence } from "@tamagui/animate-presence";
97
+ export type { PopoverContentProps, PopoverProps } from "@tamagui/popover";
98
+ export { Sheet } from "@tamagui/sheet";