@loomweaver/devkit 0.7.2
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 +127 -0
- package/generators.json +41 -0
- package/package.json +38 -0
- package/src/generators/auth-source/generator.d.ts +4 -0
- package/src/generators/auth-source/generator.js +17 -0
- package/src/generators/auth-source/generator.js.map +1 -0
- package/src/generators/auth-source/schema.json +18 -0
- package/src/generators/distribution/generator.d.ts +4 -0
- package/src/generators/distribution/generator.js +61 -0
- package/src/generators/distribution/generator.js.map +1 -0
- package/src/generators/distribution/nx-files.d.ts +22 -0
- package/src/generators/distribution/nx-files.js +133 -0
- package/src/generators/distribution/nx-files.js.map +1 -0
- package/src/generators/distribution/schema.json +48 -0
- package/src/generators/frame-plugin/generator.d.ts +4 -0
- package/src/generators/frame-plugin/generator.js +20 -0
- package/src/generators/frame-plugin/generator.js.map +1 -0
- package/src/generators/frame-plugin/schema.json +22 -0
- package/src/generators/layout/generator.d.ts +4 -0
- package/src/generators/layout/generator.js +17 -0
- package/src/generators/layout/generator.js.map +1 -0
- package/src/generators/layout/schema.json +17 -0
- package/src/generators/nx-scaffold-shared.d.ts +32 -0
- package/src/generators/nx-scaffold-shared.js +87 -0
- package/src/generators/nx-scaffold-shared.js.map +1 -0
- package/src/generators/settings-store/generator.d.ts +4 -0
- package/src/generators/settings-store/generator.js +17 -0
- package/src/generators/settings-store/generator.js.map +1 -0
- package/src/generators/settings-store/schema.json +18 -0
- package/src/generators/shared.d.ts +47 -0
- package/src/generators/shared.js +152 -0
- package/src/generators/shared.js.map +1 -0
- package/src/generators/theme/generator.d.ts +4 -0
- package/src/generators/theme/generator.js +17 -0
- package/src/generators/theme/generator.js.map +1 -0
- package/src/generators/theme/schema.json +24 -0
- package/src/generators/weaver/generator.d.ts +4 -0
- package/src/generators/weaver/generator.js +75 -0
- package/src/generators/weaver/generator.js.map +1 -0
- package/src/generators/weaver/nx-files.d.ts +26 -0
- package/src/generators/weaver/nx-files.js +73 -0
- package/src/generators/weaver/nx-files.js.map +1 -0
- package/src/generators/weaver/schema.json +97 -0
- package/src/index.d.ts +15 -0
- package/src/index.js +47 -0
- package/src/index.js.map +1 -0
- package/src/lib/generate/casing.d.ts +4 -0
- package/src/lib/generate/casing.js +27 -0
- package/src/lib/generate/casing.js.map +1 -0
- package/src/lib/generate/generate.d.ts +2 -0
- package/src/lib/generate/generate.js +18 -0
- package/src/lib/generate/generate.js.map +1 -0
- package/src/lib/generate/types.d.ts +5 -0
- package/src/lib/generate/types.js +3 -0
- package/src/lib/generate/types.js.map +1 -0
- package/src/lib/scaffolds/scaffolds.d.ts +37 -0
- package/src/lib/scaffolds/scaffolds.js +357 -0
- package/src/lib/scaffolds/scaffolds.js.map +1 -0
- package/src/lib/validate/catalog.d.ts +15 -0
- package/src/lib/validate/catalog.js +247 -0
- package/src/lib/validate/catalog.js.map +1 -0
- package/src/lib/validate/i18n.d.ts +3 -0
- package/src/lib/validate/i18n.js +41 -0
- package/src/lib/validate/i18n.js.map +1 -0
- package/src/lib/validate/manifest.d.ts +8 -0
- package/src/lib/validate/manifest.js +74 -0
- package/src/lib/validate/manifest.js.map +1 -0
- package/src/lib/validate/types.d.ts +7 -0
- package/src/lib/validate/types.js +3 -0
- package/src/lib/validate/types.js.map +1 -0
- package/src/recipes/angular-distribution/logo.d.ts +1 -0
- package/src/recipes/angular-distribution/logo.js +9 -0
- package/src/recipes/angular-distribution/logo.js.map +1 -0
- package/src/recipes/angular-distribution/recipe.d.ts +25 -0
- package/src/recipes/angular-distribution/recipe.js +407 -0
- package/src/recipes/angular-distribution/recipe.js.map +1 -0
- package/src/recipes/angular-weaver/recipe.d.ts +48 -0
- package/src/recipes/angular-weaver/recipe.js +568 -0
- package/src/recipes/angular-weaver/recipe.js.map +1 -0
- package/src/recipes/angular-weaver/weaver-i18n.d.ts +3 -0
- package/src/recipes/angular-weaver/weaver-i18n.js +24 -0
- package/src/recipes/angular-weaver/weaver-i18n.js.map +1 -0
- package/src/recipes/auth-source/recipe.d.ts +11 -0
- package/src/recipes/auth-source/recipe.js +61 -0
- package/src/recipes/auth-source/recipe.js.map +1 -0
- package/src/recipes/frame-plugin/recipe.d.ts +11 -0
- package/src/recipes/frame-plugin/recipe.js +143 -0
- package/src/recipes/frame-plugin/recipe.js.map +1 -0
- package/src/recipes/layout/recipe.d.ts +10 -0
- package/src/recipes/layout/recipe.js +35 -0
- package/src/recipes/layout/recipe.js.map +1 -0
- package/src/recipes/settings-store/recipe.d.ts +10 -0
- package/src/recipes/settings-store/recipe.js +51 -0
- package/src/recipes/settings-store/recipe.js.map +1 -0
- package/src/recipes/shell-regions.d.ts +9 -0
- package/src/recipes/shell-regions.js +23 -0
- package/src/recipes/shell-regions.js.map +1 -0
- package/src/recipes/theme/recipe.d.ts +13 -0
- package/src/recipes/theme/recipe.js +142 -0
- package/src/recipes/theme/recipe.js.map +1 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.authSource = void 0;
|
|
4
|
+
exports.resolveAuthSourceInput = resolveAuthSourceInput;
|
|
5
|
+
const casing_1 = require("../../lib/generate/casing");
|
|
6
|
+
function resolveAuthSourceInput(input) {
|
|
7
|
+
if (!(0, casing_1.isKebabId)(input.name)) {
|
|
8
|
+
throw new Error(`Auth source name must be kebab-case (e.g. "dev"); got "${input.name}".`);
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
name: input.name,
|
|
12
|
+
className: (0, casing_1.toPascalCase)(input.name),
|
|
13
|
+
propertyName: (0, casing_1.toCamelCase)(input.name),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function moduleFile(a) {
|
|
17
|
+
return `// Provider-neutral AuthSource. LoomWeaver owns no authentication — it only reacts to
|
|
18
|
+
// a session snapshot. Wire it with: provideAuthSource(() => ${a.propertyName}AuthSource()).
|
|
19
|
+
// Replace the dev switcher below by mapping your product's real session onto an AuthSnapshot.
|
|
20
|
+
import { signal, Signal } from '@angular/core';
|
|
21
|
+
import { ANONYMOUS, AuthSnapshot } from '@loomweaver/plugin-sdk';
|
|
22
|
+
|
|
23
|
+
const USER: AuthSnapshot = {
|
|
24
|
+
authenticated: true,
|
|
25
|
+
roles: ['user'],
|
|
26
|
+
claims: {},
|
|
27
|
+
displayName: 'Signed-in user',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const ADMIN: AuthSnapshot = {
|
|
31
|
+
authenticated: true,
|
|
32
|
+
roles: ['user', 'admin'],
|
|
33
|
+
claims: {},
|
|
34
|
+
displayName: 'Administrator',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const state = signal<AuthSnapshot>(ANONYMOUS);
|
|
38
|
+
|
|
39
|
+
export function ${a.propertyName}AuthSource(): Signal<AuthSnapshot> {
|
|
40
|
+
return state.asReadonly();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function cycle${a.className}User(): void {
|
|
44
|
+
const current = state();
|
|
45
|
+
const next = !current.authenticated
|
|
46
|
+
? USER
|
|
47
|
+
: current.roles.includes('admin')
|
|
48
|
+
? ANONYMOUS
|
|
49
|
+
: ADMIN;
|
|
50
|
+
state.set(next);
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
exports.authSource = {
|
|
55
|
+
id: 'auth-source',
|
|
56
|
+
build(input) {
|
|
57
|
+
const a = resolveAuthSourceInput(input);
|
|
58
|
+
return { [`${a.name}-auth-source.ts`]: moduleFile(a) };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../../../../../../libs/tooling/devkit/src/recipes/auth-source/recipe.ts"],"names":[],"mappings":";;;AAaA,wDASC;AArBD,sDAAiF;AAYjF,SAAgB,sBAAsB,CAAC,KAAsB;IAC3D,IAAI,CAAC,IAAA,kBAAS,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,0DAA0D,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,IAAA,qBAAY,EAAC,KAAK,CAAC,IAAI,CAAC;QACnC,YAAY,EAAE,IAAA,oBAAW,EAAC,KAAK,CAAC,IAAI,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAqB;IACvC,OAAO;+DACsD,CAAC,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;kBAqB3D,CAAC,CAAC,YAAY;;;;uBAIT,CAAC,CAAC,SAAS;;;;;;;;;CASjC,CAAC;AACF,CAAC;AAEY,QAAA,UAAU,GAA4B;IACjD,EAAE,EAAE,aAAa;IACjB,KAAK,CAAC,KAAsB;QAC1B,MAAM,CAAC,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACxC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,iBAAiB,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Recipe } from '../../lib/generate/types';
|
|
2
|
+
export interface FramePluginInput {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ResolvedFramePlugin {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveFramePluginInput(input: FramePluginInput): ResolvedFramePlugin;
|
|
11
|
+
export declare const framePlugin: Recipe<FramePluginInput>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.framePlugin = void 0;
|
|
4
|
+
exports.resolveFramePluginInput = resolveFramePluginInput;
|
|
5
|
+
const casing_1 = require("../../lib/generate/casing");
|
|
6
|
+
function resolveFramePluginInput(input) {
|
|
7
|
+
var _a;
|
|
8
|
+
if (!(0, casing_1.isKebabId)(input.id)) {
|
|
9
|
+
throw new Error(`Sandbox plugin id must be kebab-case (e.g. "notes"); got "${input.id}".`);
|
|
10
|
+
}
|
|
11
|
+
return { id: input.id, name: ((_a = input.name) === null || _a === void 0 ? void 0 : _a.trim()) || (0, casing_1.toTitleCase)(input.id) };
|
|
12
|
+
}
|
|
13
|
+
function pluginHtml(p) {
|
|
14
|
+
return `<!doctype html>
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head>
|
|
17
|
+
<meta charset="utf-8" />
|
|
18
|
+
<title>${p.name} — frame plugin (logic)</title>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<script src="/frame-kit/penpal.global.js"></script>
|
|
22
|
+
<script src="./plugin.js"></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
function pluginJs(p) {
|
|
28
|
+
return `(function () {
|
|
29
|
+
const Penpal = globalThis.Penpal;
|
|
30
|
+
const messenger = new Penpal.WindowMessenger({
|
|
31
|
+
remoteWindow: globalThis.parent,
|
|
32
|
+
allowedOrigins: ['*'],
|
|
33
|
+
});
|
|
34
|
+
const connection = Penpal.connect({ messenger });
|
|
35
|
+
|
|
36
|
+
connection.promise
|
|
37
|
+
.then(function (ctx) {
|
|
38
|
+
return Promise.all([
|
|
39
|
+
ctx.toast({ message: '${p.name} ready', kind: 'success', timeoutMs: 4000 }),
|
|
40
|
+
ctx.registerSurface({
|
|
41
|
+
id: '${p.id}.view',
|
|
42
|
+
title: '${p.name}',
|
|
43
|
+
iframe: '/${p.id}/view.html',
|
|
44
|
+
routable: { path: '${p.id}', titleIsLiteral: true },
|
|
45
|
+
}),
|
|
46
|
+
]);
|
|
47
|
+
})
|
|
48
|
+
.catch(function (error) {
|
|
49
|
+
console.error('[${p.id}] frame plugin failed', error);
|
|
50
|
+
});
|
|
51
|
+
})();
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
function viewHtml(p) {
|
|
55
|
+
return `<!doctype html>
|
|
56
|
+
<html lang="en">
|
|
57
|
+
<head>
|
|
58
|
+
<meta charset="utf-8" />
|
|
59
|
+
<title>${p.name}</title>
|
|
60
|
+
<link rel="stylesheet" href="/frame-kit/lw-frame.css" />
|
|
61
|
+
<style>
|
|
62
|
+
body {
|
|
63
|
+
margin: 0;
|
|
64
|
+
font-family: var(--lw-font-sans, system-ui, sans-serif);
|
|
65
|
+
color: var(--lw-content, #1f2937);
|
|
66
|
+
background: var(--lw-surface, transparent);
|
|
67
|
+
}
|
|
68
|
+
.wrap { max-width: 42rem; margin: 0 auto; padding: 1.5rem; }
|
|
69
|
+
h1 { font-size: 1.125rem; font-weight: 600; }
|
|
70
|
+
p { color: var(--lw-content-faint, #6b7280); }
|
|
71
|
+
</style>
|
|
72
|
+
</head>
|
|
73
|
+
<body>
|
|
74
|
+
<div class="wrap">
|
|
75
|
+
<h1>${p.name}</h1>
|
|
76
|
+
<p>
|
|
77
|
+
Your sandboxed surface. It runs isolated in its own iframe, so its body can be built with
|
|
78
|
+
any framework (React, Vue, Svelte, vanilla). The frame UI kit (served by the distribution
|
|
79
|
+
under <code>/frame-kit/</code>) defines the <code>lw-*</code> element family and
|
|
80
|
+
the <code>.lw-*</code> class contracts; the host pushes its resolved design tokens over RPC.
|
|
81
|
+
</p>
|
|
82
|
+
<p><lw-button variant="primary" size="sm">Kit button</lw-button></p>
|
|
83
|
+
</div>
|
|
84
|
+
<script src="/frame-kit/penpal.global.js"></script>
|
|
85
|
+
<script src="/frame-kit/lw-elements.global.js"></script>
|
|
86
|
+
<script>
|
|
87
|
+
globalThis.Penpal.connect({
|
|
88
|
+
messenger: new globalThis.Penpal.WindowMessenger({
|
|
89
|
+
remoteWindow: globalThis.parent,
|
|
90
|
+
allowedOrigins: ['*'],
|
|
91
|
+
}),
|
|
92
|
+
methods: {
|
|
93
|
+
render: function (state) {
|
|
94
|
+
globalThis.LwFrame.applySurfaceState(state);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
</script>
|
|
99
|
+
</body>
|
|
100
|
+
</html>
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
function readme(p) {
|
|
104
|
+
return [
|
|
105
|
+
`# ${p.name} — frame plugin`,
|
|
106
|
+
'',
|
|
107
|
+
`A framework-agnostic LoomWeaver plugin: it runs in an isolated \`<iframe sandbox>\` and`,
|
|
108
|
+
`receives \`ctx\` over Penpal RPC through the same default-deny broker a trusted plugin uses.`,
|
|
109
|
+
'',
|
|
110
|
+
'## Serve it + wire it into a distribution',
|
|
111
|
+
'',
|
|
112
|
+
`1. Put these files under the distribution's static dir, e.g. \`public/${p.id}/\`. The plugin`,
|
|
113
|
+
` references the **frame UI kit** (\`@loomweaver/frame-kit\`) at \`/frame-kit/\` —`,
|
|
114
|
+
` the distribution serves it via an assets glob (generated distributions already do):`,
|
|
115
|
+
'',
|
|
116
|
+
' ```jsonc',
|
|
117
|
+
' { "input": "node_modules/@loomweaver/frame-kit/dist", "glob": "**", "output": "frame-kit" }',
|
|
118
|
+
' ```',
|
|
119
|
+
'',
|
|
120
|
+
`2. Register + grant it in the composition root:`,
|
|
121
|
+
'',
|
|
122
|
+
' ```ts',
|
|
123
|
+
` provideFramePlugins({ id: '${p.id}', entryUrl: '/${p.id}/plugin.html', capabilities: ['contributions', 'ui'] });`,
|
|
124
|
+
` provideCapabilityGrants({ '${p.id}': ['contributions', 'ui'] });`,
|
|
125
|
+
' ```',
|
|
126
|
+
'',
|
|
127
|
+
`The surface is routable at \`/${p.id}\`. Replace \`view.html\` with your own UI in any framework.`,
|
|
128
|
+
'',
|
|
129
|
+
].join('\n');
|
|
130
|
+
}
|
|
131
|
+
exports.framePlugin = {
|
|
132
|
+
id: 'frame-plugin',
|
|
133
|
+
build(input) {
|
|
134
|
+
const p = resolveFramePluginInput(input);
|
|
135
|
+
return {
|
|
136
|
+
'plugin.html': pluginHtml(p),
|
|
137
|
+
'plugin.js': pluginJs(p),
|
|
138
|
+
'view.html': viewHtml(p),
|
|
139
|
+
'README.md': readme(p),
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../../../../../../libs/tooling/devkit/src/recipes/frame-plugin/recipe.ts"],"names":[],"mappings":";;;AAaA,0DAKC;AAjBD,sDAAmE;AAYnE,SAAgB,uBAAuB,CAAC,KAAuB;;IAC7D,IAAI,CAAC,IAAA,kBAAS,EAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6DAA6D,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,CAAA,MAAA,KAAK,CAAC,IAAI,0CAAE,IAAI,EAAE,KAAI,IAAA,oBAAW,EAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7E,CAAC;AAED,SAAS,UAAU,CAAC,CAAsB;IACxC,OAAO;;;;aAII,CAAC,CAAC,IAAI;;;;;;;CAOlB,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAsB;IACtC,OAAO;;;;;;;;;;;gCAWuB,CAAC,CAAC,IAAI;;iBAErB,CAAC,CAAC,EAAE;oBACD,CAAC,CAAC,IAAI;sBACJ,CAAC,CAAC,EAAE;+BACK,CAAC,CAAC,EAAE;;;;;wBAKX,CAAC,CAAC,EAAE;;;CAG3B,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,CAAsB;IACtC,OAAO;;;;aAII,CAAC,CAAC,IAAI;;;;;;;;;;;;;;;;YAgBP,CAAC,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BjB,CAAC;AACF,CAAC;AAED,SAAS,MAAM,CAAC,CAAsB;IACpC,OAAO;QACL,KAAK,CAAC,CAAC,IAAI,iBAAiB;QAC5B,EAAE;QACF,yFAAyF;QACzF,8FAA8F;QAC9F,EAAE;QACF,2CAA2C;QAC3C,EAAE;QACF,yEAAyE,CAAC,CAAC,EAAE,iBAAiB;QAC9F,qFAAqF;QACrF,wFAAwF;QACxF,EAAE;QACF,aAAa;QACb,gGAAgG;QAChG,QAAQ;QACR,EAAE;QACF,iDAAiD;QACjD,EAAE;QACF,UAAU;QACV,iCAAiC,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,EAAE,0DAA0D;QACrH,iCAAiC,CAAC,CAAC,EAAE,gCAAgC;QACrE,QAAQ;QACR,EAAE;QACF,iCAAiC,CAAC,CAAC,EAAE,8DAA8D;QACnG,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAEY,QAAA,WAAW,GAA6B;IACnD,EAAE,EAAE,cAAc;IAClB,KAAK,CAAC,KAAuB;QAC3B,MAAM,CAAC,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO;YACL,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;YAC5B,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;YACxB,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;YACxB,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;SACvB,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Recipe } from '../../lib/generate/types';
|
|
2
|
+
export interface LayoutInput {
|
|
3
|
+
readonly name?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ResolvedLayout {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly propertyName: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveLayoutInput(input: LayoutInput): ResolvedLayout;
|
|
10
|
+
export declare const layout: Recipe<LayoutInput>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.layout = void 0;
|
|
4
|
+
exports.resolveLayoutInput = resolveLayoutInput;
|
|
5
|
+
const shell_regions_1 = require("../shell-regions");
|
|
6
|
+
const casing_1 = require("../../lib/generate/casing");
|
|
7
|
+
function resolveLayoutInput(input) {
|
|
8
|
+
var _a;
|
|
9
|
+
const name = ((_a = input.name) === null || _a === void 0 ? void 0 : _a.trim()) || 'base';
|
|
10
|
+
if (!(0, casing_1.isKebabId)(name)) {
|
|
11
|
+
throw new Error(`Layout name must be kebab-case (e.g. "base"); got "${name}".`);
|
|
12
|
+
}
|
|
13
|
+
return { name, propertyName: (0, casing_1.toCamelCase)(name) };
|
|
14
|
+
}
|
|
15
|
+
function moduleFile(l) {
|
|
16
|
+
return `// A base layout for the distribution. Pass it to provideLayout(${l.propertyName}Layout)
|
|
17
|
+
// in src/app/app.config.ts. Region ids are what contributions target — 'primary' (rail) and 'status-bar' (bar) match
|
|
18
|
+
// the devkit weaver defaults, so a scaffolded weaver's rail + bar items land here.
|
|
19
|
+
import { ShellLayout } from '@loomweaver/shell';
|
|
20
|
+
|
|
21
|
+
export const ${l.propertyName}Layout: ShellLayout = {
|
|
22
|
+
regions: [
|
|
23
|
+
${(0, shell_regions_1.renderRegions)(' ')}
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
exports.layout = {
|
|
29
|
+
id: 'layout',
|
|
30
|
+
build(input) {
|
|
31
|
+
const l = resolveLayoutInput(input);
|
|
32
|
+
return { [`${l.name}-layout.ts`]: moduleFile(l) };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../../../../../../libs/tooling/devkit/src/recipes/layout/recipe.ts"],"names":[],"mappings":";;;AAaA,gDAMC;AAlBD,oDAAiD;AACjD,sDAAmE;AAWnE,SAAgB,kBAAkB,CAAC,KAAkB;;IACnD,MAAM,IAAI,GAAG,CAAA,MAAA,KAAK,CAAC,IAAI,0CAAE,IAAI,EAAE,KAAI,MAAM,CAAC;IAC1C,IAAI,CAAC,IAAA,kBAAS,EAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,sDAAsD,IAAI,IAAI,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAA,oBAAW,EAAC,IAAI,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,UAAU,CAAC,CAAiB;IACnC,OAAO,mEAAmE,CAAC,CAAC,YAAY;;;;;eAK3E,CAAC,CAAC,YAAY;;EAE3B,IAAA,6BAAa,EAAC,MAAM,CAAC;;;CAGtB,CAAC;AACF,CAAC;AAEY,QAAA,MAAM,GAAwB;IACzC,EAAE,EAAE,QAAQ;IACZ,KAAK,CAAC,KAAkB;QACtB,MAAM,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Recipe } from '../../lib/generate/types';
|
|
2
|
+
export interface SettingsStoreInput {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ResolvedSettingsStore {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly className: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveSettingsStoreInput(input: SettingsStoreInput): ResolvedSettingsStore;
|
|
10
|
+
export declare const settingsStore: Recipe<SettingsStoreInput>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.settingsStore = void 0;
|
|
4
|
+
exports.resolveSettingsStoreInput = resolveSettingsStoreInput;
|
|
5
|
+
const casing_1 = require("../../lib/generate/casing");
|
|
6
|
+
function resolveSettingsStoreInput(input) {
|
|
7
|
+
if (!(0, casing_1.isKebabId)(input.name)) {
|
|
8
|
+
throw new Error(`Settings store name must be kebab-case (e.g. "backend"); got "${input.name}".`);
|
|
9
|
+
}
|
|
10
|
+
return { name: input.name, className: (0, casing_1.toPascalCase)(input.name) };
|
|
11
|
+
}
|
|
12
|
+
function moduleFile(s) {
|
|
13
|
+
return `// A backend-backed settings store for the SETTINGS_STORE port. The platform
|
|
14
|
+
// ships local defaults; the product persists settings against its own backend. Only genuine
|
|
15
|
+
// settings flow through this port — working state stays on WORKING_STATE_STORE. Wire it with:
|
|
16
|
+
// provideSettingsStore(new ${s.className}SettingsStore()).
|
|
17
|
+
import { KeyValueStore } from '@loomweaver/shell';
|
|
18
|
+
|
|
19
|
+
export class ${s.className}SettingsStore implements KeyValueStore {
|
|
20
|
+
constructor(private readonly baseUrl = '/api/settings') {}
|
|
21
|
+
|
|
22
|
+
async get(key: string): Promise<string | undefined> {
|
|
23
|
+
const response = await fetch(this.url(key));
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return response.text();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async set(key: string, value: string): Promise<void> {
|
|
31
|
+
await fetch(this.url(key), { method: 'PUT', body: value });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async delete(key: string): Promise<void> {
|
|
35
|
+
await fetch(this.url(key), { method: 'DELETE' });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private url(key: string): string {
|
|
39
|
+
return \`\${this.baseUrl}/\${encodeURIComponent(key)}\`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
exports.settingsStore = {
|
|
45
|
+
id: 'settings-store',
|
|
46
|
+
build(input) {
|
|
47
|
+
const s = resolveSettingsStoreInput(input);
|
|
48
|
+
return { [`${s.name}-settings-store.ts`]: moduleFile(s) };
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../../../../../../libs/tooling/devkit/src/recipes/settings-store/recipe.ts"],"names":[],"mappings":";;;AAYA,8DASC;AApBD,sDAAoE;AAWpE,SAAgB,yBAAyB,CACvC,KAAyB;IAEzB,IAAI,CAAC,IAAA,kBAAS,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,iEAAiE,KAAK,CAAC,IAAI,IAAI,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,IAAA,qBAAY,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,SAAS,UAAU,CAAC,CAAwB;IAC1C,OAAO;;;8BAGqB,CAAC,CAAC,SAAS;;;eAG1B,CAAC,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;CAuBzB,CAAC;AACF,CAAC;AAEY,QAAA,aAAa,GAA+B;IACvD,EAAE,EAAE,gBAAgB;IACpB,KAAK,CAAC,KAAyB;QAC7B,MAAM,CAAC,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;QAC3C,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,oBAAoB,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The base set of regions every scaffold agrees on. `primary` (rail) and `status-bar` (bar) are the
|
|
3
|
+
* defaults the weaver recipe targets, so a scaffolded weaver's contributions land somewhere the
|
|
4
|
+
* moment a scaffolded distribution boots. Emitted by both the layout recipe and the distribution's
|
|
5
|
+
* app.config, which is why it lives in one place: a region id that drifts here renders a
|
|
6
|
+
* contribution invisible rather than failing.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SHELL_REGIONS: readonly string[];
|
|
9
|
+
export declare function renderRegions(indent: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SHELL_REGIONS = void 0;
|
|
4
|
+
exports.renderRegions = renderRegions;
|
|
5
|
+
/**
|
|
6
|
+
* The base set of regions every scaffold agrees on. `primary` (rail) and `status-bar` (bar) are the
|
|
7
|
+
* defaults the weaver recipe targets, so a scaffolded weaver's contributions land somewhere the
|
|
8
|
+
* moment a scaffolded distribution boots. Emitted by both the layout recipe and the distribution's
|
|
9
|
+
* app.config, which is why it lives in one place: a region id that drifts here renders a
|
|
10
|
+
* contribution invisible rather than failing.
|
|
11
|
+
*/
|
|
12
|
+
exports.SHELL_REGIONS = [
|
|
13
|
+
"{ id: 'top-bar', type: 'bar', dock: 'top' }",
|
|
14
|
+
"{ id: 'primary', type: 'rail', dock: 'left' }",
|
|
15
|
+
"{ id: 'left-panel', type: 'panel', dock: 'left' }",
|
|
16
|
+
"{ id: 'right-panel', type: 'panel', dock: 'right' }",
|
|
17
|
+
"{ id: 'main', type: 'content', dock: 'center' }",
|
|
18
|
+
"{ id: 'status-bar', type: 'bar', dock: 'bottom' }",
|
|
19
|
+
];
|
|
20
|
+
function renderRegions(indent) {
|
|
21
|
+
return exports.SHELL_REGIONS.map((region) => `${indent}${region},`).join('\n');
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=shell-regions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-regions.js","sourceRoot":"","sources":["../../../../../../libs/tooling/devkit/src/recipes/shell-regions.ts"],"names":[],"mappings":";;;AAgBA,sCAEC;AAlBD;;;;;;GAMG;AACU,QAAA,aAAa,GAAsB;IAC9C,6CAA6C;IAC7C,+CAA+C;IAC/C,mDAAmD;IACnD,qDAAqD;IACrD,iDAAiD;IACjD,mDAAmD;CACpD,CAAC;AAEF,SAAgB,aAAa,CAAC,MAAc;IAC1C,OAAO,qBAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Recipe } from '../../lib/generate/types';
|
|
2
|
+
export type ThemePreset = 'literal' | 'bootstrap';
|
|
3
|
+
export interface ThemeInput {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly preset?: ThemePreset;
|
|
6
|
+
}
|
|
7
|
+
export interface ResolvedTheme {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly title: string;
|
|
10
|
+
readonly preset: ThemePreset;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveThemeInput(input: ThemeInput): ResolvedTheme;
|
|
13
|
+
export declare const theme: Recipe<ThemeInput>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.theme = void 0;
|
|
4
|
+
exports.resolveThemeInput = resolveThemeInput;
|
|
5
|
+
const casing_1 = require("../../lib/generate/casing");
|
|
6
|
+
const PRESETS = ['literal', 'bootstrap'];
|
|
7
|
+
function resolveThemeInput(input) {
|
|
8
|
+
var _a;
|
|
9
|
+
if (!(0, casing_1.isKebabId)(input.name)) {
|
|
10
|
+
throw new Error(`Theme name must be kebab-case (e.g. "midnight"); got "${input.name}".`);
|
|
11
|
+
}
|
|
12
|
+
const preset = (_a = input.preset) !== null && _a !== void 0 ? _a : 'literal';
|
|
13
|
+
if (!PRESETS.includes(preset)) {
|
|
14
|
+
throw new Error(`Unknown theme preset "${preset}"; expected one of ${PRESETS.join(', ')}.`);
|
|
15
|
+
}
|
|
16
|
+
return { name: input.name, title: (0, casing_1.toTitleCase)(input.name), preset };
|
|
17
|
+
}
|
|
18
|
+
function cssFile(t) {
|
|
19
|
+
return `/* ${t.title} theme — overrides LoomWeaver's --lw-* design tokens.
|
|
20
|
+
Import it AFTER the shell theme in your distribution styles.css:
|
|
21
|
+
@import '@loomweaver/shell/styles/theme.css';
|
|
22
|
+
@import './themes/${t.name}.css';
|
|
23
|
+
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
24
|
+
(Product default < Plugin < Tenant). The --lw-* ladder flips in :root.dark, so override both. */
|
|
25
|
+
|
|
26
|
+
@layer lw-tenant-theme {
|
|
27
|
+
:root {
|
|
28
|
+
--lw-brand: #2e96c9;
|
|
29
|
+
--lw-brand-strong: #237aa6;
|
|
30
|
+
--lw-brand-fill: #2e96c9;
|
|
31
|
+
--lw-brand-text: #1f6f97;
|
|
32
|
+
--lw-accent: #c59a2f;
|
|
33
|
+
--lw-surface: #ffffff;
|
|
34
|
+
--lw-surface-raised: #f7f8fa;
|
|
35
|
+
--lw-content: #1f2937;
|
|
36
|
+
--lw-content-muted: #4b5563;
|
|
37
|
+
--lw-content-faint: #6b7280;
|
|
38
|
+
--lw-border: #e5e7eb;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:root.dark {
|
|
42
|
+
--lw-brand: #3aa9dd;
|
|
43
|
+
--lw-brand-strong: #2e96c9;
|
|
44
|
+
--lw-brand-fill: #2e96c9;
|
|
45
|
+
--lw-brand-text: #7cc4e6;
|
|
46
|
+
--lw-accent: #d8b45a;
|
|
47
|
+
--lw-surface: #0f141a;
|
|
48
|
+
--lw-surface-raised: #171d25;
|
|
49
|
+
--lw-content: #e5e7eb;
|
|
50
|
+
--lw-content-muted: #9ca3af;
|
|
51
|
+
--lw-content-faint: #6b7280;
|
|
52
|
+
--lw-border: #2a323c;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
function bootstrapFile(t) {
|
|
58
|
+
return `/* ${t.title} theme — maps LoomWeaver's --lw-* design tokens onto Bootstrap 5.3's --bs-*.
|
|
59
|
+
Import it AFTER the shell theme in your distribution styles.css, and import Bootstrap itself
|
|
60
|
+
INTO A LAYER — unlayered CSS outranks layered CSS whatever its specificity, so Bootstrap's
|
|
61
|
+
Reboot (button { border-radius: 0 }) would otherwise beat our .lw-* component classes:
|
|
62
|
+
@layer vendor;
|
|
63
|
+
@import 'bootstrap/dist/css/bootstrap.css' layer(vendor);
|
|
64
|
+
@import '@loomweaver/shell/styles/shell.css'; (or styles/theme.css if you run Tailwind)
|
|
65
|
+
@import './themes/${t.name}.css';
|
|
66
|
+
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
67
|
+
(Product default < Plugin < Tenant).
|
|
68
|
+
|
|
69
|
+
THERE IS NO :root.dark BLOCK, on purpose. Bootstrap redefines its own --bs-* variables under
|
|
70
|
+
[data-bs-theme="dark"], so every var() below already resolves to the dark value — provided that
|
|
71
|
+
attribute is in step with the shell's mode. Mirror it once at startup, from somewhere with an
|
|
72
|
+
injection context — your root component, or provideEnvironmentInitializer in app.config.ts:
|
|
73
|
+
|
|
74
|
+
private readonly theme = inject(ThemeService); // from '@loomweaver/shell'
|
|
75
|
+
private readonly html = inject(DOCUMENT).documentElement;
|
|
76
|
+
constructor() {
|
|
77
|
+
effect(() => this.html.setAttribute('data-bs-theme', this.theme.resolvedTheme()));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Use resolvedTheme, never mode: mode can be 'system', which Bootstrap does not understand.
|
|
81
|
+
|
|
82
|
+
WHERE THIS MAPPING IS DELIBERATELY NOT ONE TO ONE: LoomWeaver splits a brand colour into the
|
|
83
|
+
identity colour, the colour it is safe to *read* as text, and the colour it is safe to *fill*
|
|
84
|
+
behind white text, because each clears a different WCAG threshold. Bootstrap 5.3 draws the same
|
|
85
|
+
distinction with -text-emphasis, so that is what the text tokens point at. The on-* tokens are
|
|
86
|
+
literal because they must contrast with the fill, not follow it, and they match what Bootstrap
|
|
87
|
+
itself puts on .btn-primary, .btn-warning and .btn-info.
|
|
88
|
+
|
|
89
|
+
CONTRAST IS YOURS NOW. LoomWeaver's own palette is verified against WCAG 2.1 AA. These values are
|
|
90
|
+
your Bootstrap theme's, so the guarantee travels with them: if your --bs-primary does not clear
|
|
91
|
+
4.5:1 behind white text, neither will our buttons. */
|
|
92
|
+
|
|
93
|
+
@layer lw-tenant-theme {
|
|
94
|
+
:root {
|
|
95
|
+
--lw-brand: var(--bs-primary);
|
|
96
|
+
--lw-brand-strong: var(--bs-primary-text-emphasis);
|
|
97
|
+
--lw-brand-text: var(--bs-primary-text-emphasis);
|
|
98
|
+
--lw-brand-fill: var(--bs-primary);
|
|
99
|
+
--lw-on-brand: #fff;
|
|
100
|
+
--lw-accent: var(--bs-secondary);
|
|
101
|
+
--lw-accent-strong: var(--bs-secondary-text-emphasis);
|
|
102
|
+
|
|
103
|
+
--lw-surface: var(--bs-body-bg);
|
|
104
|
+
--lw-surface-raised: var(--bs-body-bg);
|
|
105
|
+
--lw-surface-overlay: var(--bs-tertiary-bg);
|
|
106
|
+
--lw-field: var(--bs-body-bg);
|
|
107
|
+
--lw-border: var(--bs-border-color);
|
|
108
|
+
|
|
109
|
+
--lw-content: var(--bs-body-color);
|
|
110
|
+
--lw-content-muted: var(--bs-secondary-color);
|
|
111
|
+
--lw-content-faint: var(--bs-secondary-color);
|
|
112
|
+
|
|
113
|
+
--lw-tooltip: var(--bs-emphasis-color);
|
|
114
|
+
--lw-tooltip-content: var(--bs-body-bg);
|
|
115
|
+
|
|
116
|
+
--lw-positive: var(--bs-success);
|
|
117
|
+
--lw-on-positive: #fff;
|
|
118
|
+
--lw-negative: var(--bs-danger);
|
|
119
|
+
--lw-negative-fill: var(--bs-danger);
|
|
120
|
+
--lw-on-negative: #fff;
|
|
121
|
+
--lw-caution: var(--bs-warning);
|
|
122
|
+
--lw-on-caution: #000;
|
|
123
|
+
--lw-info: var(--bs-info);
|
|
124
|
+
--lw-on-info: #000;
|
|
125
|
+
|
|
126
|
+
--lw-scrim: rgb(0 0 0 / 0.5);
|
|
127
|
+
|
|
128
|
+
--lw-font-sans: var(--bs-body-font-family);
|
|
129
|
+
--lw-font-mono: var(--bs-font-monospace);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
exports.theme = {
|
|
135
|
+
id: 'theme',
|
|
136
|
+
build(input) {
|
|
137
|
+
const t = resolveThemeInput(input);
|
|
138
|
+
const content = t.preset === 'bootstrap' ? bootstrapFile(t) : cssFile(t);
|
|
139
|
+
return { [`${t.name}.css`]: content };
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=recipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe.js","sourceRoot":"","sources":["../../../../../../../libs/tooling/devkit/src/recipes/theme/recipe.ts"],"names":[],"mappings":";;;AAkBA,8CASC;AA1BD,sDAAmE;AAenE,MAAM,OAAO,GAA2B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAEjE,SAAgB,iBAAiB,CAAC,KAAiB;;IACjD,IAAI,CAAC,IAAA,kBAAS,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yDAAyD,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,MAAM,GAAG,MAAA,KAAK,CAAC,MAAM,mCAAI,SAAS,CAAC;IACzC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,sBAAsB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAA,oBAAW,EAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,OAAO,CAAC,CAAgB;IAC/B,OAAO,MAAM,CAAC,CAAC,KAAK;;;yBAGG,CAAC,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiC9B,CAAC;AACF,CAAC;AAED,SAAS,aAAa,CAAC,CAAgB;IACrC,OAAO,MAAM,CAAC,CAAC,KAAK;;;;;;;yBAOG,CAAC,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmE9B,CAAC;AACF,CAAC;AAEY,QAAA,KAAK,GAAuB;IACvC,EAAE,EAAE,OAAO;IACX,KAAK,CAAC,KAAiB;QACrB,MAAM,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACzE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACxC,CAAC;CACF,CAAC"}
|