@trineui/cli 0.1.0-beta.1 → 0.1.1
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 +41 -0
- package/bin/trine.js +24 -0
- package/dist/add-button.js +15 -0
- package/dist/add-component.js +317 -0
- package/dist/index.js +114 -641
- package/package.json +23 -30
- package/templates/button/button.html +11 -0
- package/templates/button/button.skin.ts +128 -0
- package/templates/button/button.ts +39 -0
- package/templates/styles/tokens.css +102 -0
- package/templates/styles/trine-consumer.css +58 -0
- package/CHANGELOG.md +0 -30
- package/src/commands/add.ts +0 -101
- package/src/commands/diff.test.ts +0 -55
- package/src/commands/diff.ts +0 -104
- package/src/commands/eject.ts +0 -95
- package/src/commands/init.ts +0 -92
- package/src/commands/sync-interactive.ts +0 -108
- package/src/commands/sync.test.ts +0 -35
- package/src/commands/sync.ts +0 -113
- package/src/index.ts +0 -18
- package/src/types/manifest.ts +0 -14
- package/src/utils/__tests__/hash.test.ts +0 -35
- package/src/utils/__tests__/template.test.ts +0 -47
- package/src/utils/eject-merger.ts +0 -149
- package/src/utils/hash.ts +0 -43
- package/src/utils/manifest.ts +0 -43
- package/src/utils/template.ts +0 -26
- package/templates/button.blueprint.ts.hbs +0 -41
- package/templates/button.skin.ts.hbs +0 -35
- package/templates/checkbox.blueprint.ts.hbs +0 -57
- package/templates/checkbox.skin.ts.hbs +0 -44
- package/templates/dialog.blueprint.ts.hbs +0 -61
- package/templates/dialog.skin.ts.hbs +0 -27
- package/templates/input.blueprint.ts.hbs +0 -83
- package/templates/input.skin.ts.hbs +0 -29
- package/templates/select.blueprint.ts.hbs +0 -86
- package/templates/select.skin.ts.hbs +0 -53
- package/tsconfig.json +0 -10
package/package.json
CHANGED
|
@@ -1,41 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trineui/cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Trine CLI — add, diff, sync, and eject Angular UI components",
|
|
5
|
-
"keywords": ["angular", "cli", "trine", "ui-components", "code-generation"],
|
|
6
|
-
"author": "Chatchawan Phrueksawan <pete.chatchawan@gmail.com>",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "https://github.com/petechatchawan/forge.git",
|
|
11
|
-
"directory": "packages/cli"
|
|
12
|
-
},
|
|
13
|
-
"homepage": "https://docs.trine-ui.dev/cli/reference",
|
|
14
|
-
"bugs": "https://github.com/petechatchawan/forge/issues",
|
|
3
|
+
"version": "0.1.1",
|
|
15
4
|
"type": "module",
|
|
5
|
+
"description": "Copy-paste ownership CLI for Trine UI components.",
|
|
16
6
|
"main": "./dist/index.js",
|
|
17
|
-
"types": "./dist/index.d.ts",
|
|
18
7
|
"bin": {
|
|
19
|
-
"trine": "
|
|
20
|
-
},
|
|
21
|
-
"engines": {
|
|
22
|
-
"node": ">=18.0.0"
|
|
8
|
+
"trine": "bin/trine.js"
|
|
23
9
|
},
|
|
24
10
|
"scripts": {
|
|
25
|
-
"build": "
|
|
11
|
+
"build": "tsc -p tsconfig.build.json",
|
|
12
|
+
"prepack": "pnpm build"
|
|
26
13
|
},
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
14
|
+
"files": [
|
|
15
|
+
"bin",
|
|
16
|
+
"dist",
|
|
17
|
+
"templates",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
33
22
|
},
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
23
|
+
"keywords": [
|
|
24
|
+
"trine",
|
|
25
|
+
"angular",
|
|
26
|
+
"cli",
|
|
27
|
+
"components",
|
|
28
|
+
"copy-paste",
|
|
29
|
+
"ownership"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"typescript": "^5.9.2"
|
|
40
33
|
}
|
|
41
34
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
export const buttonSkin = cva(
|
|
4
|
+
[
|
|
5
|
+
'inline-flex items-center justify-center gap-2',
|
|
6
|
+
'whitespace-nowrap align-middle',
|
|
7
|
+
'font-medium leading-none',
|
|
8
|
+
'select-none',
|
|
9
|
+
'[&:has(>.sr-only)]:gap-0 [&:has(>.sr-only)]:px-0',
|
|
10
|
+
'rounded-[var(--trine-radius-md)] border',
|
|
11
|
+
'transition-[background-color,border-color,color,box-shadow,transform] duration-[var(--trine-duration-base)] ease-[var(--trine-ease-default)]',
|
|
12
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--trine-color-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--trine-color-bg)]',
|
|
13
|
+
'disabled:pointer-events-none disabled:cursor-not-allowed disabled:text-[var(--trine-color-text-disabled)] disabled:shadow-none',
|
|
14
|
+
],
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
variant: {
|
|
18
|
+
primary: [
|
|
19
|
+
'bg-[var(--trine-color-primary-default)]',
|
|
20
|
+
'border-[var(--trine-color-primary-default)]',
|
|
21
|
+
'text-[var(--trine-color-primary-fg)]',
|
|
22
|
+
'shadow-[var(--trine-shadow-sm)]',
|
|
23
|
+
'hover:bg-[var(--trine-color-primary-hover)] hover:border-[var(--trine-color-primary-hover)]',
|
|
24
|
+
'active:bg-[var(--trine-color-primary-active)] active:border-[var(--trine-color-primary-active)] active:translate-y-px active:shadow-none',
|
|
25
|
+
'disabled:border-[var(--trine-color-primary-subtle)] disabled:bg-[var(--trine-color-primary-subtle)]',
|
|
26
|
+
],
|
|
27
|
+
secondary: [
|
|
28
|
+
'bg-[var(--trine-color-neutral-subtle)]',
|
|
29
|
+
'border-[var(--trine-color-border)]',
|
|
30
|
+
'text-[var(--trine-color-text)]',
|
|
31
|
+
'hover:bg-[var(--trine-color-neutral-hover)] hover:border-[var(--trine-color-border-strong)]',
|
|
32
|
+
'active:bg-[var(--trine-color-neutral-active)] active:border-[var(--trine-color-border-strong)] active:translate-y-px',
|
|
33
|
+
'disabled:border-[var(--trine-color-border)] disabled:bg-[var(--trine-color-neutral-subtle)]',
|
|
34
|
+
],
|
|
35
|
+
ghost: [
|
|
36
|
+
'bg-transparent',
|
|
37
|
+
'border-transparent',
|
|
38
|
+
'text-[var(--trine-color-primary-default)]',
|
|
39
|
+
'hover:bg-[var(--trine-color-primary-subtle)] hover:text-[var(--trine-color-primary-hover)]',
|
|
40
|
+
'active:bg-[var(--trine-color-primary-subtle)] active:text-[var(--trine-color-primary-active)] active:translate-y-px',
|
|
41
|
+
'disabled:bg-transparent disabled:border-transparent disabled:text-[var(--trine-color-text-disabled)]',
|
|
42
|
+
],
|
|
43
|
+
danger: [
|
|
44
|
+
'bg-[var(--trine-color-danger-default)]',
|
|
45
|
+
'border-[var(--trine-color-danger-default)]',
|
|
46
|
+
'text-[var(--trine-color-danger-fg)]',
|
|
47
|
+
'shadow-[var(--trine-shadow-sm)]',
|
|
48
|
+
'hover:bg-[var(--trine-color-danger-hover)] hover:border-[var(--trine-color-danger-hover)]',
|
|
49
|
+
'active:bg-[var(--trine-color-danger-active)] active:border-[var(--trine-color-danger-active)] active:translate-y-px active:shadow-none',
|
|
50
|
+
'disabled:border-[var(--trine-color-danger-subtle)] disabled:bg-[var(--trine-color-danger-subtle)]',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
size: {
|
|
54
|
+
sm: ['min-h-8 min-w-8 px-3 text-xs'],
|
|
55
|
+
md: ['min-h-9 min-w-9 px-4 text-sm'],
|
|
56
|
+
lg: ['min-h-11 min-w-11 px-6 text-base'],
|
|
57
|
+
},
|
|
58
|
+
loading: {
|
|
59
|
+
true: ['cursor-progress'],
|
|
60
|
+
false: null,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
compoundVariants: [
|
|
64
|
+
{
|
|
65
|
+
variant: 'primary',
|
|
66
|
+
loading: true,
|
|
67
|
+
class: [
|
|
68
|
+
'[&[aria-busy=true]]:border-[var(--trine-color-primary-default)]',
|
|
69
|
+
'[&[aria-busy=true]]:bg-[var(--trine-color-primary-default)]',
|
|
70
|
+
'[&[aria-busy=true]]:text-[var(--trine-color-primary-fg)]',
|
|
71
|
+
'[&[aria-busy=true]]:shadow-[var(--trine-shadow-sm)]',
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
variant: 'secondary',
|
|
76
|
+
loading: true,
|
|
77
|
+
class: [
|
|
78
|
+
'[&[aria-busy=true]]:border-[var(--trine-color-border)]',
|
|
79
|
+
'[&[aria-busy=true]]:bg-[var(--trine-color-neutral-subtle)]',
|
|
80
|
+
'[&[aria-busy=true]]:text-[var(--trine-color-text)]',
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
variant: 'ghost',
|
|
85
|
+
loading: true,
|
|
86
|
+
class: [
|
|
87
|
+
'[&[aria-busy=true]]:border-transparent',
|
|
88
|
+
'[&[aria-busy=true]]:bg-[var(--trine-color-primary-subtle)]',
|
|
89
|
+
'[&[aria-busy=true]]:text-[var(--trine-color-primary-default)]',
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
variant: 'danger',
|
|
94
|
+
loading: true,
|
|
95
|
+
class: [
|
|
96
|
+
'[&[aria-busy=true]]:border-[var(--trine-color-danger-default)]',
|
|
97
|
+
'[&[aria-busy=true]]:bg-[var(--trine-color-danger-default)]',
|
|
98
|
+
'[&[aria-busy=true]]:text-[var(--trine-color-danger-fg)]',
|
|
99
|
+
'[&[aria-busy=true]]:shadow-[var(--trine-shadow-sm)]',
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
defaultVariants: {
|
|
104
|
+
variant: 'primary',
|
|
105
|
+
size: 'md',
|
|
106
|
+
loading: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
export const buttonSpinnerSkin = cva(
|
|
112
|
+
[
|
|
113
|
+
'inline-block shrink-0 rounded-full border-current border-solid border-r-transparent',
|
|
114
|
+
'animate-spin motion-reduce:animate-none',
|
|
115
|
+
],
|
|
116
|
+
{
|
|
117
|
+
variants: {
|
|
118
|
+
size: {
|
|
119
|
+
sm: ['h-3 w-3 border-2'],
|
|
120
|
+
md: ['h-3.5 w-3.5 border-2'],
|
|
121
|
+
lg: ['h-4 w-4 border-2'],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
defaultVariants: {
|
|
125
|
+
size: 'md',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
|
|
2
|
+
import { buttonSkin, buttonSpinnerSkin } from './button.skin';
|
|
3
|
+
|
|
4
|
+
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
|
|
5
|
+
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
6
|
+
export type ButtonType = 'button' | 'submit' | 'reset';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'trine-button',
|
|
10
|
+
templateUrl: './button.html',
|
|
11
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
12
|
+
host: {
|
|
13
|
+
'[attr.data-variant]': 'variant()',
|
|
14
|
+
'[attr.data-size]': 'size()',
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
export class ButtonComponent {
|
|
18
|
+
readonly variant = input<ButtonVariant>('primary');
|
|
19
|
+
readonly size = input<ButtonSize>('md');
|
|
20
|
+
readonly disabled = input<boolean>(false);
|
|
21
|
+
readonly loading = input<boolean>(false);
|
|
22
|
+
readonly type = input<ButtonType>('button');
|
|
23
|
+
|
|
24
|
+
protected readonly isDisabled = computed(() => this.disabled() || this.loading());
|
|
25
|
+
|
|
26
|
+
protected readonly classes = computed(() =>
|
|
27
|
+
buttonSkin({
|
|
28
|
+
variant: this.variant(),
|
|
29
|
+
size: this.size(),
|
|
30
|
+
loading: this.loading(),
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
protected readonly spinnerClasses = computed(() =>
|
|
35
|
+
buttonSpinnerSkin({
|
|
36
|
+
size: this.size(),
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--trine-color-primary-default: oklch(0.6204 0.195 253.83);
|
|
3
|
+
--trine-color-primary-hover: oklch(0.5804 0.195 253.83);
|
|
4
|
+
--trine-color-primary-active: oklch(0.5404 0.195 253.83);
|
|
5
|
+
--trine-color-primary-subtle: oklch(0.9524 0.02 253.83);
|
|
6
|
+
--trine-color-primary-fg: oklch(0.9911 0 0);
|
|
7
|
+
|
|
8
|
+
--trine-color-danger-default: oklch(0.6532 0.2335 25.74);
|
|
9
|
+
--trine-color-danger-hover: oklch(0.6132 0.2335 25.74);
|
|
10
|
+
--trine-color-danger-active: oklch(0.5732 0.2335 25.74);
|
|
11
|
+
--trine-color-danger-subtle: oklch(0.9524 0.025 25.74);
|
|
12
|
+
--trine-color-danger-fg: oklch(0.9911 0 0);
|
|
13
|
+
|
|
14
|
+
--trine-color-success-default: oklch(0.7329 0.1941 150.81);
|
|
15
|
+
--trine-color-success-hover: oklch(0.6929 0.1941 150.81);
|
|
16
|
+
--trine-color-success-active: oklch(0.6529 0.1941 150.81);
|
|
17
|
+
--trine-color-success-subtle: oklch(0.9524 0.02 150.81);
|
|
18
|
+
--trine-color-success-fg: oklch(0.2103 0.0059 150.81);
|
|
19
|
+
|
|
20
|
+
--trine-color-warning-default: oklch(0.7819 0.159 72.33);
|
|
21
|
+
--trine-color-warning-hover: oklch(0.7419 0.159 72.33);
|
|
22
|
+
--trine-color-warning-active: oklch(0.7019 0.159 72.33);
|
|
23
|
+
--trine-color-warning-subtle: oklch(0.9524 0.02 72.33);
|
|
24
|
+
--trine-color-warning-fg: oklch(0.2103 0.0059 72.33);
|
|
25
|
+
|
|
26
|
+
--trine-color-neutral-default: oklch(0.5517 0.003 253.83);
|
|
27
|
+
--trine-color-neutral-hover: oklch(0.9373 0.0012 253.83);
|
|
28
|
+
--trine-color-neutral-active: oklch(0.92 0.0015 253.83);
|
|
29
|
+
--trine-color-neutral-subtle: oklch(0.94 0.0015 253.83);
|
|
30
|
+
--trine-color-neutral-fg: oklch(0.2103 0.0059 253.83);
|
|
31
|
+
|
|
32
|
+
--trine-color-bg: oklch(0.9702 0.0015 253.83);
|
|
33
|
+
--trine-color-bg-elevated: oklch(1 0.0008 253.83);
|
|
34
|
+
--trine-color-bg-overlay: oklch(1 0.0004 253.83 / 0.82);
|
|
35
|
+
--trine-color-border: oklch(0.9 0.0015 253.83);
|
|
36
|
+
--trine-color-border-strong: oklch(0.871 0.0015 253.83);
|
|
37
|
+
--trine-color-ring: oklch(0.6204 0.195 253.83);
|
|
38
|
+
--trine-color-text: oklch(0.2103 0.0015 253.83);
|
|
39
|
+
--trine-color-text-subtle: oklch(0.5517 0.003 253.83);
|
|
40
|
+
--trine-color-text-disabled: oklch(0.5517 0.003 253.83);
|
|
41
|
+
|
|
42
|
+
--trine-radius-none: 0;
|
|
43
|
+
--trine-radius-sm: 0.375rem;
|
|
44
|
+
--trine-radius-md: 0.5rem;
|
|
45
|
+
--trine-radius-lg: 0.75rem;
|
|
46
|
+
--trine-radius-xl: 1rem;
|
|
47
|
+
--trine-radius-2xl: 1.25rem;
|
|
48
|
+
--trine-radius-full: 9999px;
|
|
49
|
+
|
|
50
|
+
--trine-shadow-sm: 0 1px 2px 0 oklch(0.16 0.01 258 / 0.12);
|
|
51
|
+
--trine-shadow-md: 0 10px 20px -10px oklch(0.16 0.01 258 / 0.22);
|
|
52
|
+
--trine-shadow-lg: 0 20px 40px -20px oklch(0.16 0.01 258 / 0.28);
|
|
53
|
+
|
|
54
|
+
--trine-duration-fast: 120ms;
|
|
55
|
+
--trine-duration-base: 180ms;
|
|
56
|
+
--trine-duration-slow: 280ms;
|
|
57
|
+
--trine-ease-default: cubic-bezier(0.2, 0, 0, 1);
|
|
58
|
+
--trine-ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
59
|
+
--trine-ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
html.dark {
|
|
63
|
+
--trine-color-primary-default: oklch(0.6204 0.195 253.83);
|
|
64
|
+
--trine-color-primary-hover: oklch(0.6704 0.195 253.83);
|
|
65
|
+
--trine-color-primary-active: oklch(0.5804 0.195 253.83);
|
|
66
|
+
--trine-color-primary-subtle: oklch(0.274 0.02 253.83);
|
|
67
|
+
--trine-color-primary-fg: oklch(0.9911 0 0);
|
|
68
|
+
|
|
69
|
+
--trine-color-danger-default: oklch(0.594 0.1973 24.63);
|
|
70
|
+
--trine-color-danger-hover: oklch(0.634 0.1973 24.63);
|
|
71
|
+
--trine-color-danger-active: oklch(0.554 0.1973 24.63);
|
|
72
|
+
--trine-color-danger-subtle: oklch(0.257 0.02 24.63);
|
|
73
|
+
--trine-color-danger-fg: oklch(0.9911 0 0);
|
|
74
|
+
|
|
75
|
+
--trine-color-success-default: oklch(0.7329 0.1941 150.81);
|
|
76
|
+
--trine-color-success-hover: oklch(0.7729 0.1941 150.81);
|
|
77
|
+
--trine-color-success-active: oklch(0.6929 0.1941 150.81);
|
|
78
|
+
--trine-color-success-subtle: oklch(0.257 0.02 150.81);
|
|
79
|
+
--trine-color-success-fg: oklch(0.2103 0.0059 150.81);
|
|
80
|
+
|
|
81
|
+
--trine-color-warning-default: oklch(0.8203 0.1392 76.34);
|
|
82
|
+
--trine-color-warning-hover: oklch(0.8603 0.1392 76.34);
|
|
83
|
+
--trine-color-warning-active: oklch(0.7803 0.1392 76.34);
|
|
84
|
+
--trine-color-warning-subtle: oklch(0.2721 0.02 76.34);
|
|
85
|
+
--trine-color-warning-fg: oklch(0.2103 0.0059 76.34);
|
|
86
|
+
|
|
87
|
+
--trine-color-neutral-default: oklch(0.705 0.003 253.83);
|
|
88
|
+
--trine-color-neutral-hover: oklch(0.3964 0.0015 253.83);
|
|
89
|
+
--trine-color-neutral-active: oklch(0.257 0.0023 253.83);
|
|
90
|
+
--trine-color-neutral-subtle: oklch(0.274 0.0015 253.83);
|
|
91
|
+
--trine-color-neutral-fg: oklch(0.9911 0.0015 253.83);
|
|
92
|
+
|
|
93
|
+
--trine-color-bg: oklch(0.12 0.0015 253.83);
|
|
94
|
+
--trine-color-bg-elevated: oklch(0.2103 0.003 253.83);
|
|
95
|
+
--trine-color-bg-overlay: oklch(0.2103 0.003 253.83 / 0.82);
|
|
96
|
+
--trine-color-border: oklch(0.28 0.0015 253.83);
|
|
97
|
+
--trine-color-border-strong: oklch(0.3964 0.0015 253.83);
|
|
98
|
+
--trine-color-ring: oklch(0.6204 0.195 253.83);
|
|
99
|
+
--trine-color-text: oklch(0.9911 0.0015 253.83);
|
|
100
|
+
--trine-color-text-subtle: oklch(0.705 0.003 253.83);
|
|
101
|
+
--trine-color-text-disabled: oklch(0.705 0.003 253.83);
|
|
102
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* Consumer-safe styling/bootstrap baseline for copy-paste ownership delivery. */
|
|
2
|
+
@import './tokens.css';
|
|
3
|
+
@import 'tailwindcss';
|
|
4
|
+
|
|
5
|
+
@source '../app';
|
|
6
|
+
|
|
7
|
+
@theme {
|
|
8
|
+
--color-trine-primary: var(--trine-color-primary-default);
|
|
9
|
+
--color-trine-primary-hover: var(--trine-color-primary-hover);
|
|
10
|
+
--color-trine-primary-active: var(--trine-color-primary-active);
|
|
11
|
+
--color-trine-primary-subtle: var(--trine-color-primary-subtle);
|
|
12
|
+
--color-trine-primary-fg: var(--trine-color-primary-fg);
|
|
13
|
+
|
|
14
|
+
--color-trine-danger: var(--trine-color-danger-default);
|
|
15
|
+
--color-trine-danger-hover: var(--trine-color-danger-hover);
|
|
16
|
+
--color-trine-danger-active: var(--trine-color-danger-active);
|
|
17
|
+
--color-trine-danger-subtle: var(--trine-color-danger-subtle);
|
|
18
|
+
--color-trine-danger-fg: var(--trine-color-danger-fg);
|
|
19
|
+
|
|
20
|
+
--color-trine-success: var(--trine-color-success-default);
|
|
21
|
+
--color-trine-success-hover: var(--trine-color-success-hover);
|
|
22
|
+
--color-trine-success-active: var(--trine-color-success-active);
|
|
23
|
+
--color-trine-success-subtle: var(--trine-color-success-subtle);
|
|
24
|
+
--color-trine-success-fg: var(--trine-color-success-fg);
|
|
25
|
+
|
|
26
|
+
--color-trine-warning: var(--trine-color-warning-default);
|
|
27
|
+
--color-trine-warning-hover: var(--trine-color-warning-hover);
|
|
28
|
+
--color-trine-warning-active: var(--trine-color-warning-active);
|
|
29
|
+
--color-trine-warning-subtle: var(--trine-color-warning-subtle);
|
|
30
|
+
--color-trine-warning-fg: var(--trine-color-warning-fg);
|
|
31
|
+
|
|
32
|
+
--color-trine-neutral: var(--trine-color-neutral-default);
|
|
33
|
+
--color-trine-neutral-hover: var(--trine-color-neutral-hover);
|
|
34
|
+
--color-trine-neutral-active: var(--trine-color-neutral-active);
|
|
35
|
+
--color-trine-neutral-subtle: var(--trine-color-neutral-subtle);
|
|
36
|
+
--color-trine-neutral-fg: var(--trine-color-neutral-fg);
|
|
37
|
+
|
|
38
|
+
--color-trine-bg: var(--trine-color-bg);
|
|
39
|
+
--color-trine-bg-elevated: var(--trine-color-bg-elevated);
|
|
40
|
+
--color-trine-bg-overlay: var(--trine-color-bg-overlay);
|
|
41
|
+
--color-trine-border: var(--trine-color-border);
|
|
42
|
+
--color-trine-border-strong: var(--trine-color-border-strong);
|
|
43
|
+
--color-trine-text: var(--trine-color-text);
|
|
44
|
+
--color-trine-text-subtle: var(--trine-color-text-subtle);
|
|
45
|
+
--color-trine-text-disabled: var(--trine-color-text-disabled);
|
|
46
|
+
|
|
47
|
+
--radius-trine-none: var(--trine-radius-none);
|
|
48
|
+
--radius-trine-sm: var(--trine-radius-sm);
|
|
49
|
+
--radius-trine-md: var(--trine-radius-md);
|
|
50
|
+
--radius-trine-lg: var(--trine-radius-lg);
|
|
51
|
+
--radius-trine-xl: var(--trine-radius-xl);
|
|
52
|
+
--radius-trine-2xl: var(--trine-radius-2xl);
|
|
53
|
+
--radius-trine-full: var(--trine-radius-full);
|
|
54
|
+
|
|
55
|
+
--shadow-trine-sm: var(--trine-shadow-sm);
|
|
56
|
+
--shadow-trine-md: var(--trine-shadow-md);
|
|
57
|
+
--shadow-trine-lg: var(--trine-shadow-lg);
|
|
58
|
+
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# @trine/cli Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.0-beta.1 (2026-03-22)
|
|
4
|
-
|
|
5
|
-
Initial beta release.
|
|
6
|
-
|
|
7
|
-
### Commands
|
|
8
|
-
|
|
9
|
-
- `trine add <component>` — Generate blueprint + skin files from templates
|
|
10
|
-
- `trine diff <component>` — Show local and upstream status (read-only)
|
|
11
|
-
- `trine sync <component>` — Auto-overwrite untouched blueprint with upstream changes
|
|
12
|
-
- `trine sync <component> --interactive` — 3-way merge TUI for reviewing modified blueprints
|
|
13
|
-
- `trine eject <component>` — Merge blueprint + skin into standalone component, one-way exit from lifecycle
|
|
14
|
-
- `trine init` — Reconstruct manifest.json from existing blueprint files
|
|
15
|
-
|
|
16
|
-
### Sync Model
|
|
17
|
-
|
|
18
|
-
- Normalized text hash v0.1 for change detection
|
|
19
|
-
- manifest.json (`.trine/manifest.json`) is source of truth for sync state
|
|
20
|
-
- Gate G1 validated: Prettier reformat does not trigger false-positive dirty signal
|
|
21
|
-
|
|
22
|
-
### Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install -g @trine/cli@beta
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
### Requirements
|
|
29
|
-
|
|
30
|
-
- Node.js >=18.0.0
|
package/src/commands/add.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import {
|
|
3
|
-
writeFileSync,
|
|
4
|
-
existsSync,
|
|
5
|
-
mkdirSync,
|
|
6
|
-
} from 'fs';
|
|
7
|
-
import { resolve, join } from 'path';
|
|
8
|
-
import { computeNormalizedHash } from '../utils/hash.js';
|
|
9
|
-
import { readManifest, writeManifest } from '../utils/manifest.js';
|
|
10
|
-
import { renderTemplate } from '../utils/template.js';
|
|
11
|
-
import type { ComponentEntry, TrineManifest } from '../types/manifest.js';
|
|
12
|
-
|
|
13
|
-
const ENGINE_VERSION = '0.1.0';
|
|
14
|
-
const BLUEPRINT_SCHEMA_VERSION = '1.0.0';
|
|
15
|
-
|
|
16
|
-
export const addCommand = new Command('add')
|
|
17
|
-
.argument('<component>', 'Component name (e.g. button)')
|
|
18
|
-
.option('--force', 'Overwrite existing blueprint (skin is NEVER overwritten)')
|
|
19
|
-
.option(
|
|
20
|
-
'--dir <path>',
|
|
21
|
-
'Output directory relative to project root',
|
|
22
|
-
'src/components/ui'
|
|
23
|
-
)
|
|
24
|
-
.description('Add a component blueprint + skin to your project')
|
|
25
|
-
.action(async (component: string, options: { force?: boolean; dir: string }) => {
|
|
26
|
-
const projectRoot = process.cwd();
|
|
27
|
-
const componentDir = resolve(projectRoot, options.dir, component);
|
|
28
|
-
const blueprintPath = join(componentDir, `${component}.blueprint.ts`);
|
|
29
|
-
const skinPath = join(componentDir, `${component}.skin.ts`);
|
|
30
|
-
|
|
31
|
-
if (!isSupportedComponent(component)) {
|
|
32
|
-
console.error(`✗ Unknown component: "${component}"`);
|
|
33
|
-
console.error(` Supported: button, input, dialog, checkbox, select`);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (existsSync(blueprintPath) && !options.force) {
|
|
38
|
-
console.error(`✗ ${component}.blueprint.ts already exists`);
|
|
39
|
-
console.error('');
|
|
40
|
-
console.error(` To overwrite blueprint (skin will NOT be touched):`);
|
|
41
|
-
console.error(` trine add ${component} --force`);
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const ctx = {
|
|
46
|
-
engineVersion: ENGINE_VERSION,
|
|
47
|
-
blueprintSchemaVersion: BLUEPRINT_SCHEMA_VERSION,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const blueprintContent = renderTemplate(`${component}.blueprint.ts.hbs`, ctx);
|
|
51
|
-
const skinContent = renderTemplate(`${component}.skin.ts.hbs`, ctx);
|
|
52
|
-
|
|
53
|
-
mkdirSync(componentDir, { recursive: true });
|
|
54
|
-
|
|
55
|
-
writeFileSync(blueprintPath, blueprintContent, 'utf-8');
|
|
56
|
-
console.log(` ✓ ${options.dir}/${component}/${component}.blueprint.ts`);
|
|
57
|
-
|
|
58
|
-
if (!existsSync(skinPath)) {
|
|
59
|
-
writeFileSync(skinPath, skinContent, 'utf-8');
|
|
60
|
-
console.log(` ✓ ${options.dir}/${component}/${component}.skin.ts`);
|
|
61
|
-
} else {
|
|
62
|
-
console.log(` ~ ${options.dir}/${component}/${component}.skin.ts (kept — user-owned)`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const existingManifest = readManifest(projectRoot);
|
|
66
|
-
const manifest: TrineManifest = existingManifest ?? {
|
|
67
|
-
'trine-spec': '1.0',
|
|
68
|
-
components: {},
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const blueprintHash = computeNormalizedHash(blueprintContent);
|
|
72
|
-
|
|
73
|
-
const entry: ComponentEntry = {
|
|
74
|
-
'engine-version': ENGINE_VERSION,
|
|
75
|
-
'blueprint-schema-version': BLUEPRINT_SCHEMA_VERSION,
|
|
76
|
-
'blueprint-hash': blueprintHash,
|
|
77
|
-
'blueprint-modified': false,
|
|
78
|
-
'synced-at': new Date().toISOString(),
|
|
79
|
-
'sync-model': 'normalized-text-v0.1',
|
|
80
|
-
'output-dir': options.dir,
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
manifest.components[component] = entry;
|
|
84
|
-
writeManifest(manifest, projectRoot);
|
|
85
|
-
console.log(` ✓ .trine/manifest.json`);
|
|
86
|
-
|
|
87
|
-
console.log('');
|
|
88
|
-
console.log(`✓ trine add ${component} complete`);
|
|
89
|
-
console.log('');
|
|
90
|
-
console.log(' Next steps:');
|
|
91
|
-
console.log(` Import ${capitalize(component)}Component in your module/component`);
|
|
92
|
-
console.log(` Run: trine diff ${component} — check for upstream changes`);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
function capitalize(s: string): string {
|
|
96
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function isSupportedComponent(name: string): boolean {
|
|
100
|
-
return ['button', 'input', 'dialog', 'checkbox', 'select'].includes(name);
|
|
101
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { computeNormalizedHash } from '../utils/hash.js';
|
|
3
|
-
|
|
4
|
-
describe('hash normalization (Gate G1)', () => {
|
|
5
|
-
it('same content → same hash', () => {
|
|
6
|
-
const content = `import { Component } from '@angular/core';
|
|
7
|
-
@Component({ selector: 'ui-button' })`;
|
|
8
|
-
expect(computeNormalizedHash(content)).toBe(computeNormalizedHash(content));
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('prettier-formatted single quotes → double quotes → same hash', () => {
|
|
12
|
-
const original = `import { Component } from '@angular/core';`;
|
|
13
|
-
const prettified = `import { Component } from "@angular/core";`;
|
|
14
|
-
expect(computeNormalizedHash(original)).toBe(computeNormalizedHash(prettified));
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('prettier multi-line → single line → same hash', () => {
|
|
18
|
-
const original = `@Component({
|
|
19
|
-
selector: 'ui-button',
|
|
20
|
-
standalone: true
|
|
21
|
-
})`;
|
|
22
|
-
const prettified = `@Component({ selector: 'ui-button', standalone: true })`;
|
|
23
|
-
expect(computeNormalizedHash(original)).toBe(computeNormalizedHash(prettified));
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('prettier arrays with/without trailing comma → same hash', () => {
|
|
27
|
-
const noTrailing = `@Component({ inputs: ['a', 'b'] })`;
|
|
28
|
-
const withTrailing = `@Component({ inputs: ['a', 'b',] })`;
|
|
29
|
-
expect(computeNormalizedHash(noTrailing)).toBe(computeNormalizedHash(withTrailing));
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('prettier object spacing variations → same hash', () => {
|
|
33
|
-
const tight = `@Component({selector:'ui-button',standalone:true})`;
|
|
34
|
-
const spaced = `@Component({ selector: 'ui-button', standalone: true })`;
|
|
35
|
-
const multiline = `@Component({
|
|
36
|
-
selector: 'ui-button',
|
|
37
|
-
standalone: true
|
|
38
|
-
})`;
|
|
39
|
-
const h1 = computeNormalizedHash(tight);
|
|
40
|
-
expect(computeNormalizedHash(spaced)).toBe(h1);
|
|
41
|
-
expect(computeNormalizedHash(multiline)).toBe(h1);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('structural change → different hash', () => {
|
|
45
|
-
const v1 = `@Component({ selector: 'ui-button' })`;
|
|
46
|
-
const v2 = `@Component({ selector: 'ui-btn' })`;
|
|
47
|
-
expect(computeNormalizedHash(v1)).not.toBe(computeNormalizedHash(v2));
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('import order different → same hash (sorted)', () => {
|
|
51
|
-
const order1 = `import { A } from 'x';import { B } from 'y';`;
|
|
52
|
-
const order2 = `import { B } from 'y';import { A } from 'x';`;
|
|
53
|
-
expect(computeNormalizedHash(order1)).toBe(computeNormalizedHash(order2));
|
|
54
|
-
});
|
|
55
|
-
});
|