@multiplatform.one/cli 6.7.0 → 7.1.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/README.md +83 -8
- package/lib/bin/multiplatformOne.mjs +21 -7
- package/lib/commands/adoptApp.mjs +127 -0
- package/lib/commands/init.mjs +1 -1
- package/lib/commands/initApp.mjs +43 -15
- package/lib/commands/updateApp.mjs +84 -15
- package/package.json +2 -2
- package/scripts/frappe-app-name.py +125 -0
- package/scripts/frappe-app-name.spec.ts +191 -0
- package/scripts/frappe-bootstrap.sh +8 -30
- package/src/bin/multiplatformOne.ts +78 -13
- package/src/commands/adoptApp.spec.ts +208 -0
- package/src/commands/adoptApp.ts +185 -0
- package/src/commands/initApp.spec.ts +152 -12
- package/src/commands/initApp.ts +95 -21
- package/src/commands/updateApp.spec.ts +314 -2
- package/src/commands/updateApp.ts +164 -33
- package/templates/app/apps/__NAME__/package.json +1 -1
- package/templates/app/features/__NAME__/package.json +1 -1
- package/templates/pieces/gnome/universal/README.md.partial +5 -5
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/anchor.tsx +39 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/main.tsx +4 -2
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/components.ts +26 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/forms.ts +26 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/frappe-ui.ts +17 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/one.ts +3 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/theme.ts +21 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/gnome/tamagui-barrel.ts +21 -0
- package/templates/pieces/gnome/universal/apps/__NAME__/vite.config.gnome.ts +49 -7
- package/templates/pieces/keycloak/universal/README.md.partial +13 -0
- package/templates/pieces/keycloak/universal/docker/compose.keycloak.yaml +19 -3
- package/templates/pieces/keycloak/universal/env.example.partial +4 -1
- package/templates/pieces/vscode/universal/apps/__NAME__/package.json.partial +1 -1
- package/templates/pieces/webext/universal/apps/__NAME__/package.json.partial +2 -2
- package/templates/universal/apps/__NAME__/package.json +2 -2
- package/templates/universal/packages/themes/package.json +2 -2
- package/types/bin/multiplatformOne.d.ts.map +1 -1
- package/types/commands/adoptApp.d.ts +25 -0
- package/types/commands/adoptApp.d.ts.map +1 -0
- package/types/commands/initApp.d.ts +33 -5
- package/types/commands/initApp.d.ts.map +1 -1
- package/types/commands/updateApp.d.ts +12 -2
- 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
|
|
15
|
-
*
|
|
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 {
|
|
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:
|
|
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 =
|
|
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 =
|
|
83
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377
|
-
"
|
|
378
|
-
"
|
|
379
|
-
"
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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"]);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
## GNOME desktop (GTK4 / GJS)
|
|
2
2
|
|
|
3
|
-
`mpo init --gnome` added a native GNOME target.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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";
|