@loomweaver/mcp 0.14.1 → 0.14.3
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 +4 -2
- package/dist/main.mjs +1385 -1267
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -101,7 +101,7 @@ var require_code = __commonJS({
|
|
|
101
101
|
}
|
|
102
102
|
exports._ = _;
|
|
103
103
|
var plus = new _Code("+");
|
|
104
|
-
function
|
|
104
|
+
function str(strs, ...args) {
|
|
105
105
|
const expr = [safeStringify(strs[0])];
|
|
106
106
|
let i = 0;
|
|
107
107
|
while (i < args.length) {
|
|
@@ -112,7 +112,7 @@ var require_code = __commonJS({
|
|
|
112
112
|
optimize(expr);
|
|
113
113
|
return new _Code(expr);
|
|
114
114
|
}
|
|
115
|
-
exports.str =
|
|
115
|
+
exports.str = str;
|
|
116
116
|
function addCodeArg(code, arg) {
|
|
117
117
|
if (arg instanceof _Code)
|
|
118
118
|
code.push(...arg._items);
|
|
@@ -155,7 +155,7 @@ var require_code = __commonJS({
|
|
|
155
155
|
return;
|
|
156
156
|
}
|
|
157
157
|
function strConcat(c1, c2) {
|
|
158
|
-
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 :
|
|
158
|
+
return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str`${c1}${c2}`;
|
|
159
159
|
}
|
|
160
160
|
exports.strConcat = strConcat;
|
|
161
161
|
function interpolate(x) {
|
|
@@ -1117,22 +1117,22 @@ var require_util = __commonJS({
|
|
|
1117
1117
|
return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
|
|
1118
1118
|
}
|
|
1119
1119
|
exports.schemaRefOrVal = schemaRefOrVal;
|
|
1120
|
-
function unescapeFragment(
|
|
1121
|
-
return unescapeJsonPointer(decodeURIComponent(
|
|
1120
|
+
function unescapeFragment(str) {
|
|
1121
|
+
return unescapeJsonPointer(decodeURIComponent(str));
|
|
1122
1122
|
}
|
|
1123
1123
|
exports.unescapeFragment = unescapeFragment;
|
|
1124
|
-
function escapeFragment(
|
|
1125
|
-
return encodeURIComponent(escapeJsonPointer(
|
|
1124
|
+
function escapeFragment(str) {
|
|
1125
|
+
return encodeURIComponent(escapeJsonPointer(str));
|
|
1126
1126
|
}
|
|
1127
1127
|
exports.escapeFragment = escapeFragment;
|
|
1128
|
-
function escapeJsonPointer(
|
|
1129
|
-
if (typeof
|
|
1130
|
-
return `${
|
|
1131
|
-
return
|
|
1128
|
+
function escapeJsonPointer(str) {
|
|
1129
|
+
if (typeof str == "number")
|
|
1130
|
+
return `${str}`;
|
|
1131
|
+
return str.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1132
1132
|
}
|
|
1133
1133
|
exports.escapeJsonPointer = escapeJsonPointer;
|
|
1134
|
-
function unescapeJsonPointer(
|
|
1135
|
-
return
|
|
1134
|
+
function unescapeJsonPointer(str) {
|
|
1135
|
+
return str.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1136
1136
|
}
|
|
1137
1137
|
exports.unescapeJsonPointer = unescapeJsonPointer;
|
|
1138
1138
|
function eachItem(xs, f) {
|
|
@@ -2157,8 +2157,8 @@ var require_json_schema_traverse = __commonJS({
|
|
|
2157
2157
|
post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
|
|
2158
2158
|
}
|
|
2159
2159
|
}
|
|
2160
|
-
function escapeJsonPtr(
|
|
2161
|
-
return
|
|
2160
|
+
function escapeJsonPtr(str) {
|
|
2161
|
+
return str.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
2162
2162
|
}
|
|
2163
2163
|
}
|
|
2164
2164
|
});
|
|
@@ -3222,10 +3222,10 @@ var require_utils = __commonJS({
|
|
|
3222
3222
|
return { host, isIPV6: false };
|
|
3223
3223
|
}
|
|
3224
3224
|
}
|
|
3225
|
-
function findToken(
|
|
3225
|
+
function findToken(str, token) {
|
|
3226
3226
|
let ind = 0;
|
|
3227
|
-
for (let i = 0; i <
|
|
3228
|
-
if (
|
|
3227
|
+
for (let i = 0; i < str.length; i++) {
|
|
3228
|
+
if (str[i] === token) ind++;
|
|
3229
3229
|
}
|
|
3230
3230
|
return ind;
|
|
3231
3231
|
}
|
|
@@ -3988,7 +3988,7 @@ var require_core = __commonJS({
|
|
|
3988
3988
|
var util_1 = require_util();
|
|
3989
3989
|
var $dataRefSchema = require_data();
|
|
3990
3990
|
var uri_1 = require_uri();
|
|
3991
|
-
var defaultRegExp = (
|
|
3991
|
+
var defaultRegExp = (str, flags) => new RegExp(str, flags);
|
|
3992
3992
|
defaultRegExp.code = "new RegExp";
|
|
3993
3993
|
var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"];
|
|
3994
3994
|
var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -4783,16 +4783,16 @@ var require_ucs2length = __commonJS({
|
|
|
4783
4783
|
"../../../node_modules/ajv/dist/runtime/ucs2length.js"(exports) {
|
|
4784
4784
|
"use strict";
|
|
4785
4785
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4786
|
-
function ucs2length(
|
|
4787
|
-
const len =
|
|
4786
|
+
function ucs2length(str) {
|
|
4787
|
+
const len = str.length;
|
|
4788
4788
|
let length = 0;
|
|
4789
4789
|
let pos = 0;
|
|
4790
4790
|
let value;
|
|
4791
4791
|
while (pos < len) {
|
|
4792
4792
|
length++;
|
|
4793
|
-
value =
|
|
4793
|
+
value = str.charCodeAt(pos++);
|
|
4794
4794
|
if (value >= 55296 && value <= 56319 && pos < len) {
|
|
4795
|
-
value =
|
|
4795
|
+
value = str.charCodeAt(pos);
|
|
4796
4796
|
if ((value & 64512) === 56320)
|
|
4797
4797
|
pos++;
|
|
4798
4798
|
}
|
|
@@ -6675,8 +6675,8 @@ var require_formats = __commonJS({
|
|
|
6675
6675
|
}
|
|
6676
6676
|
var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
|
6677
6677
|
var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
6678
|
-
function date5(
|
|
6679
|
-
const matches = DATE.exec(
|
|
6678
|
+
function date5(str) {
|
|
6679
|
+
const matches = DATE.exec(str);
|
|
6680
6680
|
if (!matches)
|
|
6681
6681
|
return false;
|
|
6682
6682
|
const year = +matches[1];
|
|
@@ -6695,8 +6695,8 @@ var require_formats = __commonJS({
|
|
|
6695
6695
|
}
|
|
6696
6696
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
6697
6697
|
function getTime(strictTimeZone) {
|
|
6698
|
-
return function time3(
|
|
6699
|
-
const matches = TIME.exec(
|
|
6698
|
+
return function time3(str) {
|
|
6699
|
+
const matches = TIME.exec(str);
|
|
6700
6700
|
if (!matches)
|
|
6701
6701
|
return false;
|
|
6702
6702
|
const hr = +matches[1];
|
|
@@ -6742,8 +6742,8 @@ var require_formats = __commonJS({
|
|
|
6742
6742
|
var DATE_TIME_SEPARATOR = /t|\s/i;
|
|
6743
6743
|
function getDateTime(strictTimeZone) {
|
|
6744
6744
|
const time3 = getTime(strictTimeZone);
|
|
6745
|
-
return function date_time(
|
|
6746
|
-
const dateTime =
|
|
6745
|
+
return function date_time(str) {
|
|
6746
|
+
const dateTime = str.split(DATE_TIME_SEPARATOR);
|
|
6747
6747
|
return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
|
|
6748
6748
|
};
|
|
6749
6749
|
}
|
|
@@ -6768,13 +6768,13 @@ var require_formats = __commonJS({
|
|
|
6768
6768
|
}
|
|
6769
6769
|
var NOT_URI_FRAGMENT = /\/|:/;
|
|
6770
6770
|
var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
|
|
6771
|
-
function uri(
|
|
6772
|
-
return NOT_URI_FRAGMENT.test(
|
|
6771
|
+
function uri(str) {
|
|
6772
|
+
return NOT_URI_FRAGMENT.test(str) && URI.test(str);
|
|
6773
6773
|
}
|
|
6774
6774
|
var BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
|
|
6775
|
-
function byte(
|
|
6775
|
+
function byte(str) {
|
|
6776
6776
|
BYTE.lastIndex = 0;
|
|
6777
|
-
return BYTE.test(
|
|
6777
|
+
return BYTE.test(str);
|
|
6778
6778
|
}
|
|
6779
6779
|
var MIN_INT32 = -(2 ** 31);
|
|
6780
6780
|
var MAX_INT32 = 2 ** 31 - 1;
|
|
@@ -6788,11 +6788,11 @@ var require_formats = __commonJS({
|
|
|
6788
6788
|
return true;
|
|
6789
6789
|
}
|
|
6790
6790
|
var Z_ANCHOR = /[^\\]\\Z/;
|
|
6791
|
-
function regex(
|
|
6792
|
-
if (Z_ANCHOR.test(
|
|
6791
|
+
function regex(str) {
|
|
6792
|
+
if (Z_ANCHOR.test(str))
|
|
6793
6793
|
return false;
|
|
6794
6794
|
try {
|
|
6795
|
-
new RegExp(
|
|
6795
|
+
new RegExp(str);
|
|
6796
6796
|
return true;
|
|
6797
6797
|
} catch (e) {
|
|
6798
6798
|
return false;
|
|
@@ -7702,14 +7702,14 @@ function promiseAllObject(promisesObj) {
|
|
|
7702
7702
|
}
|
|
7703
7703
|
function randomString(length = 10) {
|
|
7704
7704
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
7705
|
-
let
|
|
7705
|
+
let str = "";
|
|
7706
7706
|
for (let i = 0; i < length; i++) {
|
|
7707
|
-
|
|
7707
|
+
str += chars[Math.floor(Math.random() * chars.length)];
|
|
7708
7708
|
}
|
|
7709
|
-
return
|
|
7709
|
+
return str;
|
|
7710
7710
|
}
|
|
7711
|
-
function esc(
|
|
7712
|
-
return JSON.stringify(
|
|
7711
|
+
function esc(str) {
|
|
7712
|
+
return JSON.stringify(str);
|
|
7713
7713
|
}
|
|
7714
7714
|
function slugify(input) {
|
|
7715
7715
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -7823,8 +7823,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
|
|
|
7823
7823
|
"symbol",
|
|
7824
7824
|
"undefined"
|
|
7825
7825
|
]);
|
|
7826
|
-
function escapeRegex(
|
|
7827
|
-
return
|
|
7826
|
+
function escapeRegex(str) {
|
|
7827
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7828
7828
|
}
|
|
7829
7829
|
function clone(inst, def, params) {
|
|
7830
7830
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -31030,49 +31030,91 @@ function joinProjectPath(projectRoot, path) {
|
|
|
31030
31030
|
return root ? `${root}/${path}` : path;
|
|
31031
31031
|
}
|
|
31032
31032
|
|
|
31033
|
+
// ../devkit/src/lib/amend/compose.ts
|
|
31034
|
+
function registrationLines(amendment, providers) {
|
|
31035
|
+
return [
|
|
31036
|
+
...providers.map((provider) => provider.line),
|
|
31037
|
+
namespaceLine(amendment.id),
|
|
31038
|
+
`provideCapabilityGrants({ ${amendment.id}: [${quotedList(amendment.capabilities)}] }),`,
|
|
31039
|
+
`...providePlugins(${amendment.symbol}),`
|
|
31040
|
+
];
|
|
31041
|
+
}
|
|
31042
|
+
function quotedList(values) {
|
|
31043
|
+
return values.map((value) => `'${value}'`).join(", ");
|
|
31044
|
+
}
|
|
31045
|
+
function namespaceLine(id) {
|
|
31046
|
+
return `provideTranslationNamespaces('${id}'),`;
|
|
31047
|
+
}
|
|
31048
|
+
|
|
31033
31049
|
// ../devkit/src/lib/amend/describe.ts
|
|
31034
31050
|
function describeAmendment(amendment) {
|
|
31035
|
-
|
|
31036
|
-
|
|
31037
|
-
|
|
31038
|
-
|
|
31039
|
-
|
|
31040
|
-
|
|
31041
|
-
|
|
31042
|
-
|
|
31051
|
+
switch (amendment.kind) {
|
|
31052
|
+
case "postcss": {
|
|
31053
|
+
return `Write ${amendment.file} beside your package.json, naming ${amendment.plugin}. Without it the stylesheet is read as plain CSS: no utility class is emitted, the workbench renders unstyled, and the build still reports success.`;
|
|
31054
|
+
}
|
|
31055
|
+
case "package": {
|
|
31056
|
+
return `Add ${amendment.name}@${amendment.version} to your project's dependencies and install it. Without it the generated files import a package that is not there, so the very first build fails.`;
|
|
31057
|
+
}
|
|
31058
|
+
case "stylesheet-source": {
|
|
31059
|
+
return `Add an @source entry for '${amendment.sourceRoot}' to the application's entry stylesheet, resolved from that stylesheet. Without it none of that code's utilities are emitted.`;
|
|
31060
|
+
}
|
|
31061
|
+
case "compose-plugin": {
|
|
31062
|
+
return describeRegistration(amendment);
|
|
31063
|
+
}
|
|
31064
|
+
case "build-target": {
|
|
31065
|
+
return describeBuildTarget(amendment);
|
|
31066
|
+
}
|
|
31067
|
+
default: {
|
|
31068
|
+
const unknown2 = amendment;
|
|
31069
|
+
return unknown2;
|
|
31070
|
+
}
|
|
31043
31071
|
}
|
|
31044
|
-
|
|
31045
|
-
|
|
31046
|
-
|
|
31047
|
-
|
|
31048
|
-
|
|
31049
|
-
|
|
31050
|
-
|
|
31072
|
+
}
|
|
31073
|
+
function describeRegistration(amendment) {
|
|
31074
|
+
const calls = registrationLines(amendment, amendment.providers ?? []).map(
|
|
31075
|
+
(line) => line.replace(/,$/, "")
|
|
31076
|
+
);
|
|
31077
|
+
const imports = (amendment.providers ?? []).flatMap((provider) => provider.from ?? []).map(
|
|
31078
|
+
(imported) => ` import { ${imported.symbols.join(", ")} } from '${imported.path}'.`
|
|
31079
|
+
).join("");
|
|
31080
|
+
return `Register ${amendment.id} in the composition root: import { ${amendment.symbol} }, ${calls.slice(0, -1).join(", ")} and ${calls.at(-1)}.${imports} Without it none of its contributions appear.`;
|
|
31081
|
+
}
|
|
31082
|
+
function describeBuildTarget(amendment) {
|
|
31083
|
+
const steps = [];
|
|
31084
|
+
if (amendment.styles.length > 0) {
|
|
31085
|
+
steps.push(`name ${amendment.styles.join(", ")} in styles`);
|
|
31086
|
+
}
|
|
31087
|
+
if (amendment.assets.length > 0) {
|
|
31088
|
+
const assets = amendment.assets.map(
|
|
31089
|
+
(asset) => asset.output ? `${asset.input} served under ${asset.output}` : asset.input
|
|
31090
|
+
).join(", ");
|
|
31091
|
+
steps.push(
|
|
31092
|
+
`add assets for ${assets} (the shell fetches its own strings at runtime, so without that glob every label in the chrome renders as its raw translation key)`
|
|
31093
|
+
);
|
|
31051
31094
|
}
|
|
31052
|
-
|
|
31053
|
-
|
|
31054
|
-
...amendment.assets.length > 0 ? [
|
|
31055
|
-
`add assets for ${amendment.assets.map(
|
|
31056
|
-
(asset) => asset.output ? `${asset.input} served under ${asset.output}` : asset.input
|
|
31057
|
-
).join(
|
|
31058
|
-
", "
|
|
31059
|
-
)} (the shell fetches its own strings at runtime, so without that glob every label in the chrome renders as its raw translation key)`
|
|
31060
|
-
] : [],
|
|
31061
|
-
...amendment.serviceWorker ? [
|
|
31095
|
+
if (amendment.serviceWorker) {
|
|
31096
|
+
steps.push(
|
|
31062
31097
|
`set serviceWorker to ${amendment.serviceWorker} in the production configuration (provideShell registers a worker that 404s otherwise)`
|
|
31063
|
-
|
|
31064
|
-
|
|
31098
|
+
);
|
|
31099
|
+
}
|
|
31100
|
+
if (amendment.inlineCritical !== void 0) {
|
|
31101
|
+
steps.push(
|
|
31065
31102
|
`set optimization.styles.inlineCritical to ${amendment.inlineCritical} in the production configuration (the generated content-security policy blocks the inline handler Angular's critical-CSS pass attaches, so a release build renders unstyled)`
|
|
31066
|
-
|
|
31067
|
-
|
|
31103
|
+
);
|
|
31104
|
+
}
|
|
31105
|
+
if (amendment.initialBudget) {
|
|
31106
|
+
steps.push(
|
|
31068
31107
|
`raise the initial bundle budget to ${amendment.initialBudget.warning} warning and ${amendment.initialBudget.error} error in the production configuration (a fresh workspace carries one sized for an empty application, and the workbench alone spends almost all of it, so a release build fails)`
|
|
31069
|
-
|
|
31070
|
-
|
|
31108
|
+
);
|
|
31109
|
+
}
|
|
31110
|
+
return steps.map((step, index) => `${index + 1}. ${step}`).join(" ");
|
|
31071
31111
|
}
|
|
31072
31112
|
|
|
31073
31113
|
// ../devkit/src/lib/generate/casing.ts
|
|
31114
|
+
var KEBAB_ID_PATTERN = "^[a-z][a-z0-9]*(-[a-z0-9]+)*$";
|
|
31115
|
+
var KEBAB_ID = new RegExp(KEBAB_ID_PATTERN);
|
|
31074
31116
|
function isKebabId(value) {
|
|
31075
|
-
return
|
|
31117
|
+
return KEBAB_ID.test(value);
|
|
31076
31118
|
}
|
|
31077
31119
|
function words(value) {
|
|
31078
31120
|
return value.split(/[-_\s]+/).filter(Boolean);
|
|
@@ -31088,82 +31130,33 @@ function toTitleCase(value) {
|
|
|
31088
31130
|
return words(value).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
31089
31131
|
}
|
|
31090
31132
|
|
|
31091
|
-
// ../devkit/src/
|
|
31092
|
-
var
|
|
31093
|
-
|
|
31094
|
-
|
|
31095
|
-
|
|
31096
|
-
|
|
31097
|
-
|
|
31098
|
-
|
|
31099
|
-
"
|
|
31133
|
+
// ../devkit/src/recipes/shell-regions.ts
|
|
31134
|
+
var TOP_BAR_REGION = "top-bar";
|
|
31135
|
+
var RAIL_REGION = "primary";
|
|
31136
|
+
var LEFT_PANEL_REGION = "left-panel";
|
|
31137
|
+
var RIGHT_PANEL_REGION = "right-panel";
|
|
31138
|
+
var CONTENT_REGION = "main";
|
|
31139
|
+
var STATUS_BAR_REGION = "status-bar";
|
|
31140
|
+
var SHELL_REGIONS = [
|
|
31141
|
+
{ id: TOP_BAR_REGION, type: "bar", dock: "top" },
|
|
31142
|
+
{ id: RAIL_REGION, type: "rail", dock: "left" },
|
|
31143
|
+
{ id: LEFT_PANEL_REGION, type: "panel", dock: "left" },
|
|
31144
|
+
{ id: RIGHT_PANEL_REGION, type: "panel", dock: "right" },
|
|
31145
|
+
{ id: CONTENT_REGION, type: "content", dock: "center" },
|
|
31146
|
+
{ id: STATUS_BAR_REGION, type: "bar", dock: "bottom" }
|
|
31100
31147
|
];
|
|
31101
|
-
function
|
|
31102
|
-
|
|
31103
|
-
|
|
31104
|
-
|
|
31105
|
-
level: "error",
|
|
31106
|
-
code: "manifest.id",
|
|
31107
|
-
message: `Plugin id must be a kebab-case string; got ${JSON.stringify(manifest2.id)}.`,
|
|
31108
|
-
path: "manifest.id"
|
|
31109
|
-
});
|
|
31110
|
-
}
|
|
31111
|
-
if (manifest2.name !== void 0 && typeof manifest2.name !== "string") {
|
|
31112
|
-
findings.push({
|
|
31113
|
-
level: "error",
|
|
31114
|
-
code: "manifest.name",
|
|
31115
|
-
message: "Plugin name must be a string when provided.",
|
|
31116
|
-
path: "manifest.name"
|
|
31117
|
-
});
|
|
31118
|
-
}
|
|
31119
|
-
findings.push(...validateCapabilities(manifest2.capabilities, known));
|
|
31120
|
-
return findings;
|
|
31121
|
-
}
|
|
31122
|
-
function validateCapabilities(capabilities, known) {
|
|
31123
|
-
if (capabilities === void 0) {
|
|
31124
|
-
return [];
|
|
31125
|
-
}
|
|
31126
|
-
if (!Array.isArray(capabilities)) {
|
|
31127
|
-
return [
|
|
31128
|
-
{
|
|
31129
|
-
level: "error",
|
|
31130
|
-
code: "manifest.capabilities",
|
|
31131
|
-
message: "capabilities must be an array when provided.",
|
|
31132
|
-
path: "manifest.capabilities"
|
|
31133
|
-
}
|
|
31134
|
-
];
|
|
31135
|
-
}
|
|
31136
|
-
const findings = [];
|
|
31137
|
-
const seen = /* @__PURE__ */ new Set();
|
|
31138
|
-
for (const capability of capabilities) {
|
|
31139
|
-
if (typeof capability !== "string" || !known.includes(capability)) {
|
|
31140
|
-
findings.push({
|
|
31141
|
-
level: "error",
|
|
31142
|
-
code: "manifest.capability.unknown",
|
|
31143
|
-
message: `Unknown capability ${JSON.stringify(capability)}. Known: ${known.join(", ")}.`,
|
|
31144
|
-
path: "manifest.capabilities"
|
|
31145
|
-
});
|
|
31146
|
-
continue;
|
|
31147
|
-
}
|
|
31148
|
-
if (seen.has(capability)) {
|
|
31149
|
-
findings.push({
|
|
31150
|
-
level: "warning",
|
|
31151
|
-
code: "manifest.capability.duplicate",
|
|
31152
|
-
message: `Duplicate capability "${capability}".`,
|
|
31153
|
-
path: "manifest.capabilities"
|
|
31154
|
-
});
|
|
31155
|
-
}
|
|
31156
|
-
seen.add(capability);
|
|
31157
|
-
}
|
|
31158
|
-
return findings;
|
|
31148
|
+
function renderRegions(indent) {
|
|
31149
|
+
return SHELL_REGIONS.map(
|
|
31150
|
+
({ id, type, dock }) => `${indent}{ id: '${id}', type: '${type}', dock: '${dock}' },`
|
|
31151
|
+
).join("\n");
|
|
31159
31152
|
}
|
|
31160
31153
|
|
|
31161
31154
|
// ../devkit/src/recipes/angular-weaver/agent-panel.ts
|
|
31162
|
-
function panelFile(
|
|
31155
|
+
function panelFile(weaver) {
|
|
31163
31156
|
return `import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
31164
|
-
import { EventType, type BaseEvent, type Tool } from '@ag-ui/core';
|
|
31165
|
-
import { ${
|
|
31166
|
-
import { askAgent } from './${
|
|
31157
|
+
import { EventType, type BaseEvent, type Tool, type ToolMessage } from '@ag-ui/core';
|
|
31158
|
+
import { ${weaver.propertyName}Agent } from './${weaver.id}-agent';
|
|
31159
|
+
import { askAgent } from './${weaver.id}-agent-source';
|
|
31167
31160
|
|
|
31168
31161
|
interface Line {
|
|
31169
31162
|
readonly kind: 'you' | 'agent' | 'call' | 'result' | 'note';
|
|
@@ -31173,11 +31166,11 @@ interface Line {
|
|
|
31173
31166
|
}
|
|
31174
31167
|
|
|
31175
31168
|
@Component({
|
|
31176
|
-
selector: '${
|
|
31177
|
-
templateUrl: './${
|
|
31169
|
+
selector: '${weaver.prefix}-${weaver.id}-agent-panel',
|
|
31170
|
+
templateUrl: './${weaver.id}-agent-panel.html',
|
|
31178
31171
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
31179
31172
|
})
|
|
31180
|
-
export class ${
|
|
31173
|
+
export class ${weaver.className}AgentPanel {
|
|
31181
31174
|
protected readonly offered = signal<readonly Tool[]>([]);
|
|
31182
31175
|
|
|
31183
31176
|
protected readonly lines = signal<readonly Line[]>([]);
|
|
@@ -31193,11 +31186,11 @@ export class ${w.className}AgentPanel {
|
|
|
31193
31186
|
// The list is read again whenever the panel gains focus, because the weaver's own translations
|
|
31194
31187
|
// and other plugins' commands may arrive after this component was built.
|
|
31195
31188
|
protected refreshOffered(): void {
|
|
31196
|
-
this.offered.set(${
|
|
31189
|
+
this.offered.set(${weaver.propertyName}Agent()?.list() ?? []);
|
|
31197
31190
|
}
|
|
31198
31191
|
|
|
31199
31192
|
protected async ask(name: string): Promise<void> {
|
|
31200
|
-
const tools = ${
|
|
31193
|
+
const tools = ${weaver.propertyName}Agent();
|
|
31201
31194
|
if (!tools || this.busy()) {
|
|
31202
31195
|
return;
|
|
31203
31196
|
}
|
|
@@ -31219,29 +31212,27 @@ export class ${w.className}AgentPanel {
|
|
|
31219
31212
|
this.draw(event);
|
|
31220
31213
|
// Every event goes over, unfiltered. The adapter decides which ones matter; a filter here is
|
|
31221
31214
|
// how a call ends up half-assembled.
|
|
31222
|
-
|
|
31223
|
-
if (message) {
|
|
31224
|
-
this.push({
|
|
31225
|
-
kind: 'result',
|
|
31226
|
-
text: message.error ?? message.content,
|
|
31227
|
-
failed: Boolean(message.error),
|
|
31228
|
-
});
|
|
31229
|
-
}
|
|
31215
|
+
this.pushAnswer(await tools.receive(event));
|
|
31230
31216
|
}
|
|
31231
|
-
//
|
|
31232
|
-
|
|
31233
|
-
|
|
31234
|
-
this.push({
|
|
31235
|
-
kind: 'result',
|
|
31236
|
-
text: last.error ?? last.content,
|
|
31237
|
-
failed: Boolean(last.error),
|
|
31238
|
-
});
|
|
31217
|
+
// Every call the agent left open is answered, one per flush(), until none is left.
|
|
31218
|
+
for (let left = await tools.flush(); left; left = await tools.flush()) {
|
|
31219
|
+
this.pushAnswer(left);
|
|
31239
31220
|
}
|
|
31240
31221
|
} finally {
|
|
31241
31222
|
this.busy.set(false);
|
|
31242
31223
|
}
|
|
31243
31224
|
}
|
|
31244
31225
|
|
|
31226
|
+
private pushAnswer(message: ToolMessage | null): void {
|
|
31227
|
+
if (message) {
|
|
31228
|
+
this.push({
|
|
31229
|
+
kind: 'result',
|
|
31230
|
+
text: message.error ?? message.content,
|
|
31231
|
+
failed: Boolean(message.error),
|
|
31232
|
+
});
|
|
31233
|
+
}
|
|
31234
|
+
}
|
|
31235
|
+
|
|
31245
31236
|
private draw(event: BaseEvent): void {
|
|
31246
31237
|
const raw = event as unknown as Record<string, unknown>;
|
|
31247
31238
|
switch (event.type) {
|
|
@@ -31282,13 +31273,13 @@ export class ${w.className}AgentPanel {
|
|
|
31282
31273
|
}
|
|
31283
31274
|
`;
|
|
31284
31275
|
}
|
|
31285
|
-
function panelTemplateFile(
|
|
31276
|
+
function panelTemplateFile(weaver) {
|
|
31286
31277
|
return `<div class="flex h-full flex-col gap-3" (focusin)="refreshOffered()">
|
|
31287
31278
|
<p
|
|
31288
31279
|
class="shrink-0 rounded-md border border-border bg-surface-raised px-3 py-2 text-xs text-content-muted"
|
|
31289
31280
|
>
|
|
31290
31281
|
This is a stand-in, not an assistant. It speaks the protocol so you can watch the whole path;
|
|
31291
|
-
replace <code class="text-content">${
|
|
31282
|
+
replace <code class="text-content">${weaver.id}-agent-source.ts</code> with your own transport.
|
|
31292
31283
|
</p>
|
|
31293
31284
|
|
|
31294
31285
|
<div class="min-h-0 flex-1 overflow-auto">
|
|
@@ -31355,7 +31346,7 @@ function panelTemplateFile(w) {
|
|
|
31355
31346
|
}
|
|
31356
31347
|
|
|
31357
31348
|
// ../devkit/src/recipes/angular-weaver/agent-stand-in.ts
|
|
31358
|
-
function standInFile(
|
|
31349
|
+
function standInFile(weaver) {
|
|
31359
31350
|
return `import { EventType, type BaseEvent, type Tool } from '@ag-ui/core';
|
|
31360
31351
|
|
|
31361
31352
|
// WHAT THIS IS: a stand-in for an agent, and not an agent. It speaks the AG-UI protocol and nothing
|
|
@@ -31365,7 +31356,7 @@ function standInFile(w) {
|
|
|
31365
31356
|
// WHAT REPLACES IT: this file, and only this file. Point askAgent at your own endpoint and yield the
|
|
31366
31357
|
// events it streams back. The panel and the connection beside it stay exactly as they are.
|
|
31367
31358
|
|
|
31368
|
-
const
|
|
31359
|
+
const PAUSE_MS = 40;
|
|
31369
31360
|
|
|
31370
31361
|
export interface AgentRequest {
|
|
31371
31362
|
readonly runId: string;
|
|
@@ -31382,7 +31373,7 @@ export async function* askAgent(
|
|
|
31382
31373
|
);
|
|
31383
31374
|
|
|
31384
31375
|
yield event(EventType.RUN_STARTED, {
|
|
31385
|
-
threadId: '${
|
|
31376
|
+
threadId: '${weaver.id}-stand-in',
|
|
31386
31377
|
runId: request.runId,
|
|
31387
31378
|
});
|
|
31388
31379
|
yield* speak(
|
|
@@ -31410,7 +31401,7 @@ export async function* askAgent(
|
|
|
31410
31401
|
}
|
|
31411
31402
|
|
|
31412
31403
|
yield event(EventType.RUN_FINISHED, {
|
|
31413
|
-
threadId: '${
|
|
31404
|
+
threadId: '${weaver.id}-stand-in',
|
|
31414
31405
|
runId: request.runId,
|
|
31415
31406
|
});
|
|
31416
31407
|
}
|
|
@@ -31452,15 +31443,14 @@ function event(type: EventType, fields: Record<string, unknown>): BaseEvent {
|
|
|
31452
31443
|
}
|
|
31453
31444
|
|
|
31454
31445
|
function pause(): Promise<void> {
|
|
31455
|
-
return new Promise((done) => setTimeout(done,
|
|
31446
|
+
return new Promise((done) => setTimeout(done, PAUSE_MS));
|
|
31456
31447
|
}
|
|
31457
31448
|
`;
|
|
31458
31449
|
}
|
|
31459
31450
|
|
|
31460
31451
|
// ../devkit/src/recipes/angular-weaver/agent-files.ts
|
|
31461
|
-
var AG_UI_ADAPTER_VERSION = "0.14.1";
|
|
31462
31452
|
var AG_UI_PROTOCOL_VERSION = "0.0.x";
|
|
31463
|
-
function connectionFile(
|
|
31453
|
+
function connectionFile(weaver) {
|
|
31464
31454
|
return `import { signal } from '@angular/core';
|
|
31465
31455
|
import {
|
|
31466
31456
|
commandTools,
|
|
@@ -31472,13 +31462,13 @@ import type { PluginContext } from '@loomweaver/plugin-sdk';
|
|
|
31472
31462
|
|
|
31473
31463
|
// A factory, not a module-level connection: everything a run needs lives in the closure, so a second
|
|
31474
31464
|
// one never shares state with the first.
|
|
31475
|
-
export function ${
|
|
31465
|
+
export function ${weaver.propertyName}Connection(ctx: PluginContext): CommandTools {
|
|
31476
31466
|
return commandTools(ctx, { before: (call) => decide(ctx, call) });
|
|
31477
31467
|
}
|
|
31478
31468
|
|
|
31479
31469
|
// The one connection this plugin activates, published for its panel. Set in activate(), cleared in
|
|
31480
31470
|
// deactivate(), so the panel renders an honest empty state either side of that.
|
|
31481
|
-
export const ${
|
|
31471
|
+
export const ${weaver.propertyName}Agent = signal<CommandTools | null>(null);
|
|
31482
31472
|
|
|
31483
31473
|
// What an agent's word is enough for is the command's own statement, declared where the command is
|
|
31484
31474
|
// registered and read off the call here. No list of ids lives beside the commands: a list drifts from
|
|
@@ -31498,10 +31488,10 @@ async function decide(
|
|
|
31498
31488
|
return { decision: 'run' };
|
|
31499
31489
|
}
|
|
31500
31490
|
const yes = await ctx.ui.confirm({
|
|
31501
|
-
title: '${
|
|
31502
|
-
message: '${
|
|
31503
|
-
confirmLabel: '${
|
|
31504
|
-
cancelLabel: '${
|
|
31491
|
+
title: '${weaver.id}.agent.confirm.title',
|
|
31492
|
+
message: '${weaver.id}.agent.confirm.message',
|
|
31493
|
+
confirmLabel: '${weaver.id}.agent.confirm.yes',
|
|
31494
|
+
cancelLabel: '${weaver.id}.agent.confirm.no',
|
|
31505
31495
|
tone: 'warning',
|
|
31506
31496
|
});
|
|
31507
31497
|
// A decision can only narrow. Letting a call through does not make it reachable: the workbench
|
|
@@ -31512,10 +31502,10 @@ async function decide(
|
|
|
31512
31502
|
}
|
|
31513
31503
|
`;
|
|
31514
31504
|
}
|
|
31515
|
-
function specFile(
|
|
31505
|
+
function specFile(weaver) {
|
|
31516
31506
|
return `import { EventType, type BaseEvent } from '@ag-ui/core';
|
|
31517
31507
|
import type { CommandArguments, PluginContext } from '@loomweaver/plugin-sdk';
|
|
31518
|
-
import { ${
|
|
31508
|
+
import { ${weaver.propertyName}Connection } from './${weaver.id}-agent';
|
|
31519
31509
|
|
|
31520
31510
|
interface Asked {
|
|
31521
31511
|
readonly id: string;
|
|
@@ -31526,7 +31516,7 @@ function contextThat(confirms: boolean, ran: Asked[]): PluginContext {
|
|
|
31526
31516
|
return {
|
|
31527
31517
|
invocableCommands: () => [
|
|
31528
31518
|
// agentConsent travels with the command, which is what the connection reads off the call.
|
|
31529
|
-
{ id: '${
|
|
31519
|
+
{ id: '${weaver.id}.hello', title: '${weaver.name} action', description: 'Shows a short message.', agentConsent: 'ask' },
|
|
31530
31520
|
],
|
|
31531
31521
|
invokeCommand: (id: string, args?: CommandArguments) => {
|
|
31532
31522
|
ran.push({ id, args });
|
|
@@ -31540,21 +31530,21 @@ function event(type: EventType, fields: Record<string, unknown>): BaseEvent {
|
|
|
31540
31530
|
return { type, ...fields } as unknown as BaseEvent;
|
|
31541
31531
|
}
|
|
31542
31532
|
|
|
31543
|
-
describe('${
|
|
31533
|
+
describe('${weaver.propertyName}Connection', () => {
|
|
31544
31534
|
it('offers what the workbench offers', () => {
|
|
31545
|
-
const tools = ${
|
|
31546
|
-
expect(tools.list().map((tool) => tool.name)).toEqual(['${
|
|
31535
|
+
const tools = ${weaver.propertyName}Connection(contextThat(true, []));
|
|
31536
|
+
expect(tools.list().map((tool) => tool.name)).toEqual(['${weaver.id}.hello']);
|
|
31547
31537
|
});
|
|
31548
31538
|
|
|
31549
31539
|
it('assembles a call from its events and answers with the outcome', async () => {
|
|
31550
31540
|
const ran: Asked[] = [];
|
|
31551
|
-
const tools = ${
|
|
31541
|
+
const tools = ${weaver.propertyName}Connection(contextThat(true, ran));
|
|
31552
31542
|
|
|
31553
31543
|
expect(
|
|
31554
31544
|
await tools.receive(
|
|
31555
31545
|
event(EventType.TOOL_CALL_START, {
|
|
31556
31546
|
toolCallId: 'c1',
|
|
31557
|
-
toolCallName: '${
|
|
31547
|
+
toolCallName: '${weaver.id}.hello',
|
|
31558
31548
|
}),
|
|
31559
31549
|
),
|
|
31560
31550
|
).toBeNull();
|
|
@@ -31568,19 +31558,19 @@ describe('${w.propertyName}Connection', () => {
|
|
|
31568
31558
|
event(EventType.TOOL_CALL_END, { toolCallId: 'c1' }),
|
|
31569
31559
|
);
|
|
31570
31560
|
|
|
31571
|
-
expect(ran).toEqual([{ id: '${
|
|
31561
|
+
expect(ran).toEqual([{ id: '${weaver.id}.hello', args: { tone: 'success' } }]);
|
|
31572
31562
|
expect(JSON.parse(answer?.content ?? '{}').tone).toBe('success');
|
|
31573
31563
|
expect(answer?.error).toBeUndefined();
|
|
31574
31564
|
});
|
|
31575
31565
|
|
|
31576
31566
|
it('never reaches the workbench when a consequential call is declined', async () => {
|
|
31577
31567
|
const ran: Asked[] = [];
|
|
31578
|
-
const tools = ${
|
|
31568
|
+
const tools = ${weaver.propertyName}Connection(contextThat(false, ran));
|
|
31579
31569
|
|
|
31580
31570
|
await tools.receive(
|
|
31581
31571
|
event(EventType.TOOL_CALL_START, {
|
|
31582
31572
|
toolCallId: 'c2',
|
|
31583
|
-
toolCallName: '${
|
|
31573
|
+
toolCallName: '${weaver.id}.hello',
|
|
31584
31574
|
}),
|
|
31585
31575
|
);
|
|
31586
31576
|
await tools.receive(
|
|
@@ -31596,43 +31586,571 @@ describe('${w.propertyName}Connection', () => {
|
|
|
31596
31586
|
});
|
|
31597
31587
|
`;
|
|
31598
31588
|
}
|
|
31599
|
-
function agentSurfaceBlock(
|
|
31589
|
+
function agentSurfaceBlock(weaver) {
|
|
31600
31590
|
return [
|
|
31601
31591
|
" ctx.registerSurface({",
|
|
31602
|
-
` id: '${
|
|
31603
|
-
` title: '${
|
|
31604
|
-
` icon: '${
|
|
31605
|
-
|
|
31592
|
+
` id: '${weaver.id}.agent',`,
|
|
31593
|
+
` title: '${weaver.id}.agent.title',`,
|
|
31594
|
+
` icon: '${weaver.id}',`,
|
|
31595
|
+
` docks: ['${RIGHT_PANEL_REGION}'],`,
|
|
31606
31596
|
" padded: true,",
|
|
31607
|
-
` component: ${
|
|
31597
|
+
` component: ${weaver.className}AgentPanel,`,
|
|
31608
31598
|
" });"
|
|
31609
31599
|
].join("\n");
|
|
31610
31600
|
}
|
|
31611
|
-
function agentFiles(
|
|
31601
|
+
function agentFiles(weaver) {
|
|
31612
31602
|
const files = {
|
|
31613
|
-
[`src/lib/agent/${
|
|
31614
|
-
[`src/lib/agent/${
|
|
31615
|
-
[`src/lib/agent/${
|
|
31616
|
-
[`src/lib/agent/${
|
|
31603
|
+
[`src/lib/agent/${weaver.id}-agent.ts`]: connectionFile(weaver),
|
|
31604
|
+
[`src/lib/agent/${weaver.id}-agent-source.ts`]: standInFile(weaver),
|
|
31605
|
+
[`src/lib/agent/${weaver.id}-agent-panel.ts`]: panelFile(weaver),
|
|
31606
|
+
[`src/lib/agent/${weaver.id}-agent-panel.html`]: panelTemplateFile(weaver)
|
|
31617
31607
|
};
|
|
31618
|
-
if (
|
|
31619
|
-
files[`src/lib/agent/${
|
|
31608
|
+
if (weaver.features.spec) {
|
|
31609
|
+
files[`src/lib/agent/${weaver.id}-agent.spec.ts`] = specFile(weaver);
|
|
31620
31610
|
}
|
|
31621
31611
|
return files;
|
|
31622
31612
|
}
|
|
31623
31613
|
|
|
31624
|
-
// ../devkit/src/recipes/angular-weaver/weaver-
|
|
31614
|
+
// ../devkit/src/recipes/angular-weaver/weaver-i18n.ts
|
|
31615
|
+
function i18nBundle(weaver) {
|
|
31616
|
+
const bundle = { title: weaver.name };
|
|
31617
|
+
if (weaver.features.container) {
|
|
31618
|
+
bundle["canvas"] = "Canvas";
|
|
31619
|
+
bundle["details"] = "Details";
|
|
31620
|
+
}
|
|
31621
|
+
if (weaver.features.command) {
|
|
31622
|
+
bundle["action"] = `${weaver.name} action`;
|
|
31623
|
+
bundle["actionDescription"] = `Shows a short ${weaver.name} message in the tone the caller chooses.`;
|
|
31624
|
+
bundle["actionTone"] = "How the message is shown: info, success or warning. Use info unless the caller asked for emphasis.";
|
|
31625
|
+
bundle["actionAnswers"] = "The tone the message was shown in.";
|
|
31626
|
+
}
|
|
31627
|
+
if (weaver.features.about) bundle["about"] = `About ${weaver.name}`;
|
|
31628
|
+
if (weaver.features.agent) {
|
|
31629
|
+
bundle["agent"] = {
|
|
31630
|
+
title: `${weaver.name} assistant`,
|
|
31631
|
+
confirm: {
|
|
31632
|
+
title: "Run this command?",
|
|
31633
|
+
message: "An agent asked to run a command that was marked consequential.",
|
|
31634
|
+
yes: "Run it",
|
|
31635
|
+
no: "Not now"
|
|
31636
|
+
}
|
|
31637
|
+
};
|
|
31638
|
+
}
|
|
31639
|
+
if (weaver.features.settings) {
|
|
31640
|
+
bundle["settings"] = { title: weaver.name, enabled: "Enabled", note: "Note" };
|
|
31641
|
+
}
|
|
31642
|
+
return bundle;
|
|
31643
|
+
}
|
|
31644
|
+
function i18nFile(weaver) {
|
|
31645
|
+
return JSON.stringify(i18nBundle(weaver), null, 2) + "\n";
|
|
31646
|
+
}
|
|
31647
|
+
|
|
31648
|
+
// ../devkit/src/lib/scaffolds/scaffold-values.ts
|
|
31649
|
+
var NEUTRAL_PREFIX = "app";
|
|
31650
|
+
function stringValue(values, name) {
|
|
31651
|
+
const value = values[name];
|
|
31652
|
+
return typeof value === "string" ? value : void 0;
|
|
31653
|
+
}
|
|
31654
|
+
function booleanValue(values, name) {
|
|
31655
|
+
const value = values[name];
|
|
31656
|
+
return typeof value === "boolean" ? value : void 0;
|
|
31657
|
+
}
|
|
31658
|
+
var PLACEMENT_OPTIONS = [
|
|
31659
|
+
{
|
|
31660
|
+
name: "directory",
|
|
31661
|
+
type: "string",
|
|
31662
|
+
description: "Project root, relative to the workspace root.",
|
|
31663
|
+
workspaceOnly: true
|
|
31664
|
+
},
|
|
31665
|
+
{
|
|
31666
|
+
name: "tags",
|
|
31667
|
+
type: "string",
|
|
31668
|
+
description: "Comma-separated Nx tags for the project.",
|
|
31669
|
+
workspaceOnly: true
|
|
31670
|
+
},
|
|
31671
|
+
{
|
|
31672
|
+
name: "unitTestRunner",
|
|
31673
|
+
type: "string",
|
|
31674
|
+
description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
|
|
31675
|
+
choices: ["vitest", "none"],
|
|
31676
|
+
default: "vitest",
|
|
31677
|
+
workspaceOnly: true
|
|
31678
|
+
}
|
|
31679
|
+
];
|
|
31680
|
+
var APP_OPTION = {
|
|
31681
|
+
name: "app",
|
|
31682
|
+
type: "string",
|
|
31683
|
+
description: "Application to drop into. Inferred when the workspace has exactly one.",
|
|
31684
|
+
workspaceOnly: true
|
|
31685
|
+
};
|
|
31686
|
+
|
|
31687
|
+
// ../devkit/src/lib/validate/manifest.ts
|
|
31688
|
+
var KNOWN_CAPABILITIES = [
|
|
31689
|
+
"contributions",
|
|
31690
|
+
"ui",
|
|
31691
|
+
"host",
|
|
31692
|
+
"navigation",
|
|
31693
|
+
"session",
|
|
31694
|
+
"theme",
|
|
31695
|
+
"automation"
|
|
31696
|
+
];
|
|
31697
|
+
function validateManifest(manifest2, known = KNOWN_CAPABILITIES) {
|
|
31698
|
+
const findings = [];
|
|
31699
|
+
if (typeof manifest2.id !== "string" || !isKebabId(manifest2.id)) {
|
|
31700
|
+
findings.push({
|
|
31701
|
+
level: "error",
|
|
31702
|
+
code: "manifest.id",
|
|
31703
|
+
message: `Plugin id must be a kebab-case string; got ${JSON.stringify(manifest2.id)}.`,
|
|
31704
|
+
path: "manifest.id"
|
|
31705
|
+
});
|
|
31706
|
+
}
|
|
31707
|
+
if (manifest2.name !== void 0 && typeof manifest2.name !== "string") {
|
|
31708
|
+
findings.push({
|
|
31709
|
+
level: "error",
|
|
31710
|
+
code: "manifest.name",
|
|
31711
|
+
message: "Plugin name must be a string when provided.",
|
|
31712
|
+
path: "manifest.name"
|
|
31713
|
+
});
|
|
31714
|
+
}
|
|
31715
|
+
findings.push(...validateCapabilities(manifest2.capabilities, known));
|
|
31716
|
+
return findings;
|
|
31717
|
+
}
|
|
31718
|
+
function validateCapabilities(capabilities, known) {
|
|
31719
|
+
if (capabilities === void 0) {
|
|
31720
|
+
return [];
|
|
31721
|
+
}
|
|
31722
|
+
if (!Array.isArray(capabilities)) {
|
|
31723
|
+
return [
|
|
31724
|
+
{
|
|
31725
|
+
level: "error",
|
|
31726
|
+
code: "manifest.capabilities",
|
|
31727
|
+
message: "capabilities must be an array when provided.",
|
|
31728
|
+
path: "manifest.capabilities"
|
|
31729
|
+
}
|
|
31730
|
+
];
|
|
31731
|
+
}
|
|
31732
|
+
const findings = [];
|
|
31733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31734
|
+
for (const capability of capabilities) {
|
|
31735
|
+
if (typeof capability !== "string" || !known.includes(capability)) {
|
|
31736
|
+
findings.push({
|
|
31737
|
+
level: "error",
|
|
31738
|
+
code: "manifest.capability.unknown",
|
|
31739
|
+
message: `Unknown capability ${JSON.stringify(capability)}. Known: ${known.join(", ")}.`,
|
|
31740
|
+
path: "manifest.capabilities"
|
|
31741
|
+
});
|
|
31742
|
+
continue;
|
|
31743
|
+
}
|
|
31744
|
+
if (seen.has(capability)) {
|
|
31745
|
+
findings.push({
|
|
31746
|
+
level: "warning",
|
|
31747
|
+
code: "manifest.capability.duplicate",
|
|
31748
|
+
message: `Duplicate capability "${capability}".`,
|
|
31749
|
+
path: "manifest.capabilities"
|
|
31750
|
+
});
|
|
31751
|
+
}
|
|
31752
|
+
seen.add(capability);
|
|
31753
|
+
}
|
|
31754
|
+
return findings;
|
|
31755
|
+
}
|
|
31756
|
+
|
|
31757
|
+
// ../devkit/src/recipes/angular-weaver/weaver-input.ts
|
|
31758
|
+
var DEFAULT_MENU_SLOT = "content/tab/context";
|
|
31759
|
+
function accessLiteral(spec) {
|
|
31760
|
+
if (spec === "authenticated") {
|
|
31761
|
+
return "{ authenticated: true }";
|
|
31762
|
+
}
|
|
31763
|
+
if (spec === "anonymous") {
|
|
31764
|
+
return "{ authenticated: false }";
|
|
31765
|
+
}
|
|
31766
|
+
if (spec.startsWith("role:")) {
|
|
31767
|
+
const role = spec.slice("role:".length);
|
|
31768
|
+
if (!role) {
|
|
31769
|
+
throw new Error('Access "role:" needs a role name, e.g. "role:admin".');
|
|
31770
|
+
}
|
|
31771
|
+
if (/['\\]/.test(role)) {
|
|
31772
|
+
throw new Error(
|
|
31773
|
+
`Access role "${role}" must not contain quotes or backslashes.`
|
|
31774
|
+
);
|
|
31775
|
+
}
|
|
31776
|
+
return `{ anyRole: ['${role}'] }`;
|
|
31777
|
+
}
|
|
31778
|
+
throw new Error(
|
|
31779
|
+
`Unknown access "${spec}". Use "authenticated", "anonymous" or "role:<name>".`
|
|
31780
|
+
);
|
|
31781
|
+
}
|
|
31782
|
+
function resolveMenuSlot(menu) {
|
|
31783
|
+
if (menu === true) {
|
|
31784
|
+
return DEFAULT_MENU_SLOT;
|
|
31785
|
+
}
|
|
31786
|
+
return typeof menu === "string" && menu.length ? menu : void 0;
|
|
31787
|
+
}
|
|
31788
|
+
var PLATFORM_BOUND_CHORD_TOKENS = /* @__PURE__ */ new Set([
|
|
31789
|
+
"cmd",
|
|
31790
|
+
"command",
|
|
31791
|
+
"ctrl",
|
|
31792
|
+
"control",
|
|
31793
|
+
"meta"
|
|
31794
|
+
]);
|
|
31795
|
+
function assertPlatformNeutralChord(shortcut) {
|
|
31796
|
+
const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim());
|
|
31797
|
+
const bound = tokens.find((token) => PLATFORM_BOUND_CHORD_TOKENS.has(token));
|
|
31798
|
+
if (bound) {
|
|
31799
|
+
throw new Error(
|
|
31800
|
+
`Shortcut "${shortcut}" binds the platform-specific "${bound}" key. Use the neutral 'mod' token (e.g. 'mod+shift+k') \u2014 the host renders it as \u2318 on macOS and Ctrl elsewhere.`
|
|
31801
|
+
);
|
|
31802
|
+
}
|
|
31803
|
+
}
|
|
31804
|
+
function resolveFeatures(id, input) {
|
|
31805
|
+
const menuSlot = resolveMenuSlot(input?.menu);
|
|
31806
|
+
const barItem = Boolean(input?.barItem);
|
|
31807
|
+
const hasShortcut = Boolean(input?.shortcut?.trim());
|
|
31808
|
+
if (hasShortcut) {
|
|
31809
|
+
assertPlatformNeutralChord(input?.shortcut?.trim() ?? "");
|
|
31810
|
+
}
|
|
31811
|
+
const instanceable = Boolean(input?.instanceable);
|
|
31812
|
+
const container = Boolean(input?.container);
|
|
31813
|
+
if (instanceable && container) {
|
|
31814
|
+
throw new Error(
|
|
31815
|
+
'A surface cannot be both a container and instanceable: a container tab holds its own ":id" and is therefore routable, while named instances exist only for a docked, non-routable surface. Pick one.'
|
|
31816
|
+
);
|
|
31817
|
+
}
|
|
31818
|
+
const agent = Boolean(input?.agent);
|
|
31819
|
+
return {
|
|
31820
|
+
command: Boolean(input?.command) || menuSlot !== void 0 || barItem || hasShortcut || agent,
|
|
31821
|
+
menuSlot,
|
|
31822
|
+
settings: Boolean(input?.settings),
|
|
31823
|
+
access: input?.access ? accessLiteral(input.access) : void 0,
|
|
31824
|
+
shortcut: input?.shortcut?.trim() || `mod+shift+${id.charAt(0)}`,
|
|
31825
|
+
barItem,
|
|
31826
|
+
about: Boolean(input?.about),
|
|
31827
|
+
instanceable,
|
|
31828
|
+
container,
|
|
31829
|
+
agent,
|
|
31830
|
+
spec: input?.spec !== false
|
|
31831
|
+
};
|
|
31832
|
+
}
|
|
31833
|
+
function deriveCapabilities(features) {
|
|
31834
|
+
const set2 = /* @__PURE__ */ new Set(["contributions", "navigation"]);
|
|
31835
|
+
if (features.command) set2.add("ui");
|
|
31836
|
+
if (features.about) {
|
|
31837
|
+
set2.add("ui");
|
|
31838
|
+
set2.add("host");
|
|
31839
|
+
}
|
|
31840
|
+
if (features.agent) {
|
|
31841
|
+
set2.add("ui");
|
|
31842
|
+
set2.add("automation");
|
|
31843
|
+
}
|
|
31844
|
+
return KNOWN_CAPABILITIES.filter((capability) => set2.has(capability));
|
|
31845
|
+
}
|
|
31846
|
+
function resolvePrefix(prefix) {
|
|
31847
|
+
const trimmed = prefix?.trim();
|
|
31848
|
+
if (!trimmed) {
|
|
31849
|
+
return NEUTRAL_PREFIX;
|
|
31850
|
+
}
|
|
31851
|
+
if (!isKebabId(trimmed)) {
|
|
31852
|
+
throw new Error(
|
|
31853
|
+
`Selector prefix must be kebab-case (e.g. "acme"); got "${trimmed}".`
|
|
31854
|
+
);
|
|
31855
|
+
}
|
|
31856
|
+
return trimmed;
|
|
31857
|
+
}
|
|
31858
|
+
function resolveWeaverInput(input) {
|
|
31859
|
+
if (!isKebabId(input.id)) {
|
|
31860
|
+
throw new Error(
|
|
31861
|
+
`Weaver id must be kebab-case (e.g. "notes"); got "${input.id}".`
|
|
31862
|
+
);
|
|
31863
|
+
}
|
|
31864
|
+
const features = resolveFeatures(input.id, input.features);
|
|
31865
|
+
const capabilities = input.capabilities?.length ? [...input.capabilities] : deriveCapabilities(features);
|
|
31866
|
+
return {
|
|
31867
|
+
id: input.id,
|
|
31868
|
+
name: input.name?.trim() || toTitleCase(input.id),
|
|
31869
|
+
className: toPascalCase(input.id),
|
|
31870
|
+
propertyName: toCamelCase(input.id),
|
|
31871
|
+
capabilities,
|
|
31872
|
+
features,
|
|
31873
|
+
prefix: resolvePrefix(input.prefix),
|
|
31874
|
+
importPath: input.importPath?.trim() || `@loomweaver/${input.id}-weaver`
|
|
31875
|
+
};
|
|
31876
|
+
}
|
|
31877
|
+
|
|
31878
|
+
// ../devkit/src/recipes/angular-weaver/weaver-command.ts
|
|
31879
|
+
function commandBlock(weaver) {
|
|
31880
|
+
return [
|
|
31881
|
+
" ctx.registerCommand({",
|
|
31882
|
+
` id: '${weaver.id}.hello',`,
|
|
31883
|
+
` title: '${weaver.id}.action',`,
|
|
31884
|
+
` description: '${weaver.id}.actionDescription',`,
|
|
31885
|
+
" arguments: [",
|
|
31886
|
+
` { name: 'tone', kind: 'choice', choices: ['info', 'success', 'warning'], description: '${weaver.id}.actionTone' },`,
|
|
31887
|
+
" ],",
|
|
31888
|
+
` answers: '${weaver.id}.actionAnswers',`,
|
|
31889
|
+
` shortcut: '${weaver.features.shortcut}',`,
|
|
31890
|
+
" callable: true,",
|
|
31891
|
+
...weaver.features.agent ? [
|
|
31892
|
+
" // What an agent's word is enough for. The platform states it and enforces nothing:",
|
|
31893
|
+
" // the asking is the connection's half, which reads this off the call.",
|
|
31894
|
+
" agentConsent: 'ask',"
|
|
31895
|
+
] : [],
|
|
31896
|
+
" run: (_context, args) => {",
|
|
31897
|
+
" const tone = toneOf(args?.['tone']);",
|
|
31898
|
+
` ctx.ui.toast({ message: '${weaver.id}.action', kind: tone });`,
|
|
31899
|
+
" return { tone };",
|
|
31900
|
+
" },",
|
|
31901
|
+
" });"
|
|
31902
|
+
].join("\n");
|
|
31903
|
+
}
|
|
31904
|
+
function toneHelper() {
|
|
31905
|
+
return [
|
|
31906
|
+
"type Tone = 'info' | 'success' | 'warning';",
|
|
31907
|
+
"",
|
|
31908
|
+
"// The workbench already refused anything outside the declared choices; this narrows the type.",
|
|
31909
|
+
"function toneOf(value: unknown): Tone {",
|
|
31910
|
+
" return value === 'success' || value === 'warning' ? value : 'info';",
|
|
31911
|
+
"}"
|
|
31912
|
+
].join("\n");
|
|
31913
|
+
}
|
|
31914
|
+
|
|
31915
|
+
// ../devkit/src/recipes/angular-weaver/weaver-plugin.ts
|
|
31625
31916
|
var CONTAINER_EXAMPLE_ID = "example";
|
|
31626
|
-
|
|
31627
|
-
|
|
31917
|
+
var SURFACE_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 9h8M8 13h8M8 17h5"/></svg>';
|
|
31918
|
+
function containerChildIds(weaver) {
|
|
31919
|
+
return [`${weaver.id}.canvas`, `${weaver.id}.details`];
|
|
31920
|
+
}
|
|
31921
|
+
function containerSurfaceBlock(weaver) {
|
|
31922
|
+
const children = containerChildIds(weaver).map((id) => `'${id}'`).join(", ");
|
|
31923
|
+
const lines = [
|
|
31924
|
+
" ctx.registerSurface({",
|
|
31925
|
+
` id: '${weaver.id}',`,
|
|
31926
|
+
` title: '${weaver.id}.title',`,
|
|
31927
|
+
` icon: '${weaver.id}',`,
|
|
31928
|
+
` routable: { path: '${weaver.id}/:id' },`,
|
|
31929
|
+
" container: {",
|
|
31930
|
+
` children: [${children}],`,
|
|
31931
|
+
` initial: [${children}],`,
|
|
31932
|
+
" },",
|
|
31933
|
+
...accessLine(weaver),
|
|
31934
|
+
" });"
|
|
31935
|
+
];
|
|
31936
|
+
for (const [suffix, className] of [
|
|
31937
|
+
["canvas", `${weaver.className}CanvasView`],
|
|
31938
|
+
["details", `${weaver.className}DetailsView`]
|
|
31939
|
+
]) {
|
|
31940
|
+
lines.push(
|
|
31941
|
+
" ctx.registerSurface({",
|
|
31942
|
+
` id: '${weaver.id}.${suffix}',`,
|
|
31943
|
+
` title: '${weaver.id}.${suffix}',`,
|
|
31944
|
+
" docks: [],",
|
|
31945
|
+
` component: ${className},`,
|
|
31946
|
+
" });"
|
|
31947
|
+
);
|
|
31948
|
+
}
|
|
31949
|
+
return lines.join("\n");
|
|
31950
|
+
}
|
|
31951
|
+
function surfaceBlock(weaver) {
|
|
31952
|
+
if (weaver.features.container) {
|
|
31953
|
+
return containerSurfaceBlock(weaver);
|
|
31954
|
+
}
|
|
31955
|
+
const lines = [
|
|
31956
|
+
" ctx.registerSurface({",
|
|
31957
|
+
` id: '${weaver.id}',`,
|
|
31958
|
+
` title: '${weaver.id}.title',`,
|
|
31959
|
+
` icon: '${weaver.id}',`,
|
|
31960
|
+
` component: ${weaver.className}View,`
|
|
31961
|
+
];
|
|
31962
|
+
if (weaver.features.instanceable) {
|
|
31963
|
+
lines.push(` docks: ['${LEFT_PANEL_REGION}'],`, " instanceable: true,");
|
|
31964
|
+
} else {
|
|
31965
|
+
lines.push(` routable: { path: '${weaver.id}' },`);
|
|
31966
|
+
}
|
|
31967
|
+
lines.push(...accessLine(weaver), " });");
|
|
31968
|
+
return lines.join("\n");
|
|
31969
|
+
}
|
|
31970
|
+
function railTarget(weaver) {
|
|
31971
|
+
if (weaver.features.container) {
|
|
31972
|
+
return `ctx.navigateContent('${weaver.id}/${CONTAINER_EXAMPLE_ID}')`;
|
|
31973
|
+
}
|
|
31974
|
+
if (weaver.features.instanceable) {
|
|
31975
|
+
return `ctx.revealSurface('${weaver.id}')`;
|
|
31976
|
+
}
|
|
31977
|
+
return `ctx.navigateContent('${weaver.id}')`;
|
|
31978
|
+
}
|
|
31979
|
+
function railBlock(weaver) {
|
|
31980
|
+
const lines = [
|
|
31981
|
+
" ctx.registerRailItem({",
|
|
31982
|
+
` id: '${weaver.id}.rail',`,
|
|
31983
|
+
` rail: '${RAIL_REGION}',`,
|
|
31984
|
+
` icon: '${weaver.id}',`,
|
|
31985
|
+
` title: '${weaver.id}.title',`,
|
|
31986
|
+
` run: () => ${railTarget(weaver)},`,
|
|
31987
|
+
...accessLine(weaver),
|
|
31988
|
+
" });"
|
|
31989
|
+
];
|
|
31990
|
+
return lines.join("\n");
|
|
31991
|
+
}
|
|
31992
|
+
function barItemBlock(weaver) {
|
|
31993
|
+
return [
|
|
31994
|
+
" ctx.registerBarItem({",
|
|
31995
|
+
` id: '${weaver.id}.bar',`,
|
|
31996
|
+
` bar: '${STATUS_BAR_REGION}',`,
|
|
31997
|
+
" slot: 'end',",
|
|
31998
|
+
` icon: '${weaver.id}',`,
|
|
31999
|
+
` tooltip: '${weaver.id}.action',`,
|
|
32000
|
+
` command: '${weaver.id}.hello',`,
|
|
32001
|
+
" });"
|
|
32002
|
+
].join("\n");
|
|
32003
|
+
}
|
|
32004
|
+
function aboutCommandBlock(weaver) {
|
|
32005
|
+
return [
|
|
32006
|
+
" ctx.registerCommand({",
|
|
32007
|
+
` id: '${weaver.id}.about',`,
|
|
32008
|
+
` title: '${weaver.id}.about',`,
|
|
32009
|
+
` run: () => ctx.ui.open(${weaver.className}AboutDialog, { data: ctx.host, title: '${weaver.id}.title' }),`,
|
|
32010
|
+
" });"
|
|
32011
|
+
].join("\n");
|
|
32012
|
+
}
|
|
32013
|
+
function aboutRailBlock(weaver) {
|
|
32014
|
+
return [
|
|
32015
|
+
" ctx.registerRailItem({",
|
|
32016
|
+
` id: '${weaver.id}.rail.about',`,
|
|
32017
|
+
` rail: '${RAIL_REGION}',`,
|
|
32018
|
+
" anchor: 'bottom',",
|
|
32019
|
+
` icon: '${weaver.id}',`,
|
|
32020
|
+
` title: '${weaver.id}.about',`,
|
|
32021
|
+
` command: '${weaver.id}.about',`,
|
|
32022
|
+
" });"
|
|
32023
|
+
].join("\n");
|
|
32024
|
+
}
|
|
32025
|
+
function menuBlock(weaver) {
|
|
32026
|
+
return [
|
|
32027
|
+
" ctx.registerMenuItem({",
|
|
32028
|
+
` menu: '${weaver.features.menuSlot}',`,
|
|
32029
|
+
` command: '${weaver.id}.hello',`,
|
|
32030
|
+
" });"
|
|
32031
|
+
].join("\n");
|
|
32032
|
+
}
|
|
32033
|
+
function settingsBlock(weaver) {
|
|
32034
|
+
return [
|
|
32035
|
+
" ctx.registerSettingsSection({",
|
|
32036
|
+
` id: '${weaver.id}',`,
|
|
32037
|
+
` title: '${weaver.id}.settings.title',`,
|
|
32038
|
+
" rows: [",
|
|
32039
|
+
" {",
|
|
32040
|
+
" id: 'enabled',",
|
|
32041
|
+
` label: '${weaver.id}.settings.enabled',`,
|
|
32042
|
+
` control: { kind: 'toggle', value: () => ${weaver.propertyName}Enabled(), set: (v) => ${weaver.propertyName}Enabled.set(v) },`,
|
|
32043
|
+
" },",
|
|
32044
|
+
" {",
|
|
32045
|
+
" id: 'note',",
|
|
32046
|
+
` label: '${weaver.id}.settings.note',`,
|
|
32047
|
+
` control: { kind: 'text', value: () => ${weaver.propertyName}Note(), set: (v) => ${weaver.propertyName}Note.set(v) },`,
|
|
32048
|
+
" },",
|
|
32049
|
+
" ],",
|
|
32050
|
+
" });"
|
|
32051
|
+
].join("\n");
|
|
32052
|
+
}
|
|
32053
|
+
function pluginImports(weaver) {
|
|
32054
|
+
const imports = ["import { Plugin } from '@loomweaver/plugin-sdk';"];
|
|
32055
|
+
if (weaver.features.container) {
|
|
32056
|
+
imports.push(
|
|
32057
|
+
`import { ${weaver.className}CanvasView } from '../views/${weaver.id}-canvas-view';`,
|
|
32058
|
+
`import { ${weaver.className}DetailsView } from '../views/${weaver.id}-details-view';`
|
|
32059
|
+
);
|
|
32060
|
+
} else {
|
|
32061
|
+
imports.push(`import { ${weaver.className}View } from '../views/${weaver.id}-view';`);
|
|
32062
|
+
}
|
|
32063
|
+
if (weaver.features.about) {
|
|
32064
|
+
imports.push(
|
|
32065
|
+
`import { ${weaver.className}AboutDialog } from '../dialogs/${weaver.id}-about-dialog';`
|
|
32066
|
+
);
|
|
32067
|
+
}
|
|
32068
|
+
if (weaver.features.agent) {
|
|
32069
|
+
imports.push(
|
|
32070
|
+
`import { ${weaver.propertyName}Agent, ${weaver.propertyName}Connection } from '../agent/${weaver.id}-agent';`,
|
|
32071
|
+
`import { ${weaver.className}AgentPanel } from '../agent/${weaver.id}-agent-panel';`
|
|
32072
|
+
);
|
|
32073
|
+
}
|
|
32074
|
+
if (weaver.features.settings) {
|
|
32075
|
+
imports.unshift("import { signal } from '@angular/core';");
|
|
32076
|
+
}
|
|
32077
|
+
return imports;
|
|
32078
|
+
}
|
|
32079
|
+
function pluginConsts(weaver) {
|
|
32080
|
+
const consts = [`const icon =
|
|
32081
|
+
'${SURFACE_ICON}';`];
|
|
32082
|
+
if (weaver.features.settings) {
|
|
32083
|
+
consts.push(
|
|
32084
|
+
`const ${weaver.propertyName}Enabled = signal(true);`,
|
|
32085
|
+
`const ${weaver.propertyName}Note = signal('');`
|
|
32086
|
+
);
|
|
32087
|
+
}
|
|
32088
|
+
if (weaver.features.command) consts.push(toneHelper());
|
|
32089
|
+
return consts;
|
|
32090
|
+
}
|
|
32091
|
+
function pluginBody(weaver) {
|
|
32092
|
+
const body = [` ctx.contributeIcons({ '${weaver.id}': icon });`];
|
|
32093
|
+
if (weaver.features.agent) {
|
|
32094
|
+
body.push(
|
|
32095
|
+
` ${weaver.propertyName}Agent.set(${weaver.propertyName}Connection(ctx));`
|
|
32096
|
+
);
|
|
32097
|
+
}
|
|
32098
|
+
if (weaver.features.command) body.push(commandBlock(weaver));
|
|
32099
|
+
if (weaver.features.about) body.push(aboutCommandBlock(weaver));
|
|
32100
|
+
body.push(surfaceBlock(weaver), railBlock(weaver));
|
|
32101
|
+
if (weaver.features.about) body.push(aboutRailBlock(weaver));
|
|
32102
|
+
if (weaver.features.barItem) body.push(barItemBlock(weaver));
|
|
32103
|
+
if (weaver.features.menuSlot) body.push(menuBlock(weaver));
|
|
32104
|
+
if (weaver.features.settings) body.push(settingsBlock(weaver));
|
|
32105
|
+
if (weaver.features.agent) body.push(agentSurfaceBlock(weaver));
|
|
32106
|
+
return body;
|
|
32107
|
+
}
|
|
32108
|
+
function pluginFile(weaver) {
|
|
32109
|
+
const body = pluginBody(weaver);
|
|
32110
|
+
const deactivate = weaver.features.agent ? `
|
|
32111
|
+
deactivate() {
|
|
32112
|
+
${weaver.propertyName}Agent.set(null);
|
|
32113
|
+
},` : "";
|
|
32114
|
+
return `${pluginImports(weaver).join("\n")}
|
|
32115
|
+
|
|
32116
|
+
${pluginConsts(weaver).join("\n\n")}
|
|
32117
|
+
|
|
32118
|
+
export const ${weaver.propertyName}Plugin: Plugin = {
|
|
32119
|
+
manifest: {
|
|
32120
|
+
id: '${weaver.id}',
|
|
32121
|
+
name: '${weaver.name}',
|
|
32122
|
+
capabilities: [${quotedList(weaver.capabilities)}],
|
|
32123
|
+
},
|
|
32124
|
+
activate(ctx) {
|
|
32125
|
+
${body.join("\n")}
|
|
32126
|
+
},${deactivate}
|
|
32127
|
+
};
|
|
32128
|
+
`;
|
|
32129
|
+
}
|
|
32130
|
+
function accessLine(weaver) {
|
|
32131
|
+
return weaver.features.access ? [` access: ${weaver.features.access},`] : [];
|
|
32132
|
+
}
|
|
32133
|
+
|
|
32134
|
+
// ../devkit/src/recipes/platform-version.ts
|
|
32135
|
+
var PLATFORM_VERSION = "0.14.3";
|
|
32136
|
+
|
|
32137
|
+
// ../devkit/src/recipes/readme-notes.ts
|
|
32138
|
+
var UNTAGGED_NOTE = [
|
|
32139
|
+
"The project is generated **untagged**: Nx tags belong to your `depConstraints`, and inventing",
|
|
32140
|
+
"one would fail a lint policy you never opted this project into. If your workspace enforces",
|
|
32141
|
+
"module boundaries, give it tags your constraints allow \u2014 `--tags` at generation time, or",
|
|
32142
|
+
"`tags` in `project.json` afterwards."
|
|
32143
|
+
];
|
|
32144
|
+
function asBullet(lines) {
|
|
32145
|
+
return lines.map((line, index) => index === 0 ? `- ${line}` : ` ${line}`);
|
|
31628
32146
|
}
|
|
31629
32147
|
|
|
31630
32148
|
// ../devkit/src/recipes/angular-weaver/weaver-readme.ts
|
|
31631
|
-
function surfaceNotes(
|
|
31632
|
-
const railNote = "Rail and bar items reference region ids (`primary
|
|
31633
|
-
if (
|
|
32149
|
+
function surfaceNotes(weaver) {
|
|
32150
|
+
const railNote = "Rail and bar items reference region ids (`primary` rail, `status-bar` bar) that must exist in your layout.";
|
|
32151
|
+
if (weaver.features.container) {
|
|
31634
32152
|
return [
|
|
31635
|
-
`The surface is a **container**: it is routable at \`/${
|
|
32153
|
+
`The surface is a **container**: it is routable at \`/${weaver.id}/:id\`, and its tab holds a`,
|
|
31636
32154
|
"nested pane tree of child surfaces. The host draws the inner tabs, splits and drag targets; this",
|
|
31637
32155
|
"weaver only declares which children it offers.",
|
|
31638
32156
|
"",
|
|
@@ -31652,9 +32170,9 @@ function surfaceNotes(w) {
|
|
|
31652
32170
|
railNote
|
|
31653
32171
|
];
|
|
31654
32172
|
}
|
|
31655
|
-
if (
|
|
32173
|
+
if (weaver.features.instanceable) {
|
|
31656
32174
|
return [
|
|
31657
|
-
`The surface is **docked** into the \`
|
|
32175
|
+
`The surface is **docked** into the \`left-panel\` region and marked \`instanceable\`, so the host shows a`,
|
|
31658
32176
|
"switcher for saving, naming, renaming and deleting several configurations of it, each with its own",
|
|
31659
32177
|
"`VIEW_STATE` blob.",
|
|
31660
32178
|
"",
|
|
@@ -31672,7 +32190,7 @@ function surfaceNotes(w) {
|
|
|
31672
32190
|
];
|
|
31673
32191
|
}
|
|
31674
32192
|
return [
|
|
31675
|
-
`The surface is routable at \`/${
|
|
32193
|
+
`The surface is routable at \`/${weaver.id}\`; ${railNote.charAt(0).toLowerCase()}${railNote.slice(1)}`,
|
|
31676
32194
|
"",
|
|
31677
32195
|
"A routable surface has **no `VIEW_STATE` handle** \u2014 injecting the token there throws. It owns a URL,",
|
|
31678
32196
|
"so anything shareable (a filter, the active sub-tab) belongs in route params or `subRoutes`, where it",
|
|
@@ -31681,18 +32199,18 @@ function surfaceNotes(w) {
|
|
|
31681
32199
|
"flavour instead."
|
|
31682
32200
|
];
|
|
31683
32201
|
}
|
|
31684
|
-
function agentNotes(
|
|
32202
|
+
function agentNotes(weaver) {
|
|
31685
32203
|
return [
|
|
31686
32204
|
"",
|
|
31687
32205
|
"## The agent connection",
|
|
31688
32206
|
"",
|
|
31689
32207
|
`\`src/lib/agent/\` holds three files and one of them is meant to be thrown away.`,
|
|
31690
32208
|
"",
|
|
31691
|
-
`- \`${
|
|
32209
|
+
`- \`${weaver.id}-agent.ts\` is the connection: the workbench's own commands offered as tools, and a`,
|
|
31692
32210
|
" seam where this weaver decides about a call before it runs. Nothing is registered twice \u2014 the",
|
|
31693
32211
|
" list comes from the workbench, already narrowed by everything that would refuse the call.",
|
|
31694
|
-
`- \`${
|
|
31695
|
-
`- \`${
|
|
32212
|
+
`- \`${weaver.id}-agent-panel.ts\` shows what is offered, the call as it streams, and the outcome.`,
|
|
32213
|
+
`- \`${weaver.id}-agent-source.ts\` is a **stand-in**, not an assistant: it produces the protocol's own`,
|
|
31696
32214
|
" events so the whole path runs before you have connected anything. Replace that one file with",
|
|
31697
32215
|
" your transport and nothing else changes. No transport, credential or model is generated for",
|
|
31698
32216
|
" you, because none of them can be guessed.",
|
|
@@ -31705,15 +32223,15 @@ function agentNotes(w) {
|
|
|
31705
32223
|
`The generated weaver needs two packages your project may not carry yet:`,
|
|
31706
32224
|
"",
|
|
31707
32225
|
"```bash",
|
|
31708
|
-
`npm i @loomweaver/ag-ui @ag-ui/core`,
|
|
32226
|
+
`npm i @loomweaver/ag-ui@^${PLATFORM_VERSION} @ag-ui/core@${AG_UI_PROTOCOL_VERSION}`,
|
|
31709
32227
|
"```",
|
|
31710
32228
|
"",
|
|
31711
32229
|
"The Nx generator and the CLI record them for you; the MCP route names them instead."
|
|
31712
32230
|
];
|
|
31713
32231
|
}
|
|
31714
|
-
function readmeFile(
|
|
32232
|
+
function readmeFile(weaver) {
|
|
31715
32233
|
return [
|
|
31716
|
-
`# ${
|
|
32234
|
+
`# ${weaver.name} weaver`,
|
|
31717
32235
|
"",
|
|
31718
32236
|
`A LoomWeaver weaver (a domain plugin bundle). It consumes only the public \`@loomweaver/plugin-sdk\` contract.`,
|
|
31719
32237
|
"",
|
|
@@ -31723,22 +32241,22 @@ function readmeFile(w) {
|
|
|
31723
32241
|
` returns an array, so spread it:`,
|
|
31724
32242
|
"",
|
|
31725
32243
|
" ```ts",
|
|
31726
|
-
` import { ${
|
|
31727
|
-
` ...providePlugins(${
|
|
32244
|
+
` import { ${weaver.propertyName}Plugin } from '${weaver.importPath}'; // Nx: the workspace alias; without one, a relative path to this library's src/index.ts`,
|
|
32245
|
+
` ...providePlugins(${weaver.propertyName}Plugin),`,
|
|
31728
32246
|
" ```",
|
|
31729
32247
|
"",
|
|
31730
32248
|
`2. Grant its capabilities (default-deny) via \`provideCapabilityGrants\`:`,
|
|
31731
32249
|
"",
|
|
31732
32250
|
" ```ts",
|
|
31733
|
-
` provideCapabilityGrants({ '${
|
|
32251
|
+
` provideCapabilityGrants({ '${weaver.id}': [${quotedList(weaver.capabilities)}] });`,
|
|
31734
32252
|
" ```",
|
|
31735
32253
|
"",
|
|
31736
|
-
`3. Compose its translations with \`provideTranslationNamespaces('${
|
|
32254
|
+
`3. Compose its translations with \`provideTranslationNamespaces('${weaver.id}')\` \u2014 and serve the`,
|
|
31737
32255
|
` bundle by adding an assets glob to your application's build target, so the loader can fetch`,
|
|
31738
|
-
` \`/i18n/${
|
|
32256
|
+
` \`/i18n/${weaver.id}/<lang>.json\` (the Nx generator adds this glob for you):`,
|
|
31739
32257
|
"",
|
|
31740
32258
|
" ```json",
|
|
31741
|
-
` { "glob": "**/*.json", "input": "<path to this library>/src/lib/i18n", "output": "i18n/${
|
|
32259
|
+
` { "glob": "**/*.json", "input": "<path to this library>/src/lib/i18n", "output": "i18n/${weaver.id}" }`,
|
|
31742
32260
|
" ```",
|
|
31743
32261
|
"",
|
|
31744
32262
|
`4. If your application compiles the shell's theme with Tailwind, name this library as a source`,
|
|
@@ -31751,459 +32269,65 @@ function readmeFile(w) {
|
|
|
31751
32269
|
` @source '<path from that stylesheet to this library>/src';`,
|
|
31752
32270
|
" ```",
|
|
31753
32271
|
"",
|
|
31754
|
-
...surfaceNotes(
|
|
31755
|
-
...
|
|
32272
|
+
...surfaceNotes(weaver),
|
|
32273
|
+
...weaver.features.agent ? agentNotes(weaver) : [],
|
|
31756
32274
|
"",
|
|
31757
32275
|
"## After scaffolding",
|
|
31758
32276
|
"",
|
|
31759
32277
|
"- `src/lib/i18n/de.json` starts as a copy of the English strings \u2014 translate it.",
|
|
31760
32278
|
`- A scaffolded command defaults its shortcut to \`mod+shift+<first letter of the id>\` \u2014 two weavers whose ids share a first letter collide; pass \`--shortcut\` or edit the command.`,
|
|
31761
|
-
|
|
31762
|
-
" one would fail a lint policy you never opted this project into. If your workspace enforces",
|
|
31763
|
-
" module boundaries, give it tags your constraints allow \u2014 `--tags` at generation time, or",
|
|
31764
|
-
" `tags` in `project.json` afterwards.",
|
|
32279
|
+
...asBullet(UNTAGGED_NOTE),
|
|
31765
32280
|
""
|
|
31766
32281
|
].join("\n");
|
|
31767
32282
|
}
|
|
31768
32283
|
|
|
31769
|
-
// ../devkit/src/recipes/angular-weaver/weaver-
|
|
31770
|
-
function
|
|
31771
|
-
const bundle = { title: w.name };
|
|
31772
|
-
if (w.features.container) {
|
|
31773
|
-
bundle["canvas"] = "Canvas";
|
|
31774
|
-
bundle["details"] = "Details";
|
|
31775
|
-
}
|
|
31776
|
-
if (w.features.command) {
|
|
31777
|
-
bundle["action"] = `${w.name} action`;
|
|
31778
|
-
bundle["actionDescription"] = `Shows a short ${w.name} message in the tone the caller chooses.`;
|
|
31779
|
-
bundle["actionTone"] = "How the message is shown: info, success or warning. Use info unless the caller asked for emphasis.";
|
|
31780
|
-
bundle["actionAnswers"] = "The tone the message was shown in.";
|
|
31781
|
-
}
|
|
31782
|
-
if (w.features.about) bundle["about"] = `About ${w.name}`;
|
|
31783
|
-
if (w.features.agent) {
|
|
31784
|
-
bundle["agent"] = {
|
|
31785
|
-
title: `${w.name} assistant`,
|
|
31786
|
-
confirm: {
|
|
31787
|
-
title: "Run this command?",
|
|
31788
|
-
message: "An agent asked to run a command that was marked consequential.",
|
|
31789
|
-
yes: "Run it",
|
|
31790
|
-
no: "Not now"
|
|
31791
|
-
}
|
|
31792
|
-
};
|
|
31793
|
-
}
|
|
31794
|
-
if (w.features.settings)
|
|
31795
|
-
bundle["settings"] = { title: w.name, enabled: "Enabled", note: "Note" };
|
|
31796
|
-
return bundle;
|
|
31797
|
-
}
|
|
31798
|
-
function i18nFile(w) {
|
|
31799
|
-
return JSON.stringify(i18nBundle(w), null, 2) + "\n";
|
|
31800
|
-
}
|
|
31801
|
-
|
|
31802
|
-
// ../devkit/src/recipes/angular-weaver/weaver-command.ts
|
|
31803
|
-
function commandBlock(w) {
|
|
31804
|
-
return [
|
|
31805
|
-
" ctx.registerCommand({",
|
|
31806
|
-
` id: '${w.id}.hello',`,
|
|
31807
|
-
` title: '${w.id}.action',`,
|
|
31808
|
-
` description: '${w.id}.actionDescription',`,
|
|
31809
|
-
" arguments: [",
|
|
31810
|
-
` { name: 'tone', kind: 'choice', choices: ['info', 'success', 'warning'], description: '${w.id}.actionTone' },`,
|
|
31811
|
-
" ],",
|
|
31812
|
-
` answers: '${w.id}.actionAnswers',`,
|
|
31813
|
-
` shortcut: '${w.features.shortcut}',`,
|
|
31814
|
-
" callable: true,",
|
|
31815
|
-
...w.features.agent ? [
|
|
31816
|
-
" // What an agent's word is enough for. The platform states it and enforces nothing:",
|
|
31817
|
-
" // the asking is the connection's half, which reads this off the call.",
|
|
31818
|
-
" agentConsent: 'ask',"
|
|
31819
|
-
] : [],
|
|
31820
|
-
" run: (_context, args) => {",
|
|
31821
|
-
" const tone = toneOf(args?.['tone']);",
|
|
31822
|
-
` ctx.ui.toast({ message: '${w.id}.action', kind: tone });`,
|
|
31823
|
-
" return { tone };",
|
|
31824
|
-
" },",
|
|
31825
|
-
" });"
|
|
31826
|
-
].join("\n");
|
|
31827
|
-
}
|
|
31828
|
-
function toneHelper() {
|
|
31829
|
-
return [
|
|
31830
|
-
"type Tone = 'info' | 'success' | 'warning';",
|
|
31831
|
-
"",
|
|
31832
|
-
"// The workbench already refused anything outside the declared choices; this narrows the type.",
|
|
31833
|
-
"function toneOf(value: unknown): Tone {",
|
|
31834
|
-
" return value === 'success' || value === 'warning' ? value : 'info';",
|
|
31835
|
-
"}"
|
|
31836
|
-
].join("\n");
|
|
31837
|
-
}
|
|
31838
|
-
|
|
31839
|
-
// ../devkit/src/recipes/angular-weaver/weaver-plugin.ts
|
|
31840
|
-
var SURFACE_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 9h8M8 13h8M8 17h5"/></svg>';
|
|
31841
|
-
function containerChildIds(w) {
|
|
31842
|
-
return [`${w.id}.canvas`, `${w.id}.details`];
|
|
31843
|
-
}
|
|
31844
|
-
function containerSurfaceBlock(w) {
|
|
31845
|
-
const children = containerChildIds(w).map((id) => `'${id}'`).join(", ");
|
|
31846
|
-
const lines = [
|
|
31847
|
-
" ctx.registerSurface({",
|
|
31848
|
-
` id: '${w.id}',`,
|
|
31849
|
-
` title: '${w.id}.title',`,
|
|
31850
|
-
` icon: '${w.id}',`,
|
|
31851
|
-
` routable: { path: '${w.id}/:id' },`,
|
|
31852
|
-
" container: {",
|
|
31853
|
-
` children: [${children}],`,
|
|
31854
|
-
` initial: [${children}],`,
|
|
31855
|
-
" },"
|
|
31856
|
-
];
|
|
31857
|
-
if (w.features.access) lines.push(` access: ${w.features.access},`);
|
|
31858
|
-
lines.push(" });");
|
|
31859
|
-
for (const [suffix, className] of [
|
|
31860
|
-
["canvas", `${w.className}CanvasView`],
|
|
31861
|
-
["details", `${w.className}DetailsView`]
|
|
31862
|
-
]) {
|
|
31863
|
-
lines.push(
|
|
31864
|
-
" ctx.registerSurface({",
|
|
31865
|
-
` id: '${w.id}.${suffix}',`,
|
|
31866
|
-
` title: '${w.id}.${suffix}',`,
|
|
31867
|
-
" docks: [],",
|
|
31868
|
-
` component: ${className},`,
|
|
31869
|
-
" });"
|
|
31870
|
-
);
|
|
31871
|
-
}
|
|
31872
|
-
return lines.join("\n");
|
|
31873
|
-
}
|
|
31874
|
-
function surfaceBlock(w) {
|
|
31875
|
-
if (w.features.container) {
|
|
31876
|
-
return containerSurfaceBlock(w);
|
|
31877
|
-
}
|
|
31878
|
-
const lines = [
|
|
31879
|
-
" ctx.registerSurface({",
|
|
31880
|
-
` id: '${w.id}',`,
|
|
31881
|
-
` title: '${w.id}.title',`,
|
|
31882
|
-
` icon: '${w.id}',`,
|
|
31883
|
-
` component: ${w.className}View,`
|
|
31884
|
-
];
|
|
31885
|
-
if (w.features.instanceable) {
|
|
31886
|
-
lines.push(" docks: ['left-panel'],", " instanceable: true,");
|
|
31887
|
-
} else {
|
|
31888
|
-
lines.push(` routable: { path: '${w.id}' },`);
|
|
31889
|
-
}
|
|
31890
|
-
if (w.features.access) lines.push(` access: ${w.features.access},`);
|
|
31891
|
-
lines.push(" });");
|
|
31892
|
-
return lines.join("\n");
|
|
31893
|
-
}
|
|
31894
|
-
function railTarget(w) {
|
|
31895
|
-
if (w.features.container) {
|
|
31896
|
-
return `ctx.navigateContent('${w.id}/${CONTAINER_EXAMPLE_ID}')`;
|
|
31897
|
-
}
|
|
31898
|
-
if (w.features.instanceable) {
|
|
31899
|
-
return `ctx.revealSurface('${w.id}')`;
|
|
31900
|
-
}
|
|
31901
|
-
return `ctx.navigateContent('${w.id}')`;
|
|
31902
|
-
}
|
|
31903
|
-
function railBlock(w) {
|
|
31904
|
-
const lines = [
|
|
31905
|
-
" ctx.registerRailItem({",
|
|
31906
|
-
` id: '${w.id}.rail',`,
|
|
31907
|
-
" rail: 'primary',",
|
|
31908
|
-
` icon: '${w.id}',`,
|
|
31909
|
-
` title: '${w.id}.title',`,
|
|
31910
|
-
` run: () => ${railTarget(w)},`
|
|
31911
|
-
];
|
|
31912
|
-
if (w.features.access) lines.push(` access: ${w.features.access},`);
|
|
31913
|
-
lines.push(" });");
|
|
31914
|
-
return lines.join("\n");
|
|
31915
|
-
}
|
|
31916
|
-
function barItemBlock(w) {
|
|
31917
|
-
return [
|
|
31918
|
-
" ctx.registerBarItem({",
|
|
31919
|
-
` id: '${w.id}.bar',`,
|
|
31920
|
-
" bar: 'status-bar',",
|
|
31921
|
-
" slot: 'end',",
|
|
31922
|
-
` icon: '${w.id}',`,
|
|
31923
|
-
` tooltip: '${w.id}.action',`,
|
|
31924
|
-
` command: '${w.id}.hello',`,
|
|
31925
|
-
" });"
|
|
31926
|
-
].join("\n");
|
|
31927
|
-
}
|
|
31928
|
-
function aboutCommandBlock(w) {
|
|
31929
|
-
return [
|
|
31930
|
-
" ctx.registerCommand({",
|
|
31931
|
-
` id: '${w.id}.about',`,
|
|
31932
|
-
` title: '${w.id}.about',`,
|
|
31933
|
-
` run: () => ctx.ui.open(${w.className}AboutDialog, { data: ctx.host, title: '${w.id}.title' }),`,
|
|
31934
|
-
" });"
|
|
31935
|
-
].join("\n");
|
|
31936
|
-
}
|
|
31937
|
-
function aboutRailBlock(w) {
|
|
31938
|
-
return [
|
|
31939
|
-
" ctx.registerRailItem({",
|
|
31940
|
-
` id: '${w.id}.rail.about',`,
|
|
31941
|
-
" rail: 'primary',",
|
|
31942
|
-
" anchor: 'bottom',",
|
|
31943
|
-
` icon: '${w.id}',`,
|
|
31944
|
-
` title: '${w.id}.about',`,
|
|
31945
|
-
` command: '${w.id}.about',`,
|
|
31946
|
-
" });"
|
|
31947
|
-
].join("\n");
|
|
31948
|
-
}
|
|
31949
|
-
function menuBlock(w) {
|
|
31950
|
-
return [
|
|
31951
|
-
" ctx.registerMenuItem({",
|
|
31952
|
-
` menu: '${w.features.menuSlot}',`,
|
|
31953
|
-
` command: '${w.id}.hello',`,
|
|
31954
|
-
" });"
|
|
31955
|
-
].join("\n");
|
|
31956
|
-
}
|
|
31957
|
-
function settingsBlock(w) {
|
|
31958
|
-
return [
|
|
31959
|
-
" ctx.registerSettingsSection({",
|
|
31960
|
-
` id: '${w.id}',`,
|
|
31961
|
-
` title: '${w.id}.settings.title',`,
|
|
31962
|
-
" rows: [",
|
|
31963
|
-
" {",
|
|
31964
|
-
" id: 'enabled',",
|
|
31965
|
-
` label: '${w.id}.settings.enabled',`,
|
|
31966
|
-
` control: { kind: 'toggle', value: () => ${w.propertyName}Enabled(), set: (v) => ${w.propertyName}Enabled.set(v) },`,
|
|
31967
|
-
" },",
|
|
31968
|
-
" {",
|
|
31969
|
-
" id: 'note',",
|
|
31970
|
-
` label: '${w.id}.settings.note',`,
|
|
31971
|
-
` control: { kind: 'text', value: () => ${w.propertyName}Note(), set: (v) => ${w.propertyName}Note.set(v) },`,
|
|
31972
|
-
" },",
|
|
31973
|
-
" ],",
|
|
31974
|
-
" });"
|
|
31975
|
-
].join("\n");
|
|
31976
|
-
}
|
|
31977
|
-
function pluginImports(w) {
|
|
31978
|
-
const imports = ["import { Plugin } from '@loomweaver/plugin-sdk';"];
|
|
31979
|
-
if (w.features.container) {
|
|
31980
|
-
imports.push(
|
|
31981
|
-
`import { ${w.className}CanvasView } from '../views/${w.id}-canvas-view';`,
|
|
31982
|
-
`import { ${w.className}DetailsView } from '../views/${w.id}-details-view';`
|
|
31983
|
-
);
|
|
31984
|
-
} else {
|
|
31985
|
-
imports.push(`import { ${w.className}View } from '../views/${w.id}-view';`);
|
|
31986
|
-
}
|
|
31987
|
-
if (w.features.about) {
|
|
31988
|
-
imports.push(
|
|
31989
|
-
`import { ${w.className}AboutDialog } from '../dialogs/${w.id}-about-dialog';`
|
|
31990
|
-
);
|
|
31991
|
-
}
|
|
31992
|
-
if (w.features.agent) {
|
|
31993
|
-
imports.push(
|
|
31994
|
-
`import { ${w.propertyName}Agent, ${w.propertyName}Connection } from '../agent/${w.id}-agent';`,
|
|
31995
|
-
`import { ${w.className}AgentPanel } from '../agent/${w.id}-agent-panel';`
|
|
31996
|
-
);
|
|
31997
|
-
}
|
|
31998
|
-
if (w.features.settings) {
|
|
31999
|
-
imports.unshift("import { signal } from '@angular/core';");
|
|
32000
|
-
}
|
|
32001
|
-
return imports;
|
|
32002
|
-
}
|
|
32003
|
-
function pluginConsts(w) {
|
|
32004
|
-
const consts = [`const icon =
|
|
32005
|
-
'${SURFACE_ICON}';`];
|
|
32006
|
-
if (w.features.settings) {
|
|
32007
|
-
consts.push(
|
|
32008
|
-
`const ${w.propertyName}Enabled = signal(true);`,
|
|
32009
|
-
`const ${w.propertyName}Note = signal('');`
|
|
32010
|
-
);
|
|
32011
|
-
}
|
|
32012
|
-
if (w.features.command) consts.push(toneHelper());
|
|
32013
|
-
return consts;
|
|
32014
|
-
}
|
|
32015
|
-
function pluginBody(w) {
|
|
32016
|
-
const body = [` ctx.contributeIcons({ '${w.id}': icon });`];
|
|
32017
|
-
if (w.features.agent) {
|
|
32018
|
-
body.push(
|
|
32019
|
-
` ${w.propertyName}Agent.set(${w.propertyName}Connection(ctx));`
|
|
32020
|
-
);
|
|
32021
|
-
}
|
|
32022
|
-
if (w.features.command) body.push(commandBlock(w));
|
|
32023
|
-
if (w.features.about) body.push(aboutCommandBlock(w));
|
|
32024
|
-
body.push(surfaceBlock(w), railBlock(w));
|
|
32025
|
-
if (w.features.about) body.push(aboutRailBlock(w));
|
|
32026
|
-
if (w.features.barItem) body.push(barItemBlock(w));
|
|
32027
|
-
if (w.features.menuSlot) body.push(menuBlock(w));
|
|
32028
|
-
if (w.features.settings) body.push(settingsBlock(w));
|
|
32029
|
-
if (w.features.agent) body.push(agentSurfaceBlock(w));
|
|
32030
|
-
return body;
|
|
32031
|
-
}
|
|
32032
|
-
function pluginFile(w) {
|
|
32033
|
-
const body = pluginBody(w);
|
|
32034
|
-
const deactivate = w.features.agent ? `
|
|
32035
|
-
deactivate() {
|
|
32036
|
-
${w.propertyName}Agent.set(null);
|
|
32037
|
-
},` : "";
|
|
32038
|
-
return `${pluginImports(w).join("\n")}
|
|
32039
|
-
|
|
32040
|
-
${pluginConsts(w).join("\n\n")}
|
|
32041
|
-
|
|
32042
|
-
export const ${w.propertyName}Plugin: Plugin = {
|
|
32043
|
-
manifest: {
|
|
32044
|
-
id: '${w.id}',
|
|
32045
|
-
name: '${w.name}',
|
|
32046
|
-
capabilities: [${capabilityItems(w.capabilities)}],
|
|
32047
|
-
},
|
|
32048
|
-
activate(ctx) {
|
|
32049
|
-
${body.join("\n")}
|
|
32050
|
-
},${deactivate}
|
|
32051
|
-
};
|
|
32052
|
-
`;
|
|
32053
|
-
}
|
|
32054
|
-
|
|
32055
|
-
// ../devkit/src/recipes/angular-weaver/recipe.ts
|
|
32056
|
-
var DEFAULT_MENU_SLOT = "content/tab/context";
|
|
32057
|
-
function accessLiteral(spec) {
|
|
32058
|
-
if (spec === "authenticated") return "{ authenticated: true }";
|
|
32059
|
-
if (spec === "anonymous") return "{ authenticated: false }";
|
|
32060
|
-
if (spec.startsWith("role:")) {
|
|
32061
|
-
const role = spec.slice("role:".length);
|
|
32062
|
-
if (!role)
|
|
32063
|
-
throw new Error('Access "role:" needs a role name, e.g. "role:admin".');
|
|
32064
|
-
if (/['\\]/.test(role))
|
|
32065
|
-
throw new Error(
|
|
32066
|
-
`Access role "${role}" must not contain quotes or backslashes.`
|
|
32067
|
-
);
|
|
32068
|
-
return `{ anyRole: ['${role}'] }`;
|
|
32069
|
-
}
|
|
32070
|
-
throw new Error(
|
|
32071
|
-
`Unknown access "${spec}". Use "authenticated", "anonymous" or "role:<name>".`
|
|
32072
|
-
);
|
|
32073
|
-
}
|
|
32074
|
-
function resolveMenuSlot(menu) {
|
|
32075
|
-
if (menu === true) {
|
|
32076
|
-
return DEFAULT_MENU_SLOT;
|
|
32077
|
-
}
|
|
32078
|
-
return typeof menu === "string" && menu.length ? menu : void 0;
|
|
32079
|
-
}
|
|
32080
|
-
var PLATFORM_BOUND_CHORD_TOKENS = /* @__PURE__ */ new Set([
|
|
32081
|
-
"cmd",
|
|
32082
|
-
"command",
|
|
32083
|
-
"ctrl",
|
|
32084
|
-
"control",
|
|
32085
|
-
"meta"
|
|
32086
|
-
]);
|
|
32087
|
-
function assertPlatformNeutralChord(shortcut) {
|
|
32088
|
-
const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim());
|
|
32089
|
-
const bound = tokens.find((token) => PLATFORM_BOUND_CHORD_TOKENS.has(token));
|
|
32090
|
-
if (bound) {
|
|
32091
|
-
throw new Error(
|
|
32092
|
-
`Shortcut "${shortcut}" binds the platform-specific "${bound}" key. Use the neutral 'mod' token (e.g. 'mod+shift+k') \u2014 the host renders it as \u2318 on macOS and Ctrl elsewhere.`
|
|
32093
|
-
);
|
|
32094
|
-
}
|
|
32095
|
-
}
|
|
32096
|
-
function resolveFeatures(id, input) {
|
|
32097
|
-
const menuSlot = resolveMenuSlot(input?.menu);
|
|
32098
|
-
const barItem = Boolean(input?.barItem);
|
|
32099
|
-
const hasShortcut = Boolean(input?.shortcut?.trim());
|
|
32100
|
-
if (hasShortcut) {
|
|
32101
|
-
assertPlatformNeutralChord(input?.shortcut?.trim() ?? "");
|
|
32102
|
-
}
|
|
32103
|
-
const instanceable = Boolean(input?.instanceable);
|
|
32104
|
-
const container = Boolean(input?.container);
|
|
32105
|
-
if (instanceable && container) {
|
|
32106
|
-
throw new Error(
|
|
32107
|
-
'A surface cannot be both a container and instanceable: a container tab holds its own ":id" and is therefore routable, while named instances exist only for a docked, non-routable surface. Pick one.'
|
|
32108
|
-
);
|
|
32109
|
-
}
|
|
32110
|
-
const agent = Boolean(input?.agent);
|
|
32111
|
-
return {
|
|
32112
|
-
command: Boolean(input?.command) || menuSlot !== void 0 || barItem || hasShortcut || agent,
|
|
32113
|
-
menuSlot,
|
|
32114
|
-
settings: Boolean(input?.settings),
|
|
32115
|
-
access: input?.access ? accessLiteral(input.access) : void 0,
|
|
32116
|
-
shortcut: input?.shortcut?.trim() || `mod+shift+${id.charAt(0)}`,
|
|
32117
|
-
barItem,
|
|
32118
|
-
about: Boolean(input?.about),
|
|
32119
|
-
instanceable,
|
|
32120
|
-
container,
|
|
32121
|
-
agent,
|
|
32122
|
-
spec: input?.spec !== false
|
|
32123
|
-
};
|
|
32124
|
-
}
|
|
32125
|
-
function deriveCapabilities(features) {
|
|
32126
|
-
const set2 = /* @__PURE__ */ new Set(["contributions", "navigation"]);
|
|
32127
|
-
if (features.command) set2.add("ui");
|
|
32128
|
-
if (features.about) {
|
|
32129
|
-
set2.add("ui");
|
|
32130
|
-
set2.add("host");
|
|
32131
|
-
}
|
|
32132
|
-
if (features.agent) {
|
|
32133
|
-
set2.add("ui");
|
|
32134
|
-
set2.add("automation");
|
|
32135
|
-
}
|
|
32136
|
-
return KNOWN_CAPABILITIES.filter((capability) => set2.has(capability));
|
|
32137
|
-
}
|
|
32138
|
-
function resolveWeaverInput(input) {
|
|
32139
|
-
if (!isKebabId(input.id)) {
|
|
32140
|
-
throw new Error(
|
|
32141
|
-
`Weaver id must be kebab-case (e.g. "notes"); got "${input.id}".`
|
|
32142
|
-
);
|
|
32143
|
-
}
|
|
32144
|
-
const features = resolveFeatures(input.id, input.features);
|
|
32145
|
-
const capabilities = input.capabilities?.length ? [...input.capabilities] : deriveCapabilities(features);
|
|
32146
|
-
return {
|
|
32147
|
-
id: input.id,
|
|
32148
|
-
name: input.name?.trim() || toTitleCase(input.id),
|
|
32149
|
-
className: toPascalCase(input.id),
|
|
32150
|
-
propertyName: toCamelCase(input.id),
|
|
32151
|
-
capabilities,
|
|
32152
|
-
features,
|
|
32153
|
-
prefix: input.prefix?.trim() || "lw",
|
|
32154
|
-
importPath: input.importPath?.trim() || `@loomweaver/${input.id}-weaver`
|
|
32155
|
-
};
|
|
32156
|
-
}
|
|
32157
|
-
function aboutDialogFile(w) {
|
|
32284
|
+
// ../devkit/src/recipes/angular-weaver/weaver-views.ts
|
|
32285
|
+
function aboutDialogFile(weaver) {
|
|
32158
32286
|
return `import { Component, inject } from '@angular/core';
|
|
32159
32287
|
import { DialogRef, PluginHost } from '@loomweaver/plugin-sdk';
|
|
32160
32288
|
|
|
32161
32289
|
@Component({
|
|
32162
|
-
selector: '${
|
|
32163
|
-
templateUrl: './${
|
|
32290
|
+
selector: '${weaver.prefix}-${weaver.id}-about-dialog',
|
|
32291
|
+
templateUrl: './${weaver.id}-about-dialog.html',
|
|
32164
32292
|
})
|
|
32165
|
-
export class ${
|
|
32293
|
+
export class ${weaver.className}AboutDialog {
|
|
32166
32294
|
protected readonly host = inject(DialogRef).data as PluginHost;
|
|
32167
32295
|
}
|
|
32168
32296
|
`;
|
|
32169
32297
|
}
|
|
32170
|
-
function aboutDialogTemplateFile(
|
|
32298
|
+
function aboutDialogTemplateFile(weaver) {
|
|
32171
32299
|
return `<div class="flex flex-col items-center gap-2 text-center">
|
|
32172
|
-
<h2 class="text-lg font-semibold text-content">${
|
|
32300
|
+
<h2 class="text-lg font-semibold text-content">${weaver.name}</h2>
|
|
32173
32301
|
<span class="text-xs text-content-faint tabular-nums">v{{ host.version() }}</span>
|
|
32174
32302
|
</div>
|
|
32175
32303
|
`;
|
|
32176
32304
|
}
|
|
32177
|
-
function
|
|
32178
|
-
|
|
32179
|
-
`;
|
|
32180
|
-
}
|
|
32181
|
-
function viewFile(w) {
|
|
32182
|
-
if (!w.features.instanceable) {
|
|
32305
|
+
function viewFile(weaver) {
|
|
32306
|
+
if (!weaver.features.instanceable) {
|
|
32183
32307
|
return `import { Component } from '@angular/core';
|
|
32184
32308
|
|
|
32185
32309
|
@Component({
|
|
32186
|
-
selector: '${
|
|
32187
|
-
templateUrl: './${
|
|
32310
|
+
selector: '${weaver.prefix}-${weaver.id}-view',
|
|
32311
|
+
templateUrl: './${weaver.id}-view.html',
|
|
32188
32312
|
})
|
|
32189
|
-
export class ${
|
|
32313
|
+
export class ${weaver.className}View {}
|
|
32190
32314
|
`;
|
|
32191
32315
|
}
|
|
32192
32316
|
return `import { Component, computed, inject } from '@angular/core';
|
|
32193
32317
|
import { VIEW_STATE, type ViewState } from '@loomweaver/plugin-sdk';
|
|
32194
32318
|
|
|
32195
|
-
interface ${
|
|
32319
|
+
interface ${weaver.className}State {
|
|
32196
32320
|
readonly sort: 'natural' | 'alpha';
|
|
32197
32321
|
}
|
|
32198
32322
|
|
|
32199
|
-
const FRESH: ${
|
|
32323
|
+
const FRESH: ${weaver.className}State = { sort: 'natural' };
|
|
32200
32324
|
|
|
32201
32325
|
@Component({
|
|
32202
|
-
selector: '${
|
|
32203
|
-
templateUrl: './${
|
|
32326
|
+
selector: '${weaver.prefix}-${weaver.id}-view',
|
|
32327
|
+
templateUrl: './${weaver.id}-view.html',
|
|
32204
32328
|
})
|
|
32205
|
-
export class ${
|
|
32206
|
-
private readonly viewState = inject(VIEW_STATE) as ViewState<${
|
|
32329
|
+
export class ${weaver.className}View {
|
|
32330
|
+
private readonly viewState = inject(VIEW_STATE) as ViewState<${weaver.className}State>;
|
|
32207
32331
|
|
|
32208
32332
|
// undefined = a fresh instance, so apply your own default.
|
|
32209
32333
|
private readonly state = computed(() => this.viewState.value() ?? FRESH);
|
|
@@ -32220,8 +32344,8 @@ export class ${w.className}View {
|
|
|
32220
32344
|
}
|
|
32221
32345
|
`;
|
|
32222
32346
|
}
|
|
32223
|
-
function viewTemplateFile(
|
|
32224
|
-
const stateNote =
|
|
32347
|
+
function viewTemplateFile(weaver) {
|
|
32348
|
+
const stateNote = weaver.features.instanceable ? ` <button type="button" class="lw-btn lw-btn--default self-start" (click)="toggleSort()">
|
|
32225
32349
|
Sort: {{ sort() }}
|
|
32226
32350
|
</button>
|
|
32227
32351
|
<p class="text-sm text-content-faint">
|
|
@@ -32230,7 +32354,7 @@ function viewTemplateFile(w) {
|
|
|
32230
32354
|
</p>
|
|
32231
32355
|
` : "";
|
|
32232
32356
|
return `<div class="mx-auto flex max-w-2xl flex-col gap-4 p-6">
|
|
32233
|
-
<h2 class="text-lg font-semibold text-content">${
|
|
32357
|
+
<h2 class="text-lg font-semibold text-content">${weaver.name}</h2>
|
|
32234
32358
|
<p class="text-sm text-content-faint">
|
|
32235
32359
|
Your new weaver surface. Register more surfaces, commands, rail items and menus on
|
|
32236
32360
|
<code class="text-content">ctx</code> inside the plugin's
|
|
@@ -32239,13 +32363,13 @@ function viewTemplateFile(w) {
|
|
|
32239
32363
|
${stateNote}</div>
|
|
32240
32364
|
`;
|
|
32241
32365
|
}
|
|
32242
|
-
function childViewFile(
|
|
32366
|
+
function childViewFile(weaver, suffix, className) {
|
|
32243
32367
|
return `import { Component, inject } from '@angular/core';
|
|
32244
32368
|
import { ActivatedRoute } from '@angular/router';
|
|
32245
32369
|
|
|
32246
32370
|
@Component({
|
|
32247
|
-
selector: '${
|
|
32248
|
-
templateUrl: './${
|
|
32371
|
+
selector: '${weaver.prefix}-${weaver.id}-${suffix}-view',
|
|
32372
|
+
templateUrl: './${weaver.id}-${suffix}-view.html',
|
|
32249
32373
|
})
|
|
32250
32374
|
export class ${className} {
|
|
32251
32375
|
private readonly route = inject(ActivatedRoute, { optional: true });
|
|
@@ -32255,7 +32379,7 @@ export class ${className} {
|
|
|
32255
32379
|
}
|
|
32256
32380
|
`;
|
|
32257
32381
|
}
|
|
32258
|
-
function childViewTemplateFile(
|
|
32382
|
+
function childViewTemplateFile(weaver, heading) {
|
|
32259
32383
|
return `<div class="flex h-full flex-col gap-3 p-4">
|
|
32260
32384
|
<h3 class="text-sm font-semibold text-content">${heading}</h3>
|
|
32261
32385
|
<p class="text-sm text-content-faint">
|
|
@@ -32266,60 +32390,66 @@ function childViewTemplateFile(w, heading) {
|
|
|
32266
32390
|
</div>
|
|
32267
32391
|
`;
|
|
32268
32392
|
}
|
|
32269
|
-
function specFile2(
|
|
32270
|
-
return `import { ${
|
|
32393
|
+
function specFile2(weaver) {
|
|
32394
|
+
return `import { ${weaver.propertyName}Plugin } from './${weaver.id}.plugin';
|
|
32271
32395
|
|
|
32272
|
-
describe('${
|
|
32396
|
+
describe('${weaver.propertyName}Plugin', () => {
|
|
32273
32397
|
it('declares its manifest', () => {
|
|
32274
|
-
expect(${
|
|
32275
|
-
expect(${
|
|
32398
|
+
expect(${weaver.propertyName}Plugin.manifest.id).toBe('${weaver.id}');
|
|
32399
|
+
expect(${weaver.propertyName}Plugin.manifest.capabilities).toContain('contributions');
|
|
32276
32400
|
});
|
|
32277
32401
|
});
|
|
32278
32402
|
`;
|
|
32279
32403
|
}
|
|
32404
|
+
|
|
32405
|
+
// ../devkit/src/recipes/angular-weaver/recipe.ts
|
|
32406
|
+
function indexFile(weaver) {
|
|
32407
|
+
return `export { ${weaver.propertyName}Plugin } from './lib/plugin/${weaver.id}.plugin';
|
|
32408
|
+
`;
|
|
32409
|
+
}
|
|
32280
32410
|
var angularWeaver = {
|
|
32281
32411
|
id: "angular-weaver",
|
|
32282
32412
|
build(input) {
|
|
32283
|
-
const
|
|
32413
|
+
const weaver = resolveWeaverInput(input);
|
|
32284
32414
|
const files = {
|
|
32285
|
-
"src/index.ts": indexFile(
|
|
32286
|
-
[`src/lib/plugin/${
|
|
32287
|
-
"src/lib/i18n/en.json": i18nFile(
|
|
32288
|
-
"src/lib/i18n/de.json": i18nFile(
|
|
32289
|
-
"README.md": readmeFile(
|
|
32415
|
+
"src/index.ts": indexFile(weaver),
|
|
32416
|
+
[`src/lib/plugin/${weaver.id}.plugin.ts`]: pluginFile(weaver),
|
|
32417
|
+
"src/lib/i18n/en.json": i18nFile(weaver),
|
|
32418
|
+
"src/lib/i18n/de.json": i18nFile(weaver),
|
|
32419
|
+
"README.md": readmeFile(weaver)
|
|
32290
32420
|
};
|
|
32291
|
-
if (
|
|
32292
|
-
files[`src/lib/views/${
|
|
32293
|
-
|
|
32421
|
+
if (weaver.features.container) {
|
|
32422
|
+
files[`src/lib/views/${weaver.id}-canvas-view.ts`] = childViewFile(
|
|
32423
|
+
weaver,
|
|
32294
32424
|
"canvas",
|
|
32295
|
-
`${
|
|
32425
|
+
`${weaver.className}CanvasView`
|
|
32296
32426
|
);
|
|
32297
|
-
files[`src/lib/views/${
|
|
32298
|
-
|
|
32427
|
+
files[`src/lib/views/${weaver.id}-canvas-view.html`] = childViewTemplateFile(
|
|
32428
|
+
weaver,
|
|
32299
32429
|
"Canvas"
|
|
32300
32430
|
);
|
|
32301
|
-
files[`src/lib/views/${
|
|
32302
|
-
|
|
32431
|
+
files[`src/lib/views/${weaver.id}-details-view.ts`] = childViewFile(
|
|
32432
|
+
weaver,
|
|
32303
32433
|
"details",
|
|
32304
|
-
`${
|
|
32434
|
+
`${weaver.className}DetailsView`
|
|
32305
32435
|
);
|
|
32306
|
-
files[`src/lib/views/${
|
|
32307
|
-
|
|
32436
|
+
files[`src/lib/views/${weaver.id}-details-view.html`] = childViewTemplateFile(
|
|
32437
|
+
weaver,
|
|
32308
32438
|
"Details"
|
|
32309
32439
|
);
|
|
32310
32440
|
} else {
|
|
32311
|
-
files[`src/lib/views/${
|
|
32312
|
-
files[`src/lib/views/${
|
|
32441
|
+
files[`src/lib/views/${weaver.id}-view.ts`] = viewFile(weaver);
|
|
32442
|
+
files[`src/lib/views/${weaver.id}-view.html`] = viewTemplateFile(weaver);
|
|
32313
32443
|
}
|
|
32314
|
-
if (
|
|
32315
|
-
files[`src/lib/dialogs/${
|
|
32316
|
-
files[`src/lib/dialogs/${
|
|
32444
|
+
if (weaver.features.about) {
|
|
32445
|
+
files[`src/lib/dialogs/${weaver.id}-about-dialog.ts`] = aboutDialogFile(weaver);
|
|
32446
|
+
files[`src/lib/dialogs/${weaver.id}-about-dialog.html`] = aboutDialogTemplateFile(weaver);
|
|
32317
32447
|
}
|
|
32318
|
-
if (
|
|
32319
|
-
Object.assign(files, agentFiles(
|
|
32448
|
+
if (weaver.features.agent) {
|
|
32449
|
+
Object.assign(files, agentFiles(weaver));
|
|
32320
32450
|
}
|
|
32321
|
-
if (
|
|
32322
|
-
files[`src/lib/plugin/${
|
|
32451
|
+
if (weaver.features.spec) {
|
|
32452
|
+
files[`src/lib/plugin/${weaver.id}.plugin.spec.ts`] = specFile2(weaver);
|
|
32323
32453
|
}
|
|
32324
32454
|
return files;
|
|
32325
32455
|
}
|
|
@@ -32328,16 +32458,16 @@ var angularWeaver = {
|
|
|
32328
32458
|
// ../devkit/src/recipes/frame-plugin/recipe.ts
|
|
32329
32459
|
function resolveFramePluginInput(input) {
|
|
32330
32460
|
if (!isKebabId(input.id)) {
|
|
32331
|
-
throw new Error(`
|
|
32461
|
+
throw new Error(`Frame plugin id must be kebab-case (e.g. "notes"); got "${input.id}".`);
|
|
32332
32462
|
}
|
|
32333
32463
|
return { id: input.id, name: input.name?.trim() || toTitleCase(input.id) };
|
|
32334
32464
|
}
|
|
32335
|
-
function pluginHtml(
|
|
32465
|
+
function pluginHtml(plugin) {
|
|
32336
32466
|
return `<!doctype html>
|
|
32337
32467
|
<html lang="en">
|
|
32338
32468
|
<head>
|
|
32339
32469
|
<meta charset="utf-8" />
|
|
32340
|
-
<title>${
|
|
32470
|
+
<title>${plugin.name} \u2014 frame plugin (logic)</title>
|
|
32341
32471
|
</head>
|
|
32342
32472
|
<body>
|
|
32343
32473
|
<script src="/frame-kit/penpal.global.js"></script>
|
|
@@ -32346,7 +32476,7 @@ function pluginHtml(p) {
|
|
|
32346
32476
|
</html>
|
|
32347
32477
|
`;
|
|
32348
32478
|
}
|
|
32349
|
-
function pluginJs(
|
|
32479
|
+
function pluginJs(plugin) {
|
|
32350
32480
|
return `(function () {
|
|
32351
32481
|
const Penpal = globalThis.Penpal;
|
|
32352
32482
|
const messenger = new Penpal.WindowMessenger({
|
|
@@ -32358,27 +32488,27 @@ function pluginJs(p) {
|
|
|
32358
32488
|
connection.promise
|
|
32359
32489
|
.then(function (ctx) {
|
|
32360
32490
|
return Promise.all([
|
|
32361
|
-
ctx.toast({ message: '${
|
|
32491
|
+
ctx.toast({ message: '${plugin.name} ready', kind: 'success', timeoutMs: 4000 }),
|
|
32362
32492
|
ctx.registerSurface({
|
|
32363
|
-
id: '${
|
|
32364
|
-
title: '${
|
|
32365
|
-
iframe: '/${
|
|
32366
|
-
routable: { path: '${
|
|
32493
|
+
id: '${plugin.id}.view',
|
|
32494
|
+
title: '${plugin.name}',
|
|
32495
|
+
iframe: '/${plugin.id}/view.html',
|
|
32496
|
+
routable: { path: '${plugin.id}', titleIsLiteral: true },
|
|
32367
32497
|
}),
|
|
32368
32498
|
]);
|
|
32369
32499
|
})
|
|
32370
32500
|
.catch(function (error) {
|
|
32371
|
-
console.error('[${
|
|
32501
|
+
console.error('[${plugin.id}] frame plugin failed', error);
|
|
32372
32502
|
});
|
|
32373
32503
|
})();
|
|
32374
32504
|
`;
|
|
32375
32505
|
}
|
|
32376
|
-
function viewHtml(
|
|
32506
|
+
function viewHtml(plugin) {
|
|
32377
32507
|
return `<!doctype html>
|
|
32378
32508
|
<html lang="en">
|
|
32379
32509
|
<head>
|
|
32380
32510
|
<meta charset="utf-8" />
|
|
32381
|
-
<title>${
|
|
32511
|
+
<title>${plugin.name}</title>
|
|
32382
32512
|
<link rel="stylesheet" href="/frame-kit/lw-frame.css" />
|
|
32383
32513
|
<style>
|
|
32384
32514
|
body {
|
|
@@ -32394,7 +32524,7 @@ function viewHtml(p) {
|
|
|
32394
32524
|
</head>
|
|
32395
32525
|
<body>
|
|
32396
32526
|
<div class="wrap">
|
|
32397
|
-
<h1>${
|
|
32527
|
+
<h1>${plugin.name}</h1>
|
|
32398
32528
|
<p>
|
|
32399
32529
|
Your sandboxed surface. It runs isolated in its own iframe, so its body can be built with
|
|
32400
32530
|
any framework (React, Vue, Svelte, vanilla). The frame UI kit (served by the distribution
|
|
@@ -32422,16 +32552,16 @@ function viewHtml(p) {
|
|
|
32422
32552
|
</html>
|
|
32423
32553
|
`;
|
|
32424
32554
|
}
|
|
32425
|
-
function readme(
|
|
32555
|
+
function readme(plugin) {
|
|
32426
32556
|
return [
|
|
32427
|
-
`# ${
|
|
32557
|
+
`# ${plugin.name} \u2014 frame plugin`,
|
|
32428
32558
|
"",
|
|
32429
32559
|
`A framework-agnostic LoomWeaver plugin: it runs in an isolated \`<iframe sandbox>\` and`,
|
|
32430
32560
|
`receives \`ctx\` over Penpal RPC through the same default-deny broker a trusted plugin uses.`,
|
|
32431
32561
|
"",
|
|
32432
32562
|
"## Serve it + wire it into a distribution",
|
|
32433
32563
|
"",
|
|
32434
|
-
`1. Put these files under the distribution's static dir, e.g. \`public/${
|
|
32564
|
+
`1. Put these files under the distribution's static dir, e.g. \`public/${plugin.id}/\`. The plugin`,
|
|
32435
32565
|
` references the **frame UI kit** (\`@loomweaver/frame-kit\`) at \`/frame-kit/\` \u2014`,
|
|
32436
32566
|
` the distribution serves it via an assets glob (generated distributions already do):`,
|
|
32437
32567
|
"",
|
|
@@ -32442,43 +32572,40 @@ function readme(p) {
|
|
|
32442
32572
|
`2. Register + grant it in the composition root:`,
|
|
32443
32573
|
"",
|
|
32444
32574
|
" ```ts",
|
|
32445
|
-
` provideFramePlugins({ id: '${
|
|
32446
|
-
` provideCapabilityGrants({ '${
|
|
32575
|
+
` provideFramePlugins({ id: '${plugin.id}', entryUrl: '/${plugin.id}/plugin.html', capabilities: ['contributions', 'ui'] });`,
|
|
32576
|
+
` provideCapabilityGrants({ '${plugin.id}': ['contributions', 'ui'] });`,
|
|
32447
32577
|
" ```",
|
|
32448
32578
|
"",
|
|
32449
|
-
`The surface is routable at \`/${
|
|
32579
|
+
`The surface is routable at \`/${plugin.id}\`. Replace \`view.html\` with your own UI in any framework.`,
|
|
32450
32580
|
""
|
|
32451
32581
|
].join("\n");
|
|
32452
32582
|
}
|
|
32453
32583
|
var framePlugin = {
|
|
32454
32584
|
id: "frame-plugin",
|
|
32455
32585
|
build(input) {
|
|
32456
|
-
const
|
|
32586
|
+
const plugin = resolveFramePluginInput(input);
|
|
32457
32587
|
return {
|
|
32458
|
-
"plugin.html": pluginHtml(
|
|
32459
|
-
"plugin.js": pluginJs(
|
|
32460
|
-
"view.html": viewHtml(
|
|
32461
|
-
"README.md": readme(
|
|
32588
|
+
"plugin.html": pluginHtml(plugin),
|
|
32589
|
+
"plugin.js": pluginJs(plugin),
|
|
32590
|
+
"view.html": viewHtml(plugin),
|
|
32591
|
+
"README.md": readme(plugin)
|
|
32462
32592
|
};
|
|
32463
32593
|
}
|
|
32464
32594
|
};
|
|
32465
32595
|
|
|
32466
|
-
// ../devkit/src/recipes/platform-version.ts
|
|
32467
|
-
var PLATFORM_VERSION = "0.14.1";
|
|
32468
|
-
|
|
32469
32596
|
// ../devkit/src/recipes/angular-distribution/amendments.ts
|
|
32470
32597
|
var DISTRIBUTION_INITIAL_BUDGET = {
|
|
32471
32598
|
warning: "1.5MB",
|
|
32472
32599
|
error: "2MB"
|
|
32473
32600
|
};
|
|
32474
|
-
function distributionAmendments(
|
|
32601
|
+
function distributionAmendments(distribution) {
|
|
32475
32602
|
return [
|
|
32476
32603
|
{
|
|
32477
32604
|
kind: "package",
|
|
32478
32605
|
name: "@loomweaver/frame-kit",
|
|
32479
32606
|
version: `^${PLATFORM_VERSION}`
|
|
32480
32607
|
},
|
|
32481
|
-
...
|
|
32608
|
+
...distribution.styles === "tailwind" ? [
|
|
32482
32609
|
{
|
|
32483
32610
|
kind: "postcss",
|
|
32484
32611
|
file: ".postcssrc.json",
|
|
@@ -32510,26 +32637,13 @@ function distributionAmendments(d) {
|
|
|
32510
32637
|
];
|
|
32511
32638
|
}
|
|
32512
32639
|
|
|
32513
|
-
// ../devkit/src/recipes/shell-regions.ts
|
|
32514
|
-
var SHELL_REGIONS = [
|
|
32515
|
-
"{ id: 'top-bar', type: 'bar', dock: 'top' }",
|
|
32516
|
-
"{ id: 'primary', type: 'rail', dock: 'left' }",
|
|
32517
|
-
"{ id: 'left-panel', type: 'panel', dock: 'left' }",
|
|
32518
|
-
"{ id: 'right-panel', type: 'panel', dock: 'right' }",
|
|
32519
|
-
"{ id: 'main', type: 'content', dock: 'center' }",
|
|
32520
|
-
"{ id: 'status-bar', type: 'bar', dock: 'bottom' }"
|
|
32521
|
-
];
|
|
32522
|
-
function renderRegions(indent) {
|
|
32523
|
-
return SHELL_REGIONS.map((region) => `${indent}${region},`).join("\n");
|
|
32524
|
-
}
|
|
32525
|
-
|
|
32526
32640
|
// ../devkit/src/recipes/angular-distribution/logo.ts
|
|
32527
32641
|
var PLACEHOLDER_LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="512" height="512" viewBox="87.5 -53 1129.9 1129.9"><g paint-order="stroke"><path d="m0 0 12.6-10.6-9.2-8-14.5 12.1q-.7.8 1.2 1.5A17 17 0 0 1-2.4-.5Q-1 .7 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 265.2 368.8)"/><path d="m0 0 19-16c.6-.5.5-1.6-.2-2.3q-2.8-3-7.5-4.5c-1.2-.4-2.8-.1-3.4.4l-17 14.3z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 593.2 644.5)"/><path d="m0 0 5.8-4.9-9.2-8-2.8 2.3q-.9 1 0 2.5l4 7.4Q-1 .7 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#c59a2f;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 386.3 170)"/><path d="m0 0 12.4-10.4-9.2-8.1L-9.2-8.1Z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#c59a2f;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 608.2 356.6)"/><path d="m0 0 5-4.2c.6-.5.6-1.6 0-2.5l-4-7.4q-1-1.4-2.1-.7l-8 6.7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#c59a2f;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 933.4 630)"/><path d="m0 0 14.6-12.3-9.2-8L-11-6.6c-.6.5-.6 1.6.2 2.4Q-8-1.2-3.3.3C-2.2.7-.6.5 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 589.5 28.9)"/><path d="m0 0 17-14.3q.8-.8-1.3-1.4a17 17 0 0 1-7.5-4.5c-.7-.8-1.8-1-2.4-.5l-15 12.6Z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 948.5 330.8)"/><path d="m0 0 8.4-7-13.6-12-8.4 7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 514.5 213.2)"/><path d="m0 0 5.7-4.8q1.3-1 .4-2L3-9.5l-8.4 7 3 2.7A2 2 0 0 0 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 743 48)"/><path d="m0 0 8.4-7-2.2-2q-1-.7-2.3.2L-2-4q-1 1.2-.2 2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 158.6 526.5)"/><path d="m0 0 8.4-7L-5-18.9c-.5-.5-1.6-.4-2.3.2l-5.8 4.8q-1 1-.3 2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 492.8 507)"/><path d="m0 0 5.7-4.8q1.3-1 .4-2l-14.7-13-8.4 7.1L-2.3.3C-1.8.6-.7.5 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 902.6 182)"/><path d="m0 0 5.9-5Q7-6 6.2-7l-3-2.7-8.7 7.2L-2.4.3Q-1.4.9 0 0" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 1065.8 319.3)"/><path d="m0 0 8.6-7.2-13.6-12-8.6 7.2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 836.9 484.2)"/><path d="m0 0 8.6-7.2-2.2-2q-1-.6-2.4.3l-5.9 5Q-3-3-2.2-2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:#2e96c9;fill-rule:nonzero;opacity:1" transform="matrix(15.6303 0 0 -15.6303 481 797.5)"/><g transform="matrix(15.6303 0 0 -15.6303 877.6 706.6)"><linearGradient id="a" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 64.4 103.4)scale(2.18666 -2.18666)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#c59a2f;stop-opacity:1"/><stop offset="100%" style="stop-color:#614d06;stop-opacity:1"/></linearGradient><path d="m176.7 129.6 2-1.7 9.2 8-2 1.8z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#a);fill-rule:nonzero;opacity:1" transform="translate(-182.3 -132.8)"/></g><g transform="matrix(15.6303 0 0 -15.6303 711.4 567.2)"><linearGradient id="b" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 56.7 106)scale(-2.18726 2.18726)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#c59a2f;stop-opacity:1"/><stop offset="100%" style="stop-color:#614d06;stop-opacity:1"/></linearGradient><path d="m166 138.5 2.1-1.7 9.2 8.1-2 1.7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#b);fill-rule:nonzero;opacity:1" transform="translate(-171.7 -141.7)"/></g><g transform="matrix(15.6303 0 0 -15.6303 532.5 867.7)"><linearGradient id="c" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -65.1 251)scale(2.11643 -2.11643)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m155 125.2 8.5-7.2 2 1.8-8.6 7.2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#c);fill-rule:nonzero;opacity:1" transform="translate(-160.2 -122.5)"/></g><g transform="matrix(15.6303 0 0 -15.6303 707.2 713.6)"><linearGradient id="d" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(-134.3 114 30.2)scale(2.11524 -2.11524)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m166.1 135 8.6-7.1 2 1.7-8.6 7.2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#d);fill-rule:nonzero;opacity:1" transform="translate(-171.4 -132.3)"/></g><g transform="matrix(15.6303 0 0 -15.6303 888.2 554.5)"><linearGradient id="e" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -77.5 288)scale(2.11554 -2.11554)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m177.7 145.3 8.6-7.3 2 1.8-8.6 7.2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#e);fill-rule:nonzero;opacity:1" transform="translate(-183 -142.5)"/></g><g transform="matrix(15.6303 0 0 -15.6303 1062 401.2)"><linearGradient id="f" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -84.1 307.3)scale(-2.11583 2.11583)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m188.8 155 8.6-7.2 2 1.8-8.6 7.2z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#f);fill-rule:nonzero;opacity:1" transform="translate(-194.1 -152.3)"/></g><g transform="matrix(15.6303 0 0 -15.6303 206.5 595.5)"><linearGradient id="g" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(225.7 98.9 40.5)scale(-2.0962 2.0962)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m134.3 142.5 8.2-7 2 1.8-8.3 7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#g);fill-rule:nonzero;opacity:1" transform="translate(-139.4 -139.9)"/></g><g transform="matrix(15.6303 0 0 -15.6303 381 441.4)"><linearGradient id="h" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(-134.3 107 43.3)scale(2.0962 -2.0962)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m145.4 152.3 8.3-6.9 2 1.8-8.3 6.9z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#h);fill-rule:nonzero;opacity:1" transform="translate(-150.5 -149.8)"/></g><g transform="matrix(15.6303 0 0 -15.6303 562 282.3)"><linearGradient id="i" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(225.7 114.5 45.7)scale(-2.0953 2.0953)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m157 162.5 8.2-7 2 1.8-8.2 7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#i);fill-rule:nonzero;opacity:1" transform="translate(-162.1 -160)"/></g><g transform="matrix(15.6303 0 0 -15.6303 736 129)"><linearGradient id="j" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -115.1 291)scale(-2.0956 2.0956)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m168.1 172.3 8.3-6.9 2 1.7-8.3 7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#j);fill-rule:nonzero;opacity:1" transform="translate(-173.2 -169.7)"/></g><g transform="matrix(15.6303 0 0 -15.6303 544.8 441.3)"><linearGradient id="k" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 50.4 108)scale(2.18666 -2.18666)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#c59a2f;stop-opacity:1"/><stop offset="100%" style="stop-color:#614d06;stop-opacity:1"/></linearGradient><path d="m155.9 147 2-1.6 9.2 8-2 1.8z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#k);fill-rule:nonzero;opacity:1" transform="translate(-161.5 -150.3)"/></g><g transform="matrix(15.6303 0 0 -15.6303 390.7 294.8)"><linearGradient id="l" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 42.9 110.5)scale(-2.17357 2.17357)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#c59a2f;stop-opacity:1"/><stop offset="100%" style="stop-color:#614d06;stop-opacity:1"/></linearGradient><path d="m145.6 156 2-1.7 9 8-2 1.6z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#l);fill-rule:nonzero;opacity:1" transform="translate(-151.2 -159.1)"/></g><g transform="matrix(15.6303 0 0 -15.6303 375.9 585.7)"><linearGradient id="m" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 46.2 101)scale(-1.9968 1.9968)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m144.7 137.2 1.8-1.5 9.2 8.1-1.8 1.5z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#m);fill-rule:nonzero;opacity:1" transform="translate(-150.2 -140.5)"/></g><g transform="matrix(15.6303 0 0 -15.6303 733.7 274)"><linearGradient id="n" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(-44.3 284 -131.7)scale(1.9968 -1.9968)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m167.6 157.2 1.8-1.5 9.2 8-1.8 1.6z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#n);fill-rule:nonzero;opacity:1" transform="translate(-173.1 -160.5)"/></g><g transform="matrix(15.6303 0 0 -15.6303 893.6 408.2)"><linearGradient id="o" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 61 113.2)scale(1.9962 -1.9962)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m177.8 148.6 1.8-1.5 9.2 8-1.8 1.6z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#o);fill-rule:nonzero;opacity:1" transform="translate(-183.3 -151.9)"/></g><g transform="matrix(15.6303 0 0 -15.6303 535.9 720)"><linearGradient id="p" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(135.7 53.6 98.6)scale(1.9962 -1.9962)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m155 128.6 1.7-1.5 9.2 8.1-1.8 1.5z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#p);fill-rule:nonzero;opacity:1" transform="translate(-160.4 -132)"/></g><g transform="matrix(15.6303 0 0 -15.6303 717.3 421.6)"><linearGradient id="q" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -93.5 280)scale(-2.095 2.095)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m167 153.6 8.2-7 2 1.8-8.3 7z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#q);fill-rule:nonzero;opacity:1" transform="translate(-172 -151)"/></g><g transform="matrix(15.6303 0 0 -15.6303 542.1 575.9)"><linearGradient id="r" x1="0" x2="1" y1="0" y2="0" gradientTransform="rotate(45.7 -87 261)scale(2.0956 -2.0956)" gradientUnits="userSpaceOnUse"><stop offset="0%" style="stop-color:#2e96c9;stop-opacity:1"/><stop offset="100%" style="stop-color:#2479a3;stop-opacity:1"/></linearGradient><path d="m155.7 143.8 8.3-7 2 1.8-8.3 6.9z" style="stroke:none;stroke-width:1;stroke-dasharray:none;stroke-linecap:butt;stroke-dashoffset:0;stroke-linejoin:miter;stroke-miterlimit:4;fill:url(#r);fill-rule:nonzero;opacity:1" transform="translate(-160.8 -141.2)"/></g></g></svg>
|
|
32528
32642
|
`;
|
|
32529
32643
|
|
|
32530
32644
|
// ../devkit/src/recipes/angular-distribution/readme.ts
|
|
32531
|
-
function stylesNotes(
|
|
32532
|
-
if (
|
|
32645
|
+
function stylesNotes(distribution) {
|
|
32646
|
+
if (distribution.styles === "precompiled") {
|
|
32533
32647
|
return [
|
|
32534
32648
|
"`src/styles.css` imports the stylesheet **we** compiled \u2014 tokens, the `.lw-*` class contracts",
|
|
32535
32649
|
"and every utility the shell's own templates use, 67 KB minified and 11 KB over the wire. There",
|
|
@@ -32567,9 +32681,9 @@ function stylesNotes(d) {
|
|
|
32567
32681
|
"Bootstrap, Bulma or hand-written CSS."
|
|
32568
32682
|
];
|
|
32569
32683
|
}
|
|
32570
|
-
function readme2(
|
|
32684
|
+
function readme2(distribution) {
|
|
32571
32685
|
return [
|
|
32572
|
-
`# ${
|
|
32686
|
+
`# ${distribution.title} \u2014 a LoomWeaver distribution`,
|
|
32573
32687
|
"",
|
|
32574
32688
|
`The composition root that assembles the platform into a shippable product. It renders the bare`,
|
|
32575
32689
|
`shell out of the box; add your weavers and branding below. Your own README is untouched \u2014 the`,
|
|
@@ -32577,29 +32691,23 @@ function readme2(d) {
|
|
|
32577
32691
|
"",
|
|
32578
32692
|
"## Run it",
|
|
32579
32693
|
"",
|
|
32580
|
-
"In an Nx workspace (the generator wired the project):",
|
|
32581
|
-
"",
|
|
32582
32694
|
"```sh",
|
|
32583
|
-
`nx serve ${
|
|
32695
|
+
`nx serve ${distribution.name} # in an Nx workspace`,
|
|
32696
|
+
"ng serve # in an Angular CLI application",
|
|
32584
32697
|
"```",
|
|
32585
32698
|
"",
|
|
32586
|
-
"
|
|
32587
|
-
"
|
|
32588
|
-
"
|
|
32589
|
-
"
|
|
32590
|
-
|
|
32591
|
-
"them content you authored. Under Nx it additionally merges the build targets into that",
|
|
32592
|
-
"project's `project.json`, keeping the targets, `implicitDependencies` and tags it already had.",
|
|
32699
|
+
"The files keep Angular's own shape: `main.ts` bootstraps `App`, `App` renders `<lw-shell />`, and",
|
|
32700
|
+
"everything this product is made of lives in `app.config.ts`. Nothing of the generated app is",
|
|
32701
|
+
"deleted. Over an existing application `--force` replaces exactly the files above, all of them",
|
|
32702
|
+
"bootstrap wiring and none of them content you authored. Whether the build around them was wired",
|
|
32703
|
+
'as well depends on how they arrived; see "Build wiring" below.',
|
|
32593
32704
|
"",
|
|
32594
32705
|
"Two leftovers from `ng new` are no longer referenced and can go: `src/app/app.routes.ts` (the",
|
|
32595
32706
|
"shell owns content routing via `provideShellRouter()`) and `src/app/app.css`. The starter test",
|
|
32596
32707
|
"`src/app/app.spec.ts` was replaced by one that boots the shell with this composition root, so",
|
|
32597
32708
|
"`ng test` is green as generated; test your own components beside it.",
|
|
32598
32709
|
"",
|
|
32599
|
-
|
|
32600
|
-
"one would fail a lint policy you never opted this project into. If your workspace enforces",
|
|
32601
|
-
"module boundaries, give it tags your constraints allow \u2014 `--tags` at generation time, or",
|
|
32602
|
-
"`tags` in `project.json` afterwards.",
|
|
32710
|
+
...UNTAGGED_NOTE,
|
|
32603
32711
|
"",
|
|
32604
32712
|
"## Compose weavers + branding (in `src/app/app.config.ts`)",
|
|
32605
32713
|
"",
|
|
@@ -32647,7 +32755,7 @@ function readme2(d) {
|
|
|
32647
32755
|
"",
|
|
32648
32756
|
"```ts",
|
|
32649
32757
|
"provideCommandPaletteEntry(); // top bar, end slot, order 5",
|
|
32650
|
-
|
|
32758
|
+
`provideQuickOpenEntry({ bar: '${TOP_BAR_REGION}', order: 4 }); // \u2026or put it wherever you want`,
|
|
32651
32759
|
"```",
|
|
32652
32760
|
"",
|
|
32653
32761
|
"A badge never outlives what it opens. Drop the search and its badge goes with it; the same",
|
|
@@ -32680,13 +32788,21 @@ function readme2(d) {
|
|
|
32680
32788
|
"",
|
|
32681
32789
|
"## Styles",
|
|
32682
32790
|
"",
|
|
32683
|
-
...stylesNotes(
|
|
32791
|
+
...stylesNotes(distribution),
|
|
32684
32792
|
"",
|
|
32685
32793
|
"## Build wiring",
|
|
32686
32794
|
"",
|
|
32687
|
-
"The
|
|
32688
|
-
"
|
|
32689
|
-
"
|
|
32795
|
+
"The build target needs the stylesheet, three asset globs, the service worker and two production",
|
|
32796
|
+
"settings. Who added them depends on how these files arrived:",
|
|
32797
|
+
"",
|
|
32798
|
+
"- **The Nx generator** wrote them into the project's `project.json`. Composing over an existing",
|
|
32799
|
+
" project with `--force`, it merged them in value by value: every value the project already had",
|
|
32800
|
+
" stays, and what the workbench needs is added beside it.",
|
|
32801
|
+
"- **The CLI inside an Angular CLI application** added what was missing to the build target in",
|
|
32802
|
+
" `angular.json` and listed each line it added. Anything you had already set was left as you set",
|
|
32803
|
+
" it.",
|
|
32804
|
+
"- **The MCP server**, and the CLI anywhere else, cannot reach your build configuration, so they",
|
|
32805
|
+
" returned these as steps still to do by hand.",
|
|
32690
32806
|
"",
|
|
32691
32807
|
"What each is for, so that nobody removes one as clutter. The **`@loomweaver/shell/i18n` glob**",
|
|
32692
32808
|
"serves the strings the shell fetches at runtime; without it every label in the chrome renders as",
|
|
@@ -32746,7 +32862,7 @@ function readme2(d) {
|
|
|
32746
32862
|
}
|
|
32747
32863
|
|
|
32748
32864
|
// ../devkit/src/recipes/angular-distribution/recipe.ts
|
|
32749
|
-
var
|
|
32865
|
+
var DISTRIBUTION_STYLES = ["tailwind", "precompiled"];
|
|
32750
32866
|
function resolveDistributionInput(input) {
|
|
32751
32867
|
if (!isKebabId(input.name)) {
|
|
32752
32868
|
throw new Error(
|
|
@@ -32754,9 +32870,9 @@ function resolveDistributionInput(input) {
|
|
|
32754
32870
|
);
|
|
32755
32871
|
}
|
|
32756
32872
|
const styles = input.styles ?? "tailwind";
|
|
32757
|
-
if (!
|
|
32873
|
+
if (!DISTRIBUTION_STYLES.includes(styles)) {
|
|
32758
32874
|
throw new Error(
|
|
32759
|
-
`Unknown styles option "${styles}"; expected one of ${
|
|
32875
|
+
`Unknown styles option "${styles}"; expected one of ${DISTRIBUTION_STYLES.join(", ")}.`
|
|
32760
32876
|
);
|
|
32761
32877
|
}
|
|
32762
32878
|
const directory = input.directory === void 0 ? `apps/${input.name}` : input.directory.trim();
|
|
@@ -32777,7 +32893,7 @@ import { App } from './app/app';
|
|
|
32777
32893
|
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
|
|
32778
32894
|
`;
|
|
32779
32895
|
}
|
|
32780
|
-
function appConfigTs(
|
|
32896
|
+
function appConfigTs(distribution) {
|
|
32781
32897
|
return `import { ApplicationConfig } from '@angular/core';
|
|
32782
32898
|
import {
|
|
32783
32899
|
provideCommandPaletteEntry,
|
|
@@ -32790,8 +32906,8 @@ import {
|
|
|
32790
32906
|
import { provideProductIdentity } from '@loomweaver/plugin-sdk';
|
|
32791
32907
|
|
|
32792
32908
|
/* Which regions exist and where they dock. Contributions target these ids, so a region a
|
|
32793
|
-
weaver names but this layout omits renders nothing \u2014 silently. '
|
|
32794
|
-
'
|
|
32909
|
+
weaver names but this layout omits renders nothing \u2014 silently. '${RAIL_REGION}' (rail) and
|
|
32910
|
+
'${STATUS_BAR_REGION}' (bar) are what the scaffolded weaver targets. */
|
|
32795
32911
|
export const layout: ShellLayout = {
|
|
32796
32912
|
regions: [
|
|
32797
32913
|
${renderRegions(" ".repeat(4))}
|
|
@@ -32813,7 +32929,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
32813
32929
|
provideCommandPaletteEntry(),
|
|
32814
32930
|
provideQuickOpenEntry(),
|
|
32815
32931
|
provideProductIdentity({
|
|
32816
|
-
name: '${
|
|
32932
|
+
name: '${distribution.title}',
|
|
32817
32933
|
tagline: 'Built on LoomWeaver',
|
|
32818
32934
|
logoUrl: 'logo.svg',
|
|
32819
32935
|
}),
|
|
@@ -32846,7 +32962,7 @@ function appConfigSpec() {
|
|
|
32846
32962
|
describe('layout', () => {
|
|
32847
32963
|
it('declares the regions contributions target', () => {
|
|
32848
32964
|
const ids = layout.regions.map((region) => region.id);
|
|
32849
|
-
for (const id of ['
|
|
32965
|
+
for (const id of ['${RAIL_REGION}', '${STATUS_BAR_REGION}', '${CONTENT_REGION}']) {
|
|
32850
32966
|
expect(ids).toContain(id);
|
|
32851
32967
|
}
|
|
32852
32968
|
});
|
|
@@ -32878,12 +32994,12 @@ describe('App', () => {
|
|
|
32878
32994
|
});
|
|
32879
32995
|
`;
|
|
32880
32996
|
}
|
|
32881
|
-
function indexHtml(
|
|
32997
|
+
function indexHtml(distribution) {
|
|
32882
32998
|
return `<!DOCTYPE html>
|
|
32883
32999
|
<html lang="en">
|
|
32884
33000
|
<head>
|
|
32885
33001
|
<meta charset="utf-8" />
|
|
32886
|
-
<title>${
|
|
33002
|
+
<title>${distribution.title}</title>
|
|
32887
33003
|
<base href="/" />
|
|
32888
33004
|
<meta
|
|
32889
33005
|
http-equiv="Content-Security-Policy"
|
|
@@ -32921,7 +33037,7 @@ function precompiledCss() {
|
|
|
32921
33037
|
@import '@loomweaver/shell/styles/shell.css';
|
|
32922
33038
|
`;
|
|
32923
33039
|
}
|
|
32924
|
-
function tailwindCss(
|
|
33040
|
+
function tailwindCss(distribution) {
|
|
32925
33041
|
return `@import 'tailwindcss';
|
|
32926
33042
|
|
|
32927
33043
|
/* LoomWeaver design tokens + theme (light/dark, brand colors). Every @import has to precede the
|
|
@@ -32934,12 +33050,12 @@ function tailwindCss(d) {
|
|
|
32934
33050
|
/* Generate the utility classes the shell (and your own components) use. The @source path must
|
|
32935
33051
|
reach your workspace's node_modules FROM THIS FILE \u2014 adjust the ../ hops if this project does
|
|
32936
33052
|
not sit at that depth, or Tailwind silently emits none of the shell's classes. */
|
|
32937
|
-
@source '${
|
|
33053
|
+
@source '${distribution.nodeModulesFromSrc}/@loomweaver/shell';
|
|
32938
33054
|
@source './';
|
|
32939
33055
|
`;
|
|
32940
33056
|
}
|
|
32941
|
-
function stylesCss(
|
|
32942
|
-
return
|
|
33057
|
+
function stylesCss(distribution) {
|
|
33058
|
+
return distribution.styles === "precompiled" ? precompiledCss() : tailwindCss(distribution);
|
|
32943
33059
|
}
|
|
32944
33060
|
function ngswConfig() {
|
|
32945
33061
|
return JSON.stringify(
|
|
@@ -32978,12 +33094,12 @@ function ngswConfig() {
|
|
|
32978
33094
|
2
|
|
32979
33095
|
);
|
|
32980
33096
|
}
|
|
32981
|
-
function manifest(
|
|
33097
|
+
function manifest(distribution) {
|
|
32982
33098
|
return JSON.stringify(
|
|
32983
33099
|
{
|
|
32984
|
-
name:
|
|
32985
|
-
short_name:
|
|
32986
|
-
description: `${
|
|
33100
|
+
name: distribution.title,
|
|
33101
|
+
short_name: distribution.title,
|
|
33102
|
+
description: `${distribution.title} \u2014 a LoomWeaver distribution.`,
|
|
32987
33103
|
start_url: "/",
|
|
32988
33104
|
display: "standalone",
|
|
32989
33105
|
background_color: "#ffffff",
|
|
@@ -33000,22 +33116,22 @@ var angularDistribution = {
|
|
|
33000
33116
|
return distributionAmendments(resolveDistributionInput(input));
|
|
33001
33117
|
},
|
|
33002
33118
|
build(input) {
|
|
33003
|
-
const
|
|
33119
|
+
const distribution = resolveDistributionInput(input);
|
|
33004
33120
|
return {
|
|
33005
33121
|
"src/main.ts": mainTs(),
|
|
33006
|
-
"src/app/app.config.ts": appConfigTs(
|
|
33007
|
-
...
|
|
33122
|
+
"src/app/app.config.ts": appConfigTs(distribution),
|
|
33123
|
+
...distribution.withTests && {
|
|
33008
33124
|
"src/app/app.config.spec.ts": appConfigSpec(),
|
|
33009
33125
|
"src/app/app.spec.ts": appSpec()
|
|
33010
33126
|
},
|
|
33011
33127
|
"src/app/app.ts": appTs(),
|
|
33012
33128
|
"src/app/app.html": appHtml(),
|
|
33013
|
-
"src/index.html": indexHtml(
|
|
33014
|
-
"src/styles.css": stylesCss(
|
|
33129
|
+
"src/index.html": indexHtml(distribution),
|
|
33130
|
+
"src/styles.css": stylesCss(distribution),
|
|
33015
33131
|
"public/logo.svg": PLACEHOLDER_LOGO_SVG,
|
|
33016
|
-
"public/manifest.webmanifest": manifest(
|
|
33132
|
+
"public/manifest.webmanifest": manifest(distribution) + "\n",
|
|
33017
33133
|
"ngsw-config.json": ngswConfig() + "\n",
|
|
33018
|
-
"LOOMWEAVER.md": readme2(
|
|
33134
|
+
"LOOMWEAVER.md": readme2(distribution)
|
|
33019
33135
|
};
|
|
33020
33136
|
}
|
|
33021
33137
|
};
|
|
@@ -33033,12 +33149,12 @@ function resolveAuthSourceInput(input) {
|
|
|
33033
33149
|
bare: input.bare ?? false
|
|
33034
33150
|
};
|
|
33035
33151
|
}
|
|
33036
|
-
function sessionPluginSymbol(
|
|
33037
|
-
return `${
|
|
33152
|
+
function sessionPluginSymbol(source) {
|
|
33153
|
+
return `${source.propertyName}SessionPlugin`;
|
|
33038
33154
|
}
|
|
33039
|
-
function moduleFile(
|
|
33155
|
+
function moduleFile(source) {
|
|
33040
33156
|
return `// Provider-neutral AuthSource. LoomWeaver owns no authentication \u2014 it only reacts to
|
|
33041
|
-
// a session snapshot. Wire it with: provideAuthSource(() => ${
|
|
33157
|
+
// a session snapshot. Wire it with: provideAuthSource(() => ${source.propertyName}AuthSource()).
|
|
33042
33158
|
// Replace the dev switcher below by mapping your product's real session onto an AuthSnapshot.
|
|
33043
33159
|
import { signal, Signal } from '@angular/core';
|
|
33044
33160
|
import { ANONYMOUS, AuthSnapshot } from '@loomweaver/plugin-sdk';
|
|
@@ -33059,52 +33175,40 @@ const ADMIN: AuthSnapshot = {
|
|
|
33059
33175
|
|
|
33060
33176
|
const state = signal<AuthSnapshot>(ANONYMOUS);
|
|
33061
33177
|
|
|
33062
|
-
export function ${
|
|
33178
|
+
export function ${source.propertyName}AuthSource(): Signal<AuthSnapshot> {
|
|
33063
33179
|
return state.asReadonly();
|
|
33064
33180
|
}
|
|
33065
33181
|
|
|
33066
|
-
export function
|
|
33067
|
-
|
|
33068
|
-
|
|
33069
|
-
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33182
|
+
export function signIn${source.className}User(): void {
|
|
33183
|
+
state.set(USER);
|
|
33184
|
+
}
|
|
33185
|
+
|
|
33186
|
+
export function switch${source.className}Account(): void {
|
|
33187
|
+
state.set(state().roles.includes('admin') ? USER : ADMIN);
|
|
33188
|
+
}
|
|
33189
|
+
|
|
33190
|
+
export function signOut${source.className}User(): void {
|
|
33191
|
+
state.set(ANONYMOUS);
|
|
33074
33192
|
}
|
|
33075
33193
|
`;
|
|
33076
33194
|
}
|
|
33077
|
-
function pluginFile2(
|
|
33195
|
+
function pluginFile2(source) {
|
|
33078
33196
|
return `// The verbs a user needs to operate the stand-in session: sign in, switch the account, sign
|
|
33079
33197
|
// out, from a rail item whose menu carries them. This is presentation for a product that has no
|
|
33080
33198
|
// backend yet; nothing here protects anything. Delete it once your own session arrives.
|
|
33081
33199
|
import type { Disposable, Plugin, PluginContext } from '@loomweaver/plugin-sdk';
|
|
33082
|
-
import {
|
|
33200
|
+
import {
|
|
33201
|
+
${source.propertyName}AuthSource,
|
|
33202
|
+
signIn${source.className}User,
|
|
33203
|
+
signOut${source.className}User,
|
|
33204
|
+
switch${source.className}Account,
|
|
33205
|
+
} from './${source.name}-auth-source';
|
|
33083
33206
|
|
|
33084
|
-
const MENU = '
|
|
33085
|
-
const snapshot = ${
|
|
33207
|
+
const MENU = '${SESSION_PLUGIN_ID}.account/menu';
|
|
33208
|
+
const snapshot = ${source.propertyName}AuthSource();
|
|
33086
33209
|
|
|
33087
33210
|
let drawn: Disposable[] = [];
|
|
33088
33211
|
|
|
33089
|
-
function signIn(): void {
|
|
33090
|
-
if (!snapshot().authenticated) {
|
|
33091
|
-
cycle${a.className}User();
|
|
33092
|
-
}
|
|
33093
|
-
}
|
|
33094
|
-
|
|
33095
|
-
function switchAccount(): void {
|
|
33096
|
-
cycle${a.className}User();
|
|
33097
|
-
if (!snapshot().authenticated) {
|
|
33098
|
-
cycle${a.className}User();
|
|
33099
|
-
}
|
|
33100
|
-
}
|
|
33101
|
-
|
|
33102
|
-
function signOut(): void {
|
|
33103
|
-
while (snapshot().authenticated) {
|
|
33104
|
-
cycle${a.className}User();
|
|
33105
|
-
}
|
|
33106
|
-
}
|
|
33107
|
-
|
|
33108
33212
|
function initialsOf(name: string): string {
|
|
33109
33213
|
return name
|
|
33110
33214
|
.split(' ')
|
|
@@ -33123,7 +33227,7 @@ function draw(ctx: PluginContext): void {
|
|
|
33123
33227
|
drawn = [
|
|
33124
33228
|
ctx.registerRailItem({
|
|
33125
33229
|
id: 'session.account',
|
|
33126
|
-
rail: '
|
|
33230
|
+
rail: '${RAIL_REGION}',
|
|
33127
33231
|
icon: 'account',
|
|
33128
33232
|
title: current.authenticated ? name : 'session.signIn',
|
|
33129
33233
|
anchor: 'bottom',
|
|
@@ -33168,10 +33272,10 @@ function draw(ctx: PluginContext): void {
|
|
|
33168
33272
|
];
|
|
33169
33273
|
}
|
|
33170
33274
|
|
|
33171
|
-
export const ${sessionPluginSymbol(
|
|
33275
|
+
export const ${sessionPluginSymbol(source)}: Plugin = {
|
|
33172
33276
|
manifest: { id: 'session', name: 'Account', capabilities: ['contributions'] },
|
|
33173
33277
|
activate(ctx) {
|
|
33174
|
-
const
|
|
33278
|
+
const andRedraw = (step: () => void) => () => {
|
|
33175
33279
|
step();
|
|
33176
33280
|
draw(ctx);
|
|
33177
33281
|
};
|
|
@@ -33180,21 +33284,21 @@ export const ${sessionPluginSymbol(a)}: Plugin = {
|
|
|
33180
33284
|
title: 'session.signIn',
|
|
33181
33285
|
icon: 'account',
|
|
33182
33286
|
access: { authenticated: false },
|
|
33183
|
-
run:
|
|
33287
|
+
run: andRedraw(signIn${source.className}User),
|
|
33184
33288
|
});
|
|
33185
33289
|
ctx.registerCommand({
|
|
33186
33290
|
id: 'session.switchAccount',
|
|
33187
33291
|
title: 'session.switchAccount',
|
|
33188
33292
|
icon: 'account',
|
|
33189
33293
|
access: { authenticated: true },
|
|
33190
|
-
run:
|
|
33294
|
+
run: andRedraw(switch${source.className}Account),
|
|
33191
33295
|
});
|
|
33192
33296
|
ctx.registerCommand({
|
|
33193
33297
|
id: 'session.signOut',
|
|
33194
33298
|
title: 'session.signOut',
|
|
33195
33299
|
icon: 'signOut',
|
|
33196
33300
|
access: { authenticated: true },
|
|
33197
|
-
run:
|
|
33301
|
+
run: andRedraw(signOut${source.className}User),
|
|
33198
33302
|
});
|
|
33199
33303
|
draw(ctx);
|
|
33200
33304
|
},
|
|
@@ -33207,9 +33311,14 @@ export const ${sessionPluginSymbol(a)}: Plugin = {
|
|
|
33207
33311
|
};
|
|
33208
33312
|
`;
|
|
33209
33313
|
}
|
|
33210
|
-
function indexFile2(
|
|
33211
|
-
return `export {
|
|
33212
|
-
|
|
33314
|
+
function indexFile2(source) {
|
|
33315
|
+
return `export {
|
|
33316
|
+
${source.propertyName}AuthSource,
|
|
33317
|
+
signIn${source.className}User,
|
|
33318
|
+
signOut${source.className}User,
|
|
33319
|
+
switch${source.className}Account,
|
|
33320
|
+
} from './${source.name}-auth-source';
|
|
33321
|
+
export { ${sessionPluginSymbol(source)} } from './${source.name}-session.plugin';
|
|
33213
33322
|
`;
|
|
33214
33323
|
}
|
|
33215
33324
|
var I18N = {
|
|
@@ -33231,15 +33340,15 @@ var I18N = {
|
|
|
33231
33340
|
var authSource = {
|
|
33232
33341
|
id: "auth-source",
|
|
33233
33342
|
build(input) {
|
|
33234
|
-
const
|
|
33235
|
-
const
|
|
33236
|
-
if (
|
|
33237
|
-
return
|
|
33343
|
+
const source = resolveAuthSourceInput(input);
|
|
33344
|
+
const moduleFiles = { [`${source.name}-auth-source.ts`]: moduleFile(source) };
|
|
33345
|
+
if (source.bare) {
|
|
33346
|
+
return moduleFiles;
|
|
33238
33347
|
}
|
|
33239
33348
|
return {
|
|
33240
|
-
...
|
|
33241
|
-
[`${
|
|
33242
|
-
"index.ts": indexFile2(
|
|
33349
|
+
...moduleFiles,
|
|
33350
|
+
[`${source.name}-session.plugin.ts`]: pluginFile2(source),
|
|
33351
|
+
"index.ts": indexFile2(source),
|
|
33243
33352
|
"i18n/en.json": `${JSON.stringify(I18N.en, null, 2)}
|
|
33244
33353
|
`,
|
|
33245
33354
|
"i18n/de.json": `${JSON.stringify(I18N.de, null, 2)}
|
|
@@ -33257,14 +33366,14 @@ function resolveSettingsStoreInput(input) {
|
|
|
33257
33366
|
}
|
|
33258
33367
|
return { name: input.name, className: toPascalCase(input.name) };
|
|
33259
33368
|
}
|
|
33260
|
-
function moduleFile2(
|
|
33369
|
+
function moduleFile2(store) {
|
|
33261
33370
|
return `// A backend-backed settings store for the SETTINGS_STORE port. The platform
|
|
33262
33371
|
// ships local defaults; the product persists settings against its own backend. Only genuine
|
|
33263
33372
|
// settings flow through this port \u2014 working state stays on WORKING_STATE_STORE. Wire it with:
|
|
33264
|
-
// provideSettingsStore(new ${
|
|
33373
|
+
// provideSettingsStore(new ${store.className}SettingsStore()).
|
|
33265
33374
|
import { KeyValueStore } from '@loomweaver/shell';
|
|
33266
33375
|
|
|
33267
|
-
export class ${
|
|
33376
|
+
export class ${store.className}SettingsStore implements KeyValueStore {
|
|
33268
33377
|
constructor(private readonly baseUrl = '/api/settings') {}
|
|
33269
33378
|
|
|
33270
33379
|
async get(key: string): Promise<string | undefined> {
|
|
@@ -33292,28 +33401,28 @@ export class ${s.className}SettingsStore implements KeyValueStore {
|
|
|
33292
33401
|
var settingsStore = {
|
|
33293
33402
|
id: "settings-store",
|
|
33294
33403
|
build(input) {
|
|
33295
|
-
const
|
|
33296
|
-
return { [`${
|
|
33404
|
+
const store = resolveSettingsStoreInput(input);
|
|
33405
|
+
return { [`${store.name}-settings-store.ts`]: moduleFile2(store) };
|
|
33297
33406
|
}
|
|
33298
33407
|
};
|
|
33299
33408
|
|
|
33300
33409
|
// ../devkit/src/recipes/theme/recipe.ts
|
|
33301
|
-
var
|
|
33410
|
+
var THEME_PRESETS = ["literal", "bootstrap"];
|
|
33302
33411
|
function resolveThemeInput(input) {
|
|
33303
33412
|
if (!isKebabId(input.name)) {
|
|
33304
33413
|
throw new Error(`Theme name must be kebab-case (e.g. "midnight"); got "${input.name}".`);
|
|
33305
33414
|
}
|
|
33306
33415
|
const preset = input.preset ?? "literal";
|
|
33307
|
-
if (!
|
|
33308
|
-
throw new Error(`Unknown theme preset "${preset}"; expected one of ${
|
|
33416
|
+
if (!THEME_PRESETS.includes(preset)) {
|
|
33417
|
+
throw new Error(`Unknown theme preset "${preset}"; expected one of ${THEME_PRESETS.join(", ")}.`);
|
|
33309
33418
|
}
|
|
33310
33419
|
return { name: input.name, title: toTitleCase(input.name), preset };
|
|
33311
33420
|
}
|
|
33312
|
-
function cssFile(
|
|
33313
|
-
return `/* ${
|
|
33421
|
+
function cssFile(theme2) {
|
|
33422
|
+
return `/* ${theme2.title} theme \u2014 overrides LoomWeaver's --lw-* design tokens.
|
|
33314
33423
|
Import it AFTER the shell theme in your distribution styles.css:
|
|
33315
33424
|
@import '@loomweaver/shell/styles/theme.css';
|
|
33316
|
-
@import './themes/${
|
|
33425
|
+
@import './themes/${theme2.name}.css';
|
|
33317
33426
|
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
33318
33427
|
(Product default < Plugin < Tenant). The --lw-* ladder flips in :root.dark, so override both. */
|
|
33319
33428
|
|
|
@@ -33348,15 +33457,15 @@ function cssFile(t) {
|
|
|
33348
33457
|
}
|
|
33349
33458
|
`;
|
|
33350
33459
|
}
|
|
33351
|
-
function bootstrapFile(
|
|
33352
|
-
return `/* ${
|
|
33460
|
+
function bootstrapFile(theme2) {
|
|
33461
|
+
return `/* ${theme2.title} theme \u2014 maps LoomWeaver's --lw-* design tokens onto Bootstrap 5.3's --bs-*.
|
|
33353
33462
|
Import it AFTER the shell theme in your distribution styles.css, and import Bootstrap itself
|
|
33354
33463
|
INTO A LAYER \u2014 unlayered CSS outranks layered CSS whatever its specificity, so Bootstrap's
|
|
33355
33464
|
Reboot (button { border-radius: 0 }) would otherwise beat our .lw-* component classes:
|
|
33356
33465
|
@layer vendor;
|
|
33357
33466
|
@import 'bootstrap/dist/css/bootstrap.css' layer(vendor);
|
|
33358
33467
|
@import '@loomweaver/shell/styles/shell.css'; (or styles/theme.css if you run Tailwind)
|
|
33359
|
-
@import './themes/${
|
|
33468
|
+
@import './themes/${theme2.name}.css';
|
|
33360
33469
|
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
33361
33470
|
(Product default < Plugin < Tenant).
|
|
33362
33471
|
|
|
@@ -33431,9 +33540,9 @@ function bootstrapFile(t) {
|
|
|
33431
33540
|
var theme = {
|
|
33432
33541
|
id: "theme",
|
|
33433
33542
|
build(input) {
|
|
33434
|
-
const
|
|
33435
|
-
const content =
|
|
33436
|
-
return { [`${
|
|
33543
|
+
const theme2 = resolveThemeInput(input);
|
|
33544
|
+
const content = theme2.preset === "bootstrap" ? bootstrapFile(theme2) : cssFile(theme2);
|
|
33545
|
+
return { [`${theme2.name}.css`]: content };
|
|
33437
33546
|
}
|
|
33438
33547
|
};
|
|
33439
33548
|
|
|
@@ -33445,13 +33554,13 @@ function resolveLayoutInput(input) {
|
|
|
33445
33554
|
}
|
|
33446
33555
|
return { name, propertyName: toCamelCase(name) };
|
|
33447
33556
|
}
|
|
33448
|
-
function moduleFile3(
|
|
33449
|
-
return `// A base layout for the distribution. Pass it to provideLayout(${
|
|
33450
|
-
// in src/app/app.config.ts. Region ids are what contributions target \u2014 '
|
|
33557
|
+
function moduleFile3(layout2) {
|
|
33558
|
+
return `// A base layout for the distribution. Pass it to provideLayout(${layout2.propertyName}Layout)
|
|
33559
|
+
// in src/app/app.config.ts. Region ids are what contributions target \u2014 '${RAIL_REGION}' (rail) and '${STATUS_BAR_REGION}' (bar) match
|
|
33451
33560
|
// the devkit weaver defaults, so a scaffolded weaver's rail + bar items land here.
|
|
33452
33561
|
import { ShellLayout } from '@loomweaver/shell';
|
|
33453
33562
|
|
|
33454
|
-
export const ${
|
|
33563
|
+
export const ${layout2.propertyName}Layout: ShellLayout = {
|
|
33455
33564
|
regions: [
|
|
33456
33565
|
${renderRegions(" ".repeat(4))}
|
|
33457
33566
|
],
|
|
@@ -33461,19 +33570,70 @@ ${renderRegions(" ".repeat(4))}
|
|
|
33461
33570
|
var layout = {
|
|
33462
33571
|
id: "layout",
|
|
33463
33572
|
build(input) {
|
|
33464
|
-
const
|
|
33465
|
-
return { [`${
|
|
33573
|
+
const layout2 = resolveLayoutInput(input);
|
|
33574
|
+
return { [`${layout2.name}-layout.ts`]: moduleFile3(layout2) };
|
|
33466
33575
|
}
|
|
33467
33576
|
};
|
|
33468
33577
|
|
|
33578
|
+
// ../devkit/src/recipes/angular-distribution/scaffold.ts
|
|
33579
|
+
function distributionInput(values) {
|
|
33580
|
+
return {
|
|
33581
|
+
name: stringValue(values, "name") ?? "",
|
|
33582
|
+
title: stringValue(values, "title"),
|
|
33583
|
+
directory: stringValue(values, "directory"),
|
|
33584
|
+
styles: stringValue(values, "styles") ?? "tailwind"
|
|
33585
|
+
};
|
|
33586
|
+
}
|
|
33587
|
+
var distributionScaffold = {
|
|
33588
|
+
name: "distribution",
|
|
33589
|
+
summary: "a runnable composition root that boots the shell",
|
|
33590
|
+
options: [
|
|
33591
|
+
{
|
|
33592
|
+
name: "name",
|
|
33593
|
+
type: "string",
|
|
33594
|
+
description: "Distribution name in kebab-case, e.g. 'acme-studio'.",
|
|
33595
|
+
required: true,
|
|
33596
|
+
pattern: KEBAB_ID_PATTERN
|
|
33597
|
+
},
|
|
33598
|
+
{
|
|
33599
|
+
name: "title",
|
|
33600
|
+
type: "string",
|
|
33601
|
+
description: "Product display title. Defaults to a title-cased name."
|
|
33602
|
+
},
|
|
33603
|
+
{
|
|
33604
|
+
name: "styles",
|
|
33605
|
+
type: "string",
|
|
33606
|
+
description: "Which stylesheet to emit. 'tailwind' compiles the shell's source theme and lets you write Tailwind utilities of your own; 'precompiled' imports the stylesheet we compiled, so the application needs no Tailwind and can be themed with Bootstrap or anything else.",
|
|
33607
|
+
choices: DISTRIBUTION_STYLES,
|
|
33608
|
+
default: "tailwind"
|
|
33609
|
+
},
|
|
33610
|
+
{
|
|
33611
|
+
name: "force",
|
|
33612
|
+
type: "boolean",
|
|
33613
|
+
description: "Compose into the application already at that path, replacing the bootstrap files this scaffold owns and merging its build targets. Without it an existing project is an error.",
|
|
33614
|
+
workspaceOnly: true
|
|
33615
|
+
},
|
|
33616
|
+
{
|
|
33617
|
+
name: "prefix",
|
|
33618
|
+
type: "string",
|
|
33619
|
+
description: "Selector prefix the application declares for its components, which a weaver generated into it takes. Defaults to 'app'.",
|
|
33620
|
+
pattern: KEBAB_ID_PATTERN,
|
|
33621
|
+
workspaceOnly: true
|
|
33622
|
+
},
|
|
33623
|
+
...PLACEMENT_OPTIONS
|
|
33624
|
+
],
|
|
33625
|
+
build: (values) => generate(angularDistribution, distributionInput(values)),
|
|
33626
|
+
amend: (values) => amendments(angularDistribution, distributionInput(values))
|
|
33627
|
+
};
|
|
33628
|
+
|
|
33469
33629
|
// ../devkit/src/recipes/angular-weaver/amendments.ts
|
|
33470
33630
|
function weaverAmendments(input, where) {
|
|
33471
|
-
const
|
|
33472
|
-
const packages =
|
|
33631
|
+
const weaver = resolveWeaverInput(input);
|
|
33632
|
+
const packages = weaver.features.agent ? [
|
|
33473
33633
|
{
|
|
33474
33634
|
kind: "package",
|
|
33475
33635
|
name: "@loomweaver/ag-ui",
|
|
33476
|
-
version: `^${
|
|
33636
|
+
version: `^${PLATFORM_VERSION}`
|
|
33477
33637
|
},
|
|
33478
33638
|
{
|
|
33479
33639
|
kind: "package",
|
|
@@ -33495,25 +33655,151 @@ function weaverAmendments(input, where) {
|
|
|
33495
33655
|
glob: "**/*.json",
|
|
33496
33656
|
input: joinProjectPath(directory, "src/lib/i18n"),
|
|
33497
33657
|
from: "workspace",
|
|
33498
|
-
output: `i18n/${
|
|
33658
|
+
output: `i18n/${weaver.id}`
|
|
33499
33659
|
}
|
|
33500
33660
|
]
|
|
33501
33661
|
},
|
|
33502
33662
|
{ kind: "stylesheet-source", sourceRoot: joinProjectPath(directory, "src") },
|
|
33503
33663
|
{
|
|
33504
33664
|
kind: "compose-plugin",
|
|
33505
|
-
id:
|
|
33506
|
-
symbol: `${
|
|
33507
|
-
capabilities:
|
|
33665
|
+
id: weaver.id,
|
|
33666
|
+
symbol: `${weaver.propertyName}Plugin`,
|
|
33667
|
+
capabilities: weaver.capabilities,
|
|
33508
33668
|
sourceRoot: joinProjectPath(directory, "src")
|
|
33509
33669
|
}
|
|
33510
33670
|
];
|
|
33511
33671
|
}
|
|
33512
33672
|
|
|
33513
|
-
// ../devkit/src/recipes/
|
|
33673
|
+
// ../devkit/src/recipes/angular-weaver/scaffold.ts
|
|
33674
|
+
function weaverInput(values) {
|
|
33675
|
+
return {
|
|
33676
|
+
id: stringValue(values, "id") ?? "",
|
|
33677
|
+
name: stringValue(values, "name"),
|
|
33678
|
+
prefix: stringValue(values, "prefix"),
|
|
33679
|
+
importPath: stringValue(values, "importPath"),
|
|
33680
|
+
features: {
|
|
33681
|
+
command: booleanValue(values, "command"),
|
|
33682
|
+
shortcut: stringValue(values, "shortcut"),
|
|
33683
|
+
menu: stringValue(values, "menu"),
|
|
33684
|
+
barItem: booleanValue(values, "barItem"),
|
|
33685
|
+
settings: booleanValue(values, "settings"),
|
|
33686
|
+
about: booleanValue(values, "about"),
|
|
33687
|
+
instanceable: booleanValue(values, "instanceable"),
|
|
33688
|
+
container: booleanValue(values, "container"),
|
|
33689
|
+
agent: booleanValue(values, "agent"),
|
|
33690
|
+
access: stringValue(values, "access"),
|
|
33691
|
+
spec: booleanValue(values, "spec")
|
|
33692
|
+
}
|
|
33693
|
+
};
|
|
33694
|
+
}
|
|
33695
|
+
var weaverScaffold = {
|
|
33696
|
+
name: "weaver",
|
|
33697
|
+
summary: "an Angular in-process plugin: manifest, surface, rail item, i18n, test",
|
|
33698
|
+
options: [
|
|
33699
|
+
{
|
|
33700
|
+
name: "id",
|
|
33701
|
+
type: "string",
|
|
33702
|
+
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33703
|
+
required: true,
|
|
33704
|
+
pattern: KEBAB_ID_PATTERN
|
|
33705
|
+
},
|
|
33706
|
+
{
|
|
33707
|
+
name: "name",
|
|
33708
|
+
type: "string",
|
|
33709
|
+
description: "Human-readable name. Defaults to a title-cased id."
|
|
33710
|
+
},
|
|
33711
|
+
{
|
|
33712
|
+
name: "command",
|
|
33713
|
+
type: "boolean",
|
|
33714
|
+
description: "Also scaffold a registered command the weaver can trigger.",
|
|
33715
|
+
default: false
|
|
33716
|
+
},
|
|
33717
|
+
{
|
|
33718
|
+
name: "shortcut",
|
|
33719
|
+
type: "string",
|
|
33720
|
+
description: "Keyboard chord for the command, e.g. 'mod+shift+k'. Implies --command."
|
|
33721
|
+
},
|
|
33722
|
+
{
|
|
33723
|
+
name: "menu",
|
|
33724
|
+
type: "string",
|
|
33725
|
+
description: "Hook a menu item into a slot, e.g. 'content/tab/context'. Implies --command."
|
|
33726
|
+
},
|
|
33727
|
+
{
|
|
33728
|
+
name: "barItem",
|
|
33729
|
+
type: "boolean",
|
|
33730
|
+
description: "Also add a status-bar button that triggers the command. Implies --command.",
|
|
33731
|
+
default: false
|
|
33732
|
+
},
|
|
33733
|
+
{
|
|
33734
|
+
name: "settings",
|
|
33735
|
+
type: "boolean",
|
|
33736
|
+
description: "Also scaffold a settings section with signal-backed value owners.",
|
|
33737
|
+
default: false
|
|
33738
|
+
},
|
|
33739
|
+
{
|
|
33740
|
+
name: "about",
|
|
33741
|
+
type: "boolean",
|
|
33742
|
+
description: "Also scaffold an About dialog that reads ctx.host, plus a bottom rail item.",
|
|
33743
|
+
default: false
|
|
33744
|
+
},
|
|
33745
|
+
{
|
|
33746
|
+
name: "instanceable",
|
|
33747
|
+
type: "boolean",
|
|
33748
|
+
description: "Dock the surface and give it named saved instances, each with its own view state. Not combinable with --container: named instances exist only for a docked, non-routable surface.",
|
|
33749
|
+
default: false
|
|
33750
|
+
},
|
|
33751
|
+
{
|
|
33752
|
+
name: "container",
|
|
33753
|
+
type: "boolean",
|
|
33754
|
+
description: "Make the surface a container: a routable tab at '<id>/:id' holding a nested pane tree of child surfaces, which the host draws. Not combinable with --instanceable.",
|
|
33755
|
+
default: false
|
|
33756
|
+
},
|
|
33757
|
+
{
|
|
33758
|
+
name: "agent",
|
|
33759
|
+
type: "boolean",
|
|
33760
|
+
description: "Also scaffold the connection that lets an AG-UI agent run the commands this workbench offers: a docked panel, the seam where a call is decided before it runs, and a local stand-in that speaks the protocol so the whole path works on the first serve. Implies --command.",
|
|
33761
|
+
default: false
|
|
33762
|
+
},
|
|
33763
|
+
{
|
|
33764
|
+
name: "access",
|
|
33765
|
+
type: "string",
|
|
33766
|
+
description: "Auth-gate the surface and rail item: 'authenticated', 'anonymous' or 'role:<name>'."
|
|
33767
|
+
},
|
|
33768
|
+
{
|
|
33769
|
+
name: "spec",
|
|
33770
|
+
type: "boolean",
|
|
33771
|
+
description: "Generate a starter unit test. Use --no-spec to skip it.",
|
|
33772
|
+
default: true
|
|
33773
|
+
},
|
|
33774
|
+
{
|
|
33775
|
+
name: "projectName",
|
|
33776
|
+
type: "string",
|
|
33777
|
+
description: "Nx project name. Defaults to '<id>-weaver'.",
|
|
33778
|
+
workspaceOnly: true
|
|
33779
|
+
},
|
|
33780
|
+
{
|
|
33781
|
+
name: "importPath",
|
|
33782
|
+
type: "string",
|
|
33783
|
+
description: "Import path for the workspace alias. Defaults to the workspace scope plus the project name.",
|
|
33784
|
+
workspaceOnly: true
|
|
33785
|
+
},
|
|
33786
|
+
{
|
|
33787
|
+
name: "prefix",
|
|
33788
|
+
type: "string",
|
|
33789
|
+
description: "Selector prefix for the generated components: the one the application declares, as the prefix of its project in angular.json or project.json. Read from the workspace where it can be, else 'app'.",
|
|
33790
|
+
pattern: KEBAB_ID_PATTERN
|
|
33791
|
+
},
|
|
33792
|
+
APP_OPTION,
|
|
33793
|
+
...PLACEMENT_OPTIONS
|
|
33794
|
+
],
|
|
33795
|
+
build: (values) => generate(angularWeaver, weaverInput(values)),
|
|
33796
|
+
amend: (values) => weaverAmendments(weaverInput(values), stringValue(values, "directory"))
|
|
33797
|
+
};
|
|
33798
|
+
|
|
33799
|
+
// ../devkit/src/recipes/auth-source/amendments.ts
|
|
33514
33800
|
function authSourceAmendments(input, where) {
|
|
33515
|
-
const
|
|
33516
|
-
if (
|
|
33801
|
+
const source = resolveAuthSourceInput(input);
|
|
33802
|
+
if (source.bare || where === void 0 || where === "") {
|
|
33517
33803
|
return [];
|
|
33518
33804
|
}
|
|
33519
33805
|
const directory = normalizeProjectRoot(where);
|
|
@@ -33533,14 +33819,14 @@ function authSourceAmendments(input, where) {
|
|
|
33533
33819
|
{
|
|
33534
33820
|
kind: "compose-plugin",
|
|
33535
33821
|
id: SESSION_PLUGIN_ID,
|
|
33536
|
-
symbol: sessionPluginSymbol(
|
|
33822
|
+
symbol: sessionPluginSymbol(source),
|
|
33537
33823
|
capabilities: ["contributions"],
|
|
33538
33824
|
sourceRoot: directory,
|
|
33539
33825
|
providers: [
|
|
33540
33826
|
{
|
|
33541
|
-
line: `provideAuthSource(() => ${
|
|
33827
|
+
line: `provideAuthSource(() => ${source.propertyName}AuthSource()),`,
|
|
33542
33828
|
shell: ["provideAuthSource"],
|
|
33543
|
-
own: [`${
|
|
33829
|
+
own: [`${source.propertyName}AuthSource`],
|
|
33544
33830
|
unless: "provideAuthSource("
|
|
33545
33831
|
},
|
|
33546
33832
|
{
|
|
@@ -33558,325 +33844,135 @@ function authSourceAmendments(input, where) {
|
|
|
33558
33844
|
];
|
|
33559
33845
|
}
|
|
33560
33846
|
|
|
33561
|
-
// ../devkit/src/
|
|
33562
|
-
function
|
|
33563
|
-
return {
|
|
33564
|
-
id: str(values, "id") ?? "",
|
|
33565
|
-
name: str(values, "name"),
|
|
33566
|
-
prefix: str(values, "prefix"),
|
|
33567
|
-
importPath: str(values, "importPath"),
|
|
33568
|
-
features: {
|
|
33569
|
-
command: bool(values, "command"),
|
|
33570
|
-
shortcut: str(values, "shortcut"),
|
|
33571
|
-
menu: str(values, "menu"),
|
|
33572
|
-
barItem: bool(values, "barItem"),
|
|
33573
|
-
settings: bool(values, "settings"),
|
|
33574
|
-
about: bool(values, "about"),
|
|
33575
|
-
instanceable: bool(values, "instanceable"),
|
|
33576
|
-
container: bool(values, "container"),
|
|
33577
|
-
agent: bool(values, "agent"),
|
|
33578
|
-
access: str(values, "access"),
|
|
33579
|
-
spec: bool(values, "spec")
|
|
33580
|
-
}
|
|
33581
|
-
};
|
|
33582
|
-
}
|
|
33583
|
-
function distributionInput(values) {
|
|
33584
|
-
return {
|
|
33585
|
-
name: str(values, "name") ?? "",
|
|
33586
|
-
title: str(values, "title"),
|
|
33587
|
-
directory: str(values, "directory"),
|
|
33588
|
-
styles: str(values, "styles") ?? "tailwind"
|
|
33589
|
-
};
|
|
33847
|
+
// ../devkit/src/recipes/auth-source/scaffold.ts
|
|
33848
|
+
function authSourceInput(values) {
|
|
33849
|
+
return { name: stringValue(values, "name") ?? "", bare: booleanValue(values, "bare") };
|
|
33590
33850
|
}
|
|
33851
|
+
var authSourceScaffold = {
|
|
33852
|
+
name: "auth-source",
|
|
33853
|
+
summary: "a stand-in session a user can operate: the AuthSource, the sign-in, switch and sign-out verbs in the rail, composed in",
|
|
33854
|
+
options: [
|
|
33855
|
+
{
|
|
33856
|
+
name: "name",
|
|
33857
|
+
type: "string",
|
|
33858
|
+
description: "Source name in kebab-case, e.g. 'dev'.",
|
|
33859
|
+
required: true,
|
|
33860
|
+
pattern: KEBAB_ID_PATTERN
|
|
33861
|
+
},
|
|
33862
|
+
{
|
|
33863
|
+
name: "bare",
|
|
33864
|
+
type: "boolean",
|
|
33865
|
+
description: "Write the AuthSource alone, without the verbs, for a product that maps a session of its own onto it.",
|
|
33866
|
+
default: false
|
|
33867
|
+
},
|
|
33868
|
+
APP_OPTION,
|
|
33869
|
+
{
|
|
33870
|
+
name: "directory",
|
|
33871
|
+
type: "string",
|
|
33872
|
+
description: "Where the files land, relative to the workspace root.",
|
|
33873
|
+
workspaceOnly: true
|
|
33874
|
+
}
|
|
33875
|
+
],
|
|
33876
|
+
build: (values) => generate(authSource, authSourceInput(values)),
|
|
33877
|
+
amend: (values) => authSourceAmendments(authSourceInput(values), stringValue(values, "directory"))
|
|
33878
|
+
};
|
|
33591
33879
|
|
|
33592
|
-
// ../devkit/src/
|
|
33593
|
-
|
|
33594
|
-
|
|
33595
|
-
|
|
33596
|
-
|
|
33597
|
-
|
|
33598
|
-
|
|
33599
|
-
|
|
33600
|
-
|
|
33601
|
-
|
|
33602
|
-
|
|
33603
|
-
|
|
33604
|
-
|
|
33605
|
-
|
|
33606
|
-
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33614
|
-
}
|
|
33615
|
-
{
|
|
33616
|
-
name: "prefix",
|
|
33617
|
-
type: "string",
|
|
33618
|
-
description: "Selector prefix for generated components and directives.",
|
|
33619
|
-
default: "lw",
|
|
33620
|
-
pattern: ID_PATTERN,
|
|
33621
|
-
workspaceOnly: true
|
|
33622
|
-
},
|
|
33623
|
-
{
|
|
33624
|
-
name: "unitTestRunner",
|
|
33625
|
-
type: "string",
|
|
33626
|
-
description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
|
|
33627
|
-
choices: ["vitest", "none"],
|
|
33628
|
-
default: "vitest",
|
|
33629
|
-
workspaceOnly: true
|
|
33630
|
-
}
|
|
33631
|
-
];
|
|
33632
|
-
var APP_OPTION = {
|
|
33633
|
-
name: "app",
|
|
33634
|
-
type: "string",
|
|
33635
|
-
description: "Application to drop into. Inferred when the workspace has exactly one.",
|
|
33636
|
-
workspaceOnly: true
|
|
33880
|
+
// ../devkit/src/recipes/frame-plugin/scaffold.ts
|
|
33881
|
+
var framePluginScaffold = {
|
|
33882
|
+
name: "frame-plugin",
|
|
33883
|
+
summary: "a framework-agnostic iframe plugin (Penpal + the frame UI kit)",
|
|
33884
|
+
options: [
|
|
33885
|
+
{
|
|
33886
|
+
name: "id",
|
|
33887
|
+
type: "string",
|
|
33888
|
+
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33889
|
+
required: true,
|
|
33890
|
+
pattern: KEBAB_ID_PATTERN
|
|
33891
|
+
},
|
|
33892
|
+
{
|
|
33893
|
+
name: "name",
|
|
33894
|
+
type: "string",
|
|
33895
|
+
description: "Human-readable name. Defaults to a title-cased id."
|
|
33896
|
+
},
|
|
33897
|
+
APP_OPTION
|
|
33898
|
+
],
|
|
33899
|
+
build: (values) => generate(framePlugin, {
|
|
33900
|
+
id: stringValue(values, "id") ?? "",
|
|
33901
|
+
name: stringValue(values, "name")
|
|
33902
|
+
})
|
|
33637
33903
|
};
|
|
33638
|
-
|
|
33639
|
-
|
|
33640
|
-
|
|
33904
|
+
|
|
33905
|
+
// ../devkit/src/recipes/layout/scaffold.ts
|
|
33906
|
+
var layoutScaffold = {
|
|
33907
|
+
name: "layout",
|
|
33908
|
+
summary: "a ShellLayout with the regions a weaver expects",
|
|
33909
|
+
options: [
|
|
33910
|
+
{
|
|
33911
|
+
name: "name",
|
|
33912
|
+
type: "string",
|
|
33913
|
+
description: "Layout name. Defaults to a base layout."
|
|
33914
|
+
},
|
|
33915
|
+
APP_OPTION
|
|
33916
|
+
],
|
|
33917
|
+
build: (values) => generate(layout, { name: stringValue(values, "name") })
|
|
33918
|
+
};
|
|
33919
|
+
|
|
33920
|
+
// ../devkit/src/recipes/settings-store/scaffold.ts
|
|
33921
|
+
var settingsStoreScaffold = {
|
|
33922
|
+
name: "settings-store",
|
|
33923
|
+
summary: "a settings-store implementation backed by your API",
|
|
33924
|
+
options: [
|
|
33925
|
+
{
|
|
33926
|
+
name: "name",
|
|
33927
|
+
type: "string",
|
|
33928
|
+
description: "Store name in kebab-case, e.g. 'backend'.",
|
|
33929
|
+
required: true,
|
|
33930
|
+
pattern: KEBAB_ID_PATTERN
|
|
33931
|
+
},
|
|
33932
|
+
APP_OPTION
|
|
33933
|
+
],
|
|
33934
|
+
build: (values) => generate(settingsStore, { name: stringValue(values, "name") ?? "" })
|
|
33935
|
+
};
|
|
33936
|
+
|
|
33937
|
+
// ../devkit/src/recipes/theme/scaffold.ts
|
|
33938
|
+
var themeScaffold = {
|
|
33939
|
+
name: "theme",
|
|
33940
|
+
summary: "a token-override stylesheet in @layer lw-tenant-theme",
|
|
33941
|
+
options: [
|
|
33942
|
+
{
|
|
33943
|
+
name: "name",
|
|
33944
|
+
type: "string",
|
|
33945
|
+
description: "Theme name in kebab-case, e.g. 'midnight'.",
|
|
33946
|
+
required: true,
|
|
33947
|
+
pattern: KEBAB_ID_PATTERN
|
|
33948
|
+
},
|
|
33949
|
+
{
|
|
33950
|
+
name: "preset",
|
|
33951
|
+
type: "string",
|
|
33952
|
+
description: "Where the token values come from. 'literal' writes editable colours; 'bootstrap' maps them onto Bootstrap 5.3's --bs-* variables, which makes the shell follow your Bootstrap theme live.",
|
|
33953
|
+
choices: THEME_PRESETS,
|
|
33954
|
+
default: "literal"
|
|
33955
|
+
},
|
|
33956
|
+
APP_OPTION
|
|
33957
|
+
],
|
|
33958
|
+
build: (values) => generate(theme, {
|
|
33959
|
+
name: stringValue(values, "name") ?? "",
|
|
33960
|
+
preset: stringValue(values, "preset") ?? "literal"
|
|
33961
|
+
})
|
|
33962
|
+
};
|
|
33963
|
+
|
|
33964
|
+
// ../devkit/src/lib/scaffolds/scaffolds.ts
|
|
33641
33965
|
var SCAFFOLDS = [
|
|
33642
|
-
|
|
33643
|
-
|
|
33644
|
-
|
|
33645
|
-
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33650
|
-
required: true,
|
|
33651
|
-
pattern: ID_PATTERN
|
|
33652
|
-
},
|
|
33653
|
-
{
|
|
33654
|
-
name: "name",
|
|
33655
|
-
type: "string",
|
|
33656
|
-
description: "Human-readable name. Defaults to a title-cased id."
|
|
33657
|
-
},
|
|
33658
|
-
{
|
|
33659
|
-
name: "command",
|
|
33660
|
-
type: "boolean",
|
|
33661
|
-
description: "Also scaffold a registered command the weaver can trigger.",
|
|
33662
|
-
default: false
|
|
33663
|
-
},
|
|
33664
|
-
{
|
|
33665
|
-
name: "shortcut",
|
|
33666
|
-
type: "string",
|
|
33667
|
-
description: "Keyboard chord for the command, e.g. 'mod+shift+k'. Implies --command."
|
|
33668
|
-
},
|
|
33669
|
-
{
|
|
33670
|
-
name: "menu",
|
|
33671
|
-
type: "string",
|
|
33672
|
-
description: "Hook a menu item into a slot, e.g. 'content/tab/context'. Implies --command."
|
|
33673
|
-
},
|
|
33674
|
-
{
|
|
33675
|
-
name: "barItem",
|
|
33676
|
-
type: "boolean",
|
|
33677
|
-
description: "Also add a status-bar button that triggers the command. Implies --command.",
|
|
33678
|
-
default: false
|
|
33679
|
-
},
|
|
33680
|
-
{
|
|
33681
|
-
name: "settings",
|
|
33682
|
-
type: "boolean",
|
|
33683
|
-
description: "Also scaffold a settings section with signal-backed value owners.",
|
|
33684
|
-
default: false
|
|
33685
|
-
},
|
|
33686
|
-
{
|
|
33687
|
-
name: "about",
|
|
33688
|
-
type: "boolean",
|
|
33689
|
-
description: "Also scaffold an About dialog that reads ctx.host, plus a bottom rail item.",
|
|
33690
|
-
default: false
|
|
33691
|
-
},
|
|
33692
|
-
{
|
|
33693
|
-
name: "instanceable",
|
|
33694
|
-
type: "boolean",
|
|
33695
|
-
description: "Dock the surface and give it named saved instances, each with its own view state. Not combinable with --container: named instances exist only for a docked, non-routable surface.",
|
|
33696
|
-
default: false
|
|
33697
|
-
},
|
|
33698
|
-
{
|
|
33699
|
-
name: "container",
|
|
33700
|
-
type: "boolean",
|
|
33701
|
-
description: "Make the surface a container: a routable tab at '<id>/:id' holding a nested pane tree of child surfaces, which the host draws. Not combinable with --instanceable.",
|
|
33702
|
-
default: false
|
|
33703
|
-
},
|
|
33704
|
-
{
|
|
33705
|
-
name: "agent",
|
|
33706
|
-
type: "boolean",
|
|
33707
|
-
description: "Also scaffold the connection that lets an AG-UI agent run the commands this workbench offers: a docked panel, the seam where a call is decided before it runs, and a local stand-in that speaks the protocol so the whole path works on the first serve. Implies --command.",
|
|
33708
|
-
default: false
|
|
33709
|
-
},
|
|
33710
|
-
{
|
|
33711
|
-
name: "access",
|
|
33712
|
-
type: "string",
|
|
33713
|
-
description: "Auth-gate the surface and rail item: 'authenticated', 'anonymous' or 'role:<name>'."
|
|
33714
|
-
},
|
|
33715
|
-
{
|
|
33716
|
-
name: "spec",
|
|
33717
|
-
type: "boolean",
|
|
33718
|
-
description: "Generate a starter unit test. Use --no-spec to skip it.",
|
|
33719
|
-
default: true
|
|
33720
|
-
},
|
|
33721
|
-
{
|
|
33722
|
-
name: "projectName",
|
|
33723
|
-
type: "string",
|
|
33724
|
-
description: "Nx project name. Defaults to '<id>-weaver'.",
|
|
33725
|
-
workspaceOnly: true
|
|
33726
|
-
},
|
|
33727
|
-
{
|
|
33728
|
-
name: "importPath",
|
|
33729
|
-
type: "string",
|
|
33730
|
-
description: "Import path for the workspace alias. Defaults to the workspace scope plus the project name.",
|
|
33731
|
-
workspaceOnly: true
|
|
33732
|
-
},
|
|
33733
|
-
APP_OPTION,
|
|
33734
|
-
...PLACEMENT_OPTIONS
|
|
33735
|
-
],
|
|
33736
|
-
build: (values) => generate(angularWeaver, weaverInput(values)),
|
|
33737
|
-
amend: (values) => weaverAmendments(weaverInput(values), str(values, "directory"))
|
|
33738
|
-
},
|
|
33739
|
-
{
|
|
33740
|
-
name: "frame-plugin",
|
|
33741
|
-
summary: "a framework-agnostic iframe plugin (Penpal + the frame UI kit)",
|
|
33742
|
-
options: [
|
|
33743
|
-
{
|
|
33744
|
-
name: "id",
|
|
33745
|
-
type: "string",
|
|
33746
|
-
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33747
|
-
required: true,
|
|
33748
|
-
pattern: ID_PATTERN
|
|
33749
|
-
},
|
|
33750
|
-
{
|
|
33751
|
-
name: "name",
|
|
33752
|
-
type: "string",
|
|
33753
|
-
description: "Human-readable name. Defaults to a title-cased id."
|
|
33754
|
-
},
|
|
33755
|
-
APP_OPTION
|
|
33756
|
-
],
|
|
33757
|
-
build: (values) => generate(framePlugin, {
|
|
33758
|
-
id: str(values, "id") ?? "",
|
|
33759
|
-
name: str(values, "name")
|
|
33760
|
-
})
|
|
33761
|
-
},
|
|
33762
|
-
{
|
|
33763
|
-
name: "distribution",
|
|
33764
|
-
summary: "a runnable composition root that boots the shell",
|
|
33765
|
-
options: [
|
|
33766
|
-
{
|
|
33767
|
-
name: "name",
|
|
33768
|
-
type: "string",
|
|
33769
|
-
description: "Distribution name in kebab-case, e.g. 'acme-studio'.",
|
|
33770
|
-
required: true,
|
|
33771
|
-
pattern: ID_PATTERN
|
|
33772
|
-
},
|
|
33773
|
-
{
|
|
33774
|
-
name: "title",
|
|
33775
|
-
type: "string",
|
|
33776
|
-
description: "Product display title. Defaults to a title-cased name."
|
|
33777
|
-
},
|
|
33778
|
-
{
|
|
33779
|
-
name: "styles",
|
|
33780
|
-
type: "string",
|
|
33781
|
-
description: "Which stylesheet to emit. 'tailwind' compiles the shell's source theme and lets you write Tailwind utilities of your own; 'precompiled' imports the stylesheet we compiled, so the application needs no Tailwind and can be themed with Bootstrap or anything else.",
|
|
33782
|
-
choices: ["tailwind", "precompiled"],
|
|
33783
|
-
default: "tailwind"
|
|
33784
|
-
},
|
|
33785
|
-
{
|
|
33786
|
-
name: "force",
|
|
33787
|
-
type: "boolean",
|
|
33788
|
-
description: "Compose into the application already at that path, replacing the bootstrap files this scaffold owns and merging its build targets. Without it an existing project is an error.",
|
|
33789
|
-
workspaceOnly: true
|
|
33790
|
-
},
|
|
33791
|
-
...PLACEMENT_OPTIONS
|
|
33792
|
-
],
|
|
33793
|
-
build: (values) => generate(angularDistribution, distributionInput(values)),
|
|
33794
|
-
amend: (values) => amendments(angularDistribution, distributionInput(values))
|
|
33795
|
-
},
|
|
33796
|
-
{
|
|
33797
|
-
name: "auth-source",
|
|
33798
|
-
summary: "a stand-in session a user can operate: the AuthSource, the sign-in, switch and sign-out verbs in the rail, composed in",
|
|
33799
|
-
options: [
|
|
33800
|
-
{
|
|
33801
|
-
name: "name",
|
|
33802
|
-
type: "string",
|
|
33803
|
-
description: "Source name in kebab-case, e.g. 'dev'.",
|
|
33804
|
-
required: true,
|
|
33805
|
-
pattern: ID_PATTERN
|
|
33806
|
-
},
|
|
33807
|
-
{
|
|
33808
|
-
name: "bare",
|
|
33809
|
-
type: "boolean",
|
|
33810
|
-
description: "Write the AuthSource alone, without the verbs, for a product that maps a session of its own onto it.",
|
|
33811
|
-
default: false
|
|
33812
|
-
},
|
|
33813
|
-
APP_OPTION,
|
|
33814
|
-
{
|
|
33815
|
-
name: "directory",
|
|
33816
|
-
type: "string",
|
|
33817
|
-
description: "Where the files land, relative to the workspace root.",
|
|
33818
|
-
workspaceOnly: true
|
|
33819
|
-
}
|
|
33820
|
-
],
|
|
33821
|
-
build: (values) => generate(authSource, authSourceInput(values)),
|
|
33822
|
-
amend: (values) => authSourceAmendments(authSourceInput(values), str(values, "directory"))
|
|
33823
|
-
},
|
|
33824
|
-
{
|
|
33825
|
-
name: "settings-store",
|
|
33826
|
-
summary: "a settings-store implementation backed by your API",
|
|
33827
|
-
options: [
|
|
33828
|
-
{
|
|
33829
|
-
name: "name",
|
|
33830
|
-
type: "string",
|
|
33831
|
-
description: "Store name in kebab-case, e.g. 'backend'.",
|
|
33832
|
-
required: true,
|
|
33833
|
-
pattern: ID_PATTERN
|
|
33834
|
-
},
|
|
33835
|
-
APP_OPTION
|
|
33836
|
-
],
|
|
33837
|
-
build: (values) => generate(settingsStore, { name: str(values, "name") ?? "" })
|
|
33838
|
-
},
|
|
33839
|
-
{
|
|
33840
|
-
name: "theme",
|
|
33841
|
-
summary: "a token-override stylesheet in @layer lw-tenant-theme",
|
|
33842
|
-
options: [
|
|
33843
|
-
{
|
|
33844
|
-
name: "name",
|
|
33845
|
-
type: "string",
|
|
33846
|
-
description: "Theme name in kebab-case, e.g. 'midnight'.",
|
|
33847
|
-
required: true,
|
|
33848
|
-
pattern: ID_PATTERN
|
|
33849
|
-
},
|
|
33850
|
-
{
|
|
33851
|
-
name: "preset",
|
|
33852
|
-
type: "string",
|
|
33853
|
-
description: "Where the token values come from. 'literal' writes editable colours; 'bootstrap' maps them onto Bootstrap 5.3's --bs-* variables, which makes the shell follow your Bootstrap theme live.",
|
|
33854
|
-
choices: ["literal", "bootstrap"],
|
|
33855
|
-
default: "literal"
|
|
33856
|
-
},
|
|
33857
|
-
APP_OPTION
|
|
33858
|
-
],
|
|
33859
|
-
build: (values) => generate(theme, {
|
|
33860
|
-
name: str(values, "name") ?? "",
|
|
33861
|
-
preset: str(values, "preset") ?? "literal"
|
|
33862
|
-
})
|
|
33863
|
-
},
|
|
33864
|
-
{
|
|
33865
|
-
name: "layout",
|
|
33866
|
-
summary: "a ShellLayout with the regions a weaver expects",
|
|
33867
|
-
options: [
|
|
33868
|
-
{
|
|
33869
|
-
name: "name",
|
|
33870
|
-
type: "string",
|
|
33871
|
-
description: "Layout name. Defaults to a base layout."
|
|
33872
|
-
},
|
|
33873
|
-
APP_OPTION
|
|
33874
|
-
],
|
|
33875
|
-
build: (values) => generate(layout, { name: str(values, "name") })
|
|
33876
|
-
}
|
|
33966
|
+
weaverScaffold,
|
|
33967
|
+
framePluginScaffold,
|
|
33968
|
+
distributionScaffold,
|
|
33969
|
+
authSourceScaffold,
|
|
33970
|
+
settingsStoreScaffold,
|
|
33971
|
+
themeScaffold,
|
|
33972
|
+
layoutScaffold
|
|
33877
33973
|
];
|
|
33878
33974
|
|
|
33879
|
-
// ../devkit/src/lib/scaffolds/
|
|
33975
|
+
// ../devkit/src/lib/scaffolds/adapter-options.ts
|
|
33880
33976
|
function portableOptions(scaffold2) {
|
|
33881
33977
|
return scaffold2.options.filter((option) => !option.workspaceOnly);
|
|
33882
33978
|
}
|
|
@@ -33938,41 +34034,50 @@ var CATALOG_ENTRY_KEYS = [
|
|
|
33938
34034
|
"readmeUrl"
|
|
33939
34035
|
];
|
|
33940
34036
|
var SAME_ORIGIN_FIELDS = ["entryUrl", "iconUrl", "readmeUrl"];
|
|
33941
|
-
function
|
|
34037
|
+
function pathOf(index, field) {
|
|
33942
34038
|
return field ? `catalog[${index}].${field}` : `catalog[${index}]`;
|
|
33943
34039
|
}
|
|
33944
34040
|
function isPlainObject3(raw) {
|
|
33945
34041
|
return typeof raw === "object" && raw !== null && !Array.isArray(raw);
|
|
33946
34042
|
}
|
|
33947
|
-
function
|
|
34043
|
+
function requiredUrlFinding(value, index, field) {
|
|
33948
34044
|
if (typeof value !== "string" || value.length === 0) {
|
|
33949
|
-
return
|
|
34045
|
+
return {
|
|
33950
34046
|
level: "error",
|
|
33951
34047
|
code: "catalog.entryUrl",
|
|
33952
|
-
message: `${
|
|
33953
|
-
path:
|
|
33954
|
-
}
|
|
34048
|
+
message: `${pathOf(index, field)} must be a non-empty string; the host drops the whole entry without it.`,
|
|
34049
|
+
path: pathOf(index, field)
|
|
34050
|
+
};
|
|
34051
|
+
}
|
|
34052
|
+
return schemeFinding(value, index, field, "drops the entry");
|
|
34053
|
+
}
|
|
34054
|
+
function optionalUrlFinding(value, index, field) {
|
|
34055
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
34056
|
+
return {
|
|
33955
34057
|
level: "warning",
|
|
33956
34058
|
code: "catalog.url.empty",
|
|
33957
|
-
message: `${
|
|
33958
|
-
path:
|
|
34059
|
+
message: `${pathOf(index, field)} is present but not a non-empty string, so the host ignores it.`,
|
|
34060
|
+
path: pathOf(index, field)
|
|
33959
34061
|
};
|
|
33960
34062
|
}
|
|
34063
|
+
return schemeFinding(value, index, field, "ignores the field");
|
|
34064
|
+
}
|
|
34065
|
+
function schemeFinding(value, index, field, refusal) {
|
|
33961
34066
|
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value)?.[1]?.toLowerCase();
|
|
33962
34067
|
if (scheme && scheme !== "http" && scheme !== "https") {
|
|
33963
34068
|
return {
|
|
33964
34069
|
level: "error",
|
|
33965
34070
|
code: "catalog.url.scheme",
|
|
33966
|
-
message: `${
|
|
33967
|
-
path:
|
|
34071
|
+
message: `${pathOf(index, field)} uses the "${scheme}:" scheme. The host accepts same-origin http(s) URLs only and ${refusal}.`,
|
|
34072
|
+
path: pathOf(index, field)
|
|
33968
34073
|
};
|
|
33969
34074
|
}
|
|
33970
34075
|
if (scheme) {
|
|
33971
34076
|
return {
|
|
33972
34077
|
level: "warning",
|
|
33973
34078
|
code: "catalog.url.absolute",
|
|
33974
|
-
message: `${
|
|
33975
|
-
path:
|
|
34079
|
+
message: `${pathOf(index, field)} is absolute. The host requires same-origin, so this holds only while it matches the origin the app is served from; a root-relative path is same-origin by construction.`,
|
|
34080
|
+
path: pathOf(index, field)
|
|
33976
34081
|
};
|
|
33977
34082
|
}
|
|
33978
34083
|
return void 0;
|
|
@@ -33983,8 +34088,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
33983
34088
|
{
|
|
33984
34089
|
level: "warning",
|
|
33985
34090
|
code: "catalog.capabilities.missing",
|
|
33986
|
-
message: `${
|
|
33987
|
-
path:
|
|
34091
|
+
message: `${pathOf(index)} declares no capabilities. Accepting the install dialog grants exactly what is declared, so the plugin will be denied everything at runtime.`,
|
|
34092
|
+
path: pathOf(index, "capabilities")
|
|
33988
34093
|
}
|
|
33989
34094
|
];
|
|
33990
34095
|
}
|
|
@@ -33993,8 +34098,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
33993
34098
|
{
|
|
33994
34099
|
level: "error",
|
|
33995
34100
|
code: "catalog.capabilities",
|
|
33996
|
-
message: `${
|
|
33997
|
-
path:
|
|
34101
|
+
message: `${pathOf(index, "capabilities")} must be an array; the host ignores any other shape and grants nothing.`,
|
|
34102
|
+
path: pathOf(index, "capabilities")
|
|
33998
34103
|
}
|
|
33999
34104
|
];
|
|
34000
34105
|
}
|
|
@@ -34004,8 +34109,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
34004
34109
|
findings.push({
|
|
34005
34110
|
level: "error",
|
|
34006
34111
|
code: "catalog.capability.unknown",
|
|
34007
|
-
message: `${
|
|
34008
|
-
path:
|
|
34112
|
+
message: `${pathOf(index, "capabilities")} contains ${JSON.stringify(capability)}, which the host filters out silently \u2014 the plugin then throws CapabilityError at runtime. Known: ${known.join(", ")}.`,
|
|
34113
|
+
path: pathOf(index, "capabilities")
|
|
34009
34114
|
});
|
|
34010
34115
|
}
|
|
34011
34116
|
}
|
|
@@ -34017,58 +34122,69 @@ function validateEntry(raw, index, known) {
|
|
|
34017
34122
|
{
|
|
34018
34123
|
level: "error",
|
|
34019
34124
|
code: "catalog.entry",
|
|
34020
|
-
message: `${
|
|
34021
|
-
path:
|
|
34125
|
+
message: `${pathOf(index)} is not an object, so the host drops it.`,
|
|
34126
|
+
path: pathOf(index)
|
|
34022
34127
|
}
|
|
34023
34128
|
];
|
|
34024
34129
|
}
|
|
34025
34130
|
return [
|
|
34026
|
-
...
|
|
34131
|
+
...validateId(raw, index),
|
|
34132
|
+
...validateSameOriginUrls(raw, index),
|
|
34133
|
+
...validateNameAndVersion(raw, index),
|
|
34027
34134
|
...validateCapabilities2(raw["capabilities"], index, known),
|
|
34028
34135
|
...validateEntryMetadata(raw, index),
|
|
34029
34136
|
...validateEntryKeys(raw, index)
|
|
34030
34137
|
];
|
|
34031
34138
|
}
|
|
34032
|
-
function
|
|
34033
|
-
|
|
34034
|
-
|
|
34035
|
-
findings.push({
|
|
34139
|
+
function validateId(raw, index) {
|
|
34140
|
+
return typeof raw["id"] === "string" && raw["id"].length > 0 ? [] : [
|
|
34141
|
+
{
|
|
34036
34142
|
level: "error",
|
|
34037
34143
|
code: "catalog.id",
|
|
34038
|
-
message: `${
|
|
34039
|
-
path:
|
|
34040
|
-
});
|
|
34041
|
-
}
|
|
34042
|
-
for (const field of SAME_ORIGIN_FIELDS) {
|
|
34043
|
-
if (field !== "entryUrl" && raw[field] === void 0) {
|
|
34044
|
-
continue;
|
|
34144
|
+
message: `${pathOf(index, "id")} must be a non-empty string; the host drops the whole entry without it.`,
|
|
34145
|
+
path: pathOf(index, "id")
|
|
34045
34146
|
}
|
|
34046
|
-
|
|
34147
|
+
];
|
|
34148
|
+
}
|
|
34149
|
+
function validateSameOriginUrls(raw, index) {
|
|
34150
|
+
const findings = [];
|
|
34151
|
+
for (const field of SAME_ORIGIN_FIELDS) {
|
|
34152
|
+
const finding = sameOriginUrlFinding(raw, index, field);
|
|
34047
34153
|
if (finding) {
|
|
34048
34154
|
findings.push(finding);
|
|
34049
34155
|
}
|
|
34050
34156
|
}
|
|
34157
|
+
return findings;
|
|
34158
|
+
}
|
|
34159
|
+
function sameOriginUrlFinding(raw, index, field) {
|
|
34160
|
+
if (field === "entryUrl") {
|
|
34161
|
+
return requiredUrlFinding(raw[field], index, field);
|
|
34162
|
+
}
|
|
34163
|
+
return raw[field] === void 0 ? void 0 : optionalUrlFinding(raw[field], index, field);
|
|
34164
|
+
}
|
|
34165
|
+
function validateNameAndVersion(raw, index) {
|
|
34166
|
+
const findings = [];
|
|
34051
34167
|
if (raw["name"] === void 0) {
|
|
34052
34168
|
findings.push({
|
|
34053
34169
|
level: "warning",
|
|
34054
34170
|
code: "catalog.name.missing",
|
|
34055
|
-
message: `${
|
|
34056
|
-
path:
|
|
34171
|
+
message: `${pathOf(index)} has no name, so the store falls back to showing the id.`,
|
|
34172
|
+
path: pathOf(index, "name")
|
|
34057
34173
|
});
|
|
34058
34174
|
} else if (typeof raw["name"] !== "string" || raw["name"].length === 0) {
|
|
34059
34175
|
findings.push({
|
|
34060
34176
|
level: "warning",
|
|
34061
34177
|
code: "catalog.name",
|
|
34062
|
-
message: `${
|
|
34063
|
-
path:
|
|
34178
|
+
message: `${pathOf(index, "name")} is not a non-empty string, so the store falls back to showing the id.`,
|
|
34179
|
+
path: pathOf(index, "name")
|
|
34064
34180
|
});
|
|
34065
34181
|
}
|
|
34066
34182
|
if (raw["version"] === void 0) {
|
|
34067
34183
|
findings.push({
|
|
34068
34184
|
level: "warning",
|
|
34069
34185
|
code: "catalog.version.missing",
|
|
34070
|
-
message: `${
|
|
34071
|
-
path:
|
|
34186
|
+
message: `${pathOf(index)} carries no version. Update detection compares catalog versions, so the store can never offer an update and republishing the plugin will not respawn it for anyone who already installed it.`,
|
|
34187
|
+
path: pathOf(index, "version")
|
|
34072
34188
|
});
|
|
34073
34189
|
}
|
|
34074
34190
|
return findings;
|
|
@@ -34079,24 +34195,24 @@ function validateEntryMetadata(raw, index) {
|
|
|
34079
34195
|
findings.push({
|
|
34080
34196
|
level: "warning",
|
|
34081
34197
|
code: "catalog.downloads",
|
|
34082
|
-
message: `${
|
|
34083
|
-
path:
|
|
34198
|
+
message: `${pathOf(index, "downloads")} must be a non-negative number, so the host ignores it.`,
|
|
34199
|
+
path: pathOf(index, "downloads")
|
|
34084
34200
|
});
|
|
34085
34201
|
}
|
|
34086
34202
|
if (raw["updated"] !== void 0 && !isRenderableDate(raw["updated"])) {
|
|
34087
34203
|
findings.push({
|
|
34088
34204
|
level: "warning",
|
|
34089
34205
|
code: "catalog.updated",
|
|
34090
|
-
message: `${
|
|
34091
|
-
path:
|
|
34206
|
+
message: `${pathOf(index, "updated")} is not a date the store can parse, so it renders the raw string instead of "2 days ago".`,
|
|
34207
|
+
path: pathOf(index, "updated")
|
|
34092
34208
|
});
|
|
34093
34209
|
}
|
|
34094
34210
|
if (raw["repository"] !== void 0 && !isHttpUrl(raw["repository"])) {
|
|
34095
34211
|
findings.push({
|
|
34096
34212
|
level: "warning",
|
|
34097
34213
|
code: "catalog.repository",
|
|
34098
|
-
message: `${
|
|
34099
|
-
path:
|
|
34214
|
+
message: `${pathOf(index, "repository")} must be an http(s) URL, so the host drops it and the detail pane shows no link.`,
|
|
34215
|
+
path: pathOf(index, "repository")
|
|
34100
34216
|
});
|
|
34101
34217
|
}
|
|
34102
34218
|
return findings;
|
|
@@ -34108,8 +34224,8 @@ function validateEntryKeys(raw, index) {
|
|
|
34108
34224
|
findings.push({
|
|
34109
34225
|
level: "warning",
|
|
34110
34226
|
code: "catalog.unknown-key",
|
|
34111
|
-
message: `${
|
|
34112
|
-
path:
|
|
34227
|
+
message: `${pathOf(index, key)} is not one of the fields the host reads (${CATALOG_ENTRY_KEYS.join(", ")}), so it is ignored without a word \u2014 which is exactly what a misspelled field looks like.`,
|
|
34228
|
+
path: pathOf(index, key)
|
|
34113
34229
|
});
|
|
34114
34230
|
}
|
|
34115
34231
|
}
|
|
@@ -34150,8 +34266,8 @@ function validateCatalog(catalog, known = KNOWN_CAPABILITIES) {
|
|
|
34150
34266
|
findings.push({
|
|
34151
34267
|
level: "warning",
|
|
34152
34268
|
code: "catalog.id.duplicate",
|
|
34153
|
-
message: `${
|
|
34154
|
-
path:
|
|
34269
|
+
message: `${pathOf(index, "id")} repeats "${id}". The host keeps the first entry with an id and drops the rest.`,
|
|
34270
|
+
path: pathOf(index, "id")
|
|
34155
34271
|
});
|
|
34156
34272
|
}
|
|
34157
34273
|
seen.add(id);
|
|
@@ -34167,7 +34283,7 @@ function validateCommands(sources, ts) {
|
|
|
34167
34283
|
for (const source of sources) {
|
|
34168
34284
|
const file2 = ts.createSourceFile(source.path, source.text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
34169
34285
|
for (const call of registerCommandCalls(ts, file2)) {
|
|
34170
|
-
findings.push(...
|
|
34286
|
+
findings.push(...findingsFor(readRegistration(ts, file2, call)));
|
|
34171
34287
|
}
|
|
34172
34288
|
}
|
|
34173
34289
|
findings.push({ level: "info", code: "commands.runtime", message: RUNTIME_NOTE });
|
|
@@ -34184,11 +34300,11 @@ function registerCommandCalls(ts, file2) {
|
|
|
34184
34300
|
visit(file2);
|
|
34185
34301
|
return calls;
|
|
34186
34302
|
}
|
|
34187
|
-
function
|
|
34303
|
+
function readRegistration(ts, file2, call) {
|
|
34188
34304
|
const line = file2.getLineAndCharacterOfPosition(call.getStart(file2)).line + 1;
|
|
34189
|
-
const
|
|
34305
|
+
const at = `${file2.fileName}:${line}`;
|
|
34190
34306
|
const literal2 = call.arguments[0];
|
|
34191
|
-
const empty = { at
|
|
34307
|
+
const empty = { at, callable: false, described: false, answers: false, returnsValue: false, arguments: [] };
|
|
34192
34308
|
if (!literal2 || !ts.isObjectLiteralExpression(literal2)) {
|
|
34193
34309
|
return { ...empty, unreadable: "its argument is not an object literal" };
|
|
34194
34310
|
}
|
|
@@ -34210,7 +34326,7 @@ function read(ts, file2, call) {
|
|
|
34210
34326
|
const callable = properties.get("callable");
|
|
34211
34327
|
const consent = properties.get("agentConsent");
|
|
34212
34328
|
return {
|
|
34213
|
-
at
|
|
34329
|
+
at,
|
|
34214
34330
|
id: id.text,
|
|
34215
34331
|
callable: callable?.kind === ts.SyntaxKind.TrueKeyword,
|
|
34216
34332
|
described: properties.has("description"),
|
|
@@ -34230,25 +34346,27 @@ function unwrapped(ts, value) {
|
|
|
34230
34346
|
}
|
|
34231
34347
|
return ts.isAsExpression(value) || ts.isSatisfiesExpression(value) || ts.isParenthesizedExpression(value) ? unwrapped(ts, value.expression) : value;
|
|
34232
34348
|
}
|
|
34233
|
-
var
|
|
34349
|
+
var CONSENT_LINES = /* @__PURE__ */ new Map([
|
|
34350
|
+
["allow", "says an agent's word is enough to run it."],
|
|
34351
|
+
[
|
|
34352
|
+
"ask",
|
|
34353
|
+
"says the person is asked first; whoever runs it on an agent's behalf does the asking."
|
|
34354
|
+
],
|
|
34355
|
+
[
|
|
34356
|
+
"ask-always",
|
|
34357
|
+
"says the person is asked every time; whoever runs it on an agent's behalf does the asking."
|
|
34358
|
+
],
|
|
34359
|
+
[
|
|
34360
|
+
"never",
|
|
34361
|
+
"says it is not to be run on an agent's word; closing it to other callers is what enforces that."
|
|
34362
|
+
]
|
|
34363
|
+
]);
|
|
34364
|
+
var STATED_CONSENTS = [...CONSENT_LINES.keys()];
|
|
34234
34365
|
function consentLine(consent) {
|
|
34235
|
-
|
|
34236
|
-
|
|
34237
|
-
return "says an agent's word is enough to run it.";
|
|
34238
|
-
}
|
|
34239
|
-
case "ask": {
|
|
34240
|
-
return "says the person is asked first; whoever runs it on an agent's behalf does the asking.";
|
|
34241
|
-
}
|
|
34242
|
-
case "ask-always": {
|
|
34243
|
-
return "says the person is asked every time; whoever runs it on an agent's behalf does the asking.";
|
|
34244
|
-
}
|
|
34245
|
-
case "never": {
|
|
34246
|
-
return "says it is not to be run on an agent's word; closing it to other callers is what enforces that.";
|
|
34247
|
-
}
|
|
34248
|
-
default: {
|
|
34249
|
-
return consent === void 0 ? "says nothing about whether an agent's word is enough to run it." : `declares "${consent}" about an agent's word, which is none of ${STATED.join(", ")}, so nothing reads it.`;
|
|
34250
|
-
}
|
|
34366
|
+
if (consent === void 0) {
|
|
34367
|
+
return "says nothing about whether an agent's word is enough to run it.";
|
|
34251
34368
|
}
|
|
34369
|
+
return CONSENT_LINES.get(consent) ?? `declares "${consent}" about an agent's word, which is none of ${STATED_CONSENTS.join(", ")}, so nothing reads it.`;
|
|
34252
34370
|
}
|
|
34253
34371
|
function readArguments(ts, value) {
|
|
34254
34372
|
if (!value || !ts.isArrayLiteralExpression(value)) {
|
|
@@ -34295,15 +34413,15 @@ function returnsValue(ts, run) {
|
|
|
34295
34413
|
visit(run);
|
|
34296
34414
|
return found;
|
|
34297
34415
|
}
|
|
34298
|
-
function
|
|
34299
|
-
const { at
|
|
34416
|
+
function findingsFor(registration) {
|
|
34417
|
+
const { at, id } = registration;
|
|
34300
34418
|
if (registration.unreadable) {
|
|
34301
34419
|
return [
|
|
34302
34420
|
{
|
|
34303
34421
|
level: "info",
|
|
34304
34422
|
code: "command.unreadable",
|
|
34305
|
-
path:
|
|
34306
|
-
message: `A registerCommand call at ${
|
|
34423
|
+
path: at,
|
|
34424
|
+
message: `A registerCommand call at ${at} could not be read: ${registration.unreadable}. What it offers an agent is unknown here.`
|
|
34307
34425
|
}
|
|
34308
34426
|
];
|
|
34309
34427
|
}
|
|
@@ -34312,7 +34430,7 @@ function judge(registration) {
|
|
|
34312
34430
|
{
|
|
34313
34431
|
level: "info",
|
|
34314
34432
|
code: "command.private",
|
|
34315
|
-
path:
|
|
34433
|
+
path: at,
|
|
34316
34434
|
message: `${id}: not offered to an agent, because it is not callable; an agent never sees it.`
|
|
34317
34435
|
}
|
|
34318
34436
|
];
|
|
@@ -34322,7 +34440,7 @@ function judge(registration) {
|
|
|
34322
34440
|
findings.push({
|
|
34323
34441
|
level: "warning",
|
|
34324
34442
|
code: "command.description",
|
|
34325
|
-
path:
|
|
34443
|
+
path: at,
|
|
34326
34444
|
message: `${id}: offered to an agent without a description; the agent sees only the id and has to guess what it does.`
|
|
34327
34445
|
});
|
|
34328
34446
|
}
|
|
@@ -34331,7 +34449,7 @@ function judge(registration) {
|
|
|
34331
34449
|
findings.push({
|
|
34332
34450
|
level: "info",
|
|
34333
34451
|
code: "command.argument",
|
|
34334
|
-
path:
|
|
34452
|
+
path: at,
|
|
34335
34453
|
message: `${id}: argument "${argument.name}" has no description; an agent sees a bare name and guesses its value.`
|
|
34336
34454
|
});
|
|
34337
34455
|
}
|
|
@@ -34340,7 +34458,7 @@ function judge(registration) {
|
|
|
34340
34458
|
findings.push({
|
|
34341
34459
|
level: "info",
|
|
34342
34460
|
code: "command.answers",
|
|
34343
|
-
path:
|
|
34461
|
+
path: at,
|
|
34344
34462
|
message: `${id}: run returns a value but declares no answers; an agent hears that the command ran, not what it answered.`
|
|
34345
34463
|
});
|
|
34346
34464
|
}
|
|
@@ -34349,14 +34467,14 @@ function judge(registration) {
|
|
|
34349
34467
|
findings.push({
|
|
34350
34468
|
level: "info",
|
|
34351
34469
|
code: "command.offered",
|
|
34352
|
-
path:
|
|
34470
|
+
path: at,
|
|
34353
34471
|
message: `${id}: offered to an agent, with ${count} described argument${count === 1 ? "" : "s"}${registration.answers ? " and a declared answer" : ""}.`
|
|
34354
34472
|
});
|
|
34355
34473
|
}
|
|
34356
34474
|
findings.push({
|
|
34357
34475
|
level: "info",
|
|
34358
34476
|
code: "command.consent",
|
|
34359
|
-
path:
|
|
34477
|
+
path: at,
|
|
34360
34478
|
message: `${id}: ${consentLine(registration.agentConsent)}`
|
|
34361
34479
|
});
|
|
34362
34480
|
return findings;
|
|
@@ -34394,7 +34512,7 @@ function listGenerators() {
|
|
|
34394
34512
|
}))
|
|
34395
34513
|
});
|
|
34396
34514
|
}
|
|
34397
|
-
function
|
|
34515
|
+
function portableValuesFrom(scaffold2, args) {
|
|
34398
34516
|
const values = {};
|
|
34399
34517
|
for (const option of portableOptions(scaffold2)) {
|
|
34400
34518
|
const value = args[option.name];
|
|
@@ -34409,31 +34527,31 @@ function valuesFor(scaffold2, args) {
|
|
|
34409
34527
|
}
|
|
34410
34528
|
var WHERE_THE_FILES_LAND = "<the directory you wrote these files into>";
|
|
34411
34529
|
function scaffold(scaffold2, args) {
|
|
34412
|
-
const values =
|
|
34530
|
+
const values = portableValuesFrom(scaffold2, args);
|
|
34413
34531
|
const remaining = (scaffold2.amend?.({ ...values, directory: WHERE_THE_FILES_LAND }) ?? []).map((amendment) => describeAmendment(amendment));
|
|
34414
34532
|
return ok({
|
|
34415
34533
|
files: scaffold2.build(values),
|
|
34416
34534
|
...remaining.length > 0 && { remaining }
|
|
34417
34535
|
});
|
|
34418
34536
|
}
|
|
34419
|
-
function validateManifestTool(
|
|
34537
|
+
function validateManifestTool(args) {
|
|
34420
34538
|
return ok({
|
|
34421
34539
|
findings: validateManifest({
|
|
34422
|
-
id:
|
|
34423
|
-
name:
|
|
34424
|
-
capabilities:
|
|
34540
|
+
id: args["id"],
|
|
34541
|
+
name: args["name"],
|
|
34542
|
+
capabilities: args["capabilities"]
|
|
34425
34543
|
})
|
|
34426
34544
|
});
|
|
34427
34545
|
}
|
|
34428
|
-
function validateCatalogTool(
|
|
34429
|
-
return ok({ findings: validateCatalog(
|
|
34546
|
+
function validateCatalogTool(args) {
|
|
34547
|
+
return ok({ findings: validateCatalog(args["catalog"]) });
|
|
34430
34548
|
}
|
|
34431
|
-
function validateI18nTool(
|
|
34432
|
-
const bundles =
|
|
34549
|
+
function validateI18nTool(args) {
|
|
34550
|
+
const bundles = args["bundles"] ?? {};
|
|
34433
34551
|
return ok({ findings: validateI18nParity(bundles) });
|
|
34434
34552
|
}
|
|
34435
|
-
function validateCommandsTool(
|
|
34436
|
-
const files =
|
|
34553
|
+
function validateCommandsTool(args) {
|
|
34554
|
+
const files = args["files"] ?? {};
|
|
34437
34555
|
const ts = loadTypeScript(process.cwd());
|
|
34438
34556
|
if (!ts) {
|
|
34439
34557
|
return ok({
|
|
@@ -34470,7 +34588,7 @@ function inputSchema(descriptor) {
|
|
|
34470
34588
|
function createMcpServer() {
|
|
34471
34589
|
const server = new McpServer({
|
|
34472
34590
|
name: "loomweaver-devkit",
|
|
34473
|
-
version: "0.14.
|
|
34591
|
+
version: "0.14.3"
|
|
34474
34592
|
});
|
|
34475
34593
|
server.registerTool(
|
|
34476
34594
|
"list_generators",
|