@loomweaver/mcp 0.14.1 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/main.mjs +1367 -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,520 @@ 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/validate/manifest.ts
|
|
31649
|
+
var KNOWN_CAPABILITIES = [
|
|
31650
|
+
"contributions",
|
|
31651
|
+
"ui",
|
|
31652
|
+
"host",
|
|
31653
|
+
"navigation",
|
|
31654
|
+
"session",
|
|
31655
|
+
"theme",
|
|
31656
|
+
"automation"
|
|
31657
|
+
];
|
|
31658
|
+
function validateManifest(manifest2, known = KNOWN_CAPABILITIES) {
|
|
31659
|
+
const findings = [];
|
|
31660
|
+
if (typeof manifest2.id !== "string" || !isKebabId(manifest2.id)) {
|
|
31661
|
+
findings.push({
|
|
31662
|
+
level: "error",
|
|
31663
|
+
code: "manifest.id",
|
|
31664
|
+
message: `Plugin id must be a kebab-case string; got ${JSON.stringify(manifest2.id)}.`,
|
|
31665
|
+
path: "manifest.id"
|
|
31666
|
+
});
|
|
31667
|
+
}
|
|
31668
|
+
if (manifest2.name !== void 0 && typeof manifest2.name !== "string") {
|
|
31669
|
+
findings.push({
|
|
31670
|
+
level: "error",
|
|
31671
|
+
code: "manifest.name",
|
|
31672
|
+
message: "Plugin name must be a string when provided.",
|
|
31673
|
+
path: "manifest.name"
|
|
31674
|
+
});
|
|
31675
|
+
}
|
|
31676
|
+
findings.push(...validateCapabilities(manifest2.capabilities, known));
|
|
31677
|
+
return findings;
|
|
31678
|
+
}
|
|
31679
|
+
function validateCapabilities(capabilities, known) {
|
|
31680
|
+
if (capabilities === void 0) {
|
|
31681
|
+
return [];
|
|
31682
|
+
}
|
|
31683
|
+
if (!Array.isArray(capabilities)) {
|
|
31684
|
+
return [
|
|
31685
|
+
{
|
|
31686
|
+
level: "error",
|
|
31687
|
+
code: "manifest.capabilities",
|
|
31688
|
+
message: "capabilities must be an array when provided.",
|
|
31689
|
+
path: "manifest.capabilities"
|
|
31690
|
+
}
|
|
31691
|
+
];
|
|
31692
|
+
}
|
|
31693
|
+
const findings = [];
|
|
31694
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31695
|
+
for (const capability of capabilities) {
|
|
31696
|
+
if (typeof capability !== "string" || !known.includes(capability)) {
|
|
31697
|
+
findings.push({
|
|
31698
|
+
level: "error",
|
|
31699
|
+
code: "manifest.capability.unknown",
|
|
31700
|
+
message: `Unknown capability ${JSON.stringify(capability)}. Known: ${known.join(", ")}.`,
|
|
31701
|
+
path: "manifest.capabilities"
|
|
31702
|
+
});
|
|
31703
|
+
continue;
|
|
31704
|
+
}
|
|
31705
|
+
if (seen.has(capability)) {
|
|
31706
|
+
findings.push({
|
|
31707
|
+
level: "warning",
|
|
31708
|
+
code: "manifest.capability.duplicate",
|
|
31709
|
+
message: `Duplicate capability "${capability}".`,
|
|
31710
|
+
path: "manifest.capabilities"
|
|
31711
|
+
});
|
|
31712
|
+
}
|
|
31713
|
+
seen.add(capability);
|
|
31714
|
+
}
|
|
31715
|
+
return findings;
|
|
31716
|
+
}
|
|
31717
|
+
|
|
31718
|
+
// ../devkit/src/recipes/angular-weaver/weaver-input.ts
|
|
31719
|
+
var DEFAULT_MENU_SLOT = "content/tab/context";
|
|
31720
|
+
function accessLiteral(spec) {
|
|
31721
|
+
if (spec === "authenticated") {
|
|
31722
|
+
return "{ authenticated: true }";
|
|
31723
|
+
}
|
|
31724
|
+
if (spec === "anonymous") {
|
|
31725
|
+
return "{ authenticated: false }";
|
|
31726
|
+
}
|
|
31727
|
+
if (spec.startsWith("role:")) {
|
|
31728
|
+
const role = spec.slice("role:".length);
|
|
31729
|
+
if (!role) {
|
|
31730
|
+
throw new Error('Access "role:" needs a role name, e.g. "role:admin".');
|
|
31731
|
+
}
|
|
31732
|
+
if (/['\\]/.test(role)) {
|
|
31733
|
+
throw new Error(
|
|
31734
|
+
`Access role "${role}" must not contain quotes or backslashes.`
|
|
31735
|
+
);
|
|
31736
|
+
}
|
|
31737
|
+
return `{ anyRole: ['${role}'] }`;
|
|
31738
|
+
}
|
|
31739
|
+
throw new Error(
|
|
31740
|
+
`Unknown access "${spec}". Use "authenticated", "anonymous" or "role:<name>".`
|
|
31741
|
+
);
|
|
31742
|
+
}
|
|
31743
|
+
function resolveMenuSlot(menu) {
|
|
31744
|
+
if (menu === true) {
|
|
31745
|
+
return DEFAULT_MENU_SLOT;
|
|
31746
|
+
}
|
|
31747
|
+
return typeof menu === "string" && menu.length ? menu : void 0;
|
|
31748
|
+
}
|
|
31749
|
+
var PLATFORM_BOUND_CHORD_TOKENS = /* @__PURE__ */ new Set([
|
|
31750
|
+
"cmd",
|
|
31751
|
+
"command",
|
|
31752
|
+
"ctrl",
|
|
31753
|
+
"control",
|
|
31754
|
+
"meta"
|
|
31755
|
+
]);
|
|
31756
|
+
function assertPlatformNeutralChord(shortcut) {
|
|
31757
|
+
const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim());
|
|
31758
|
+
const bound = tokens.find((token) => PLATFORM_BOUND_CHORD_TOKENS.has(token));
|
|
31759
|
+
if (bound) {
|
|
31760
|
+
throw new Error(
|
|
31761
|
+
`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.`
|
|
31762
|
+
);
|
|
31763
|
+
}
|
|
31764
|
+
}
|
|
31765
|
+
function resolveFeatures(id, input) {
|
|
31766
|
+
const menuSlot = resolveMenuSlot(input?.menu);
|
|
31767
|
+
const barItem = Boolean(input?.barItem);
|
|
31768
|
+
const hasShortcut = Boolean(input?.shortcut?.trim());
|
|
31769
|
+
if (hasShortcut) {
|
|
31770
|
+
assertPlatformNeutralChord(input?.shortcut?.trim() ?? "");
|
|
31771
|
+
}
|
|
31772
|
+
const instanceable = Boolean(input?.instanceable);
|
|
31773
|
+
const container = Boolean(input?.container);
|
|
31774
|
+
if (instanceable && container) {
|
|
31775
|
+
throw new Error(
|
|
31776
|
+
'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.'
|
|
31777
|
+
);
|
|
31778
|
+
}
|
|
31779
|
+
const agent = Boolean(input?.agent);
|
|
31780
|
+
return {
|
|
31781
|
+
command: Boolean(input?.command) || menuSlot !== void 0 || barItem || hasShortcut || agent,
|
|
31782
|
+
menuSlot,
|
|
31783
|
+
settings: Boolean(input?.settings),
|
|
31784
|
+
access: input?.access ? accessLiteral(input.access) : void 0,
|
|
31785
|
+
shortcut: input?.shortcut?.trim() || `mod+shift+${id.charAt(0)}`,
|
|
31786
|
+
barItem,
|
|
31787
|
+
about: Boolean(input?.about),
|
|
31788
|
+
instanceable,
|
|
31789
|
+
container,
|
|
31790
|
+
agent,
|
|
31791
|
+
spec: input?.spec !== false
|
|
31792
|
+
};
|
|
31793
|
+
}
|
|
31794
|
+
function deriveCapabilities(features) {
|
|
31795
|
+
const set2 = /* @__PURE__ */ new Set(["contributions", "navigation"]);
|
|
31796
|
+
if (features.command) set2.add("ui");
|
|
31797
|
+
if (features.about) {
|
|
31798
|
+
set2.add("ui");
|
|
31799
|
+
set2.add("host");
|
|
31800
|
+
}
|
|
31801
|
+
if (features.agent) {
|
|
31802
|
+
set2.add("ui");
|
|
31803
|
+
set2.add("automation");
|
|
31804
|
+
}
|
|
31805
|
+
return KNOWN_CAPABILITIES.filter((capability) => set2.has(capability));
|
|
31806
|
+
}
|
|
31807
|
+
function resolveWeaverInput(input) {
|
|
31808
|
+
if (!isKebabId(input.id)) {
|
|
31809
|
+
throw new Error(
|
|
31810
|
+
`Weaver id must be kebab-case (e.g. "notes"); got "${input.id}".`
|
|
31811
|
+
);
|
|
31812
|
+
}
|
|
31813
|
+
const features = resolveFeatures(input.id, input.features);
|
|
31814
|
+
const capabilities = input.capabilities?.length ? [...input.capabilities] : deriveCapabilities(features);
|
|
31815
|
+
return {
|
|
31816
|
+
id: input.id,
|
|
31817
|
+
name: input.name?.trim() || toTitleCase(input.id),
|
|
31818
|
+
className: toPascalCase(input.id),
|
|
31819
|
+
propertyName: toCamelCase(input.id),
|
|
31820
|
+
capabilities,
|
|
31821
|
+
features,
|
|
31822
|
+
prefix: input.prefix?.trim() || "lw",
|
|
31823
|
+
importPath: input.importPath?.trim() || `@loomweaver/${input.id}-weaver`
|
|
31824
|
+
};
|
|
31825
|
+
}
|
|
31826
|
+
|
|
31827
|
+
// ../devkit/src/recipes/angular-weaver/weaver-command.ts
|
|
31828
|
+
function commandBlock(weaver) {
|
|
31829
|
+
return [
|
|
31830
|
+
" ctx.registerCommand({",
|
|
31831
|
+
` id: '${weaver.id}.hello',`,
|
|
31832
|
+
` title: '${weaver.id}.action',`,
|
|
31833
|
+
` description: '${weaver.id}.actionDescription',`,
|
|
31834
|
+
" arguments: [",
|
|
31835
|
+
` { name: 'tone', kind: 'choice', choices: ['info', 'success', 'warning'], description: '${weaver.id}.actionTone' },`,
|
|
31836
|
+
" ],",
|
|
31837
|
+
` answers: '${weaver.id}.actionAnswers',`,
|
|
31838
|
+
` shortcut: '${weaver.features.shortcut}',`,
|
|
31839
|
+
" callable: true,",
|
|
31840
|
+
...weaver.features.agent ? [
|
|
31841
|
+
" // What an agent's word is enough for. The platform states it and enforces nothing:",
|
|
31842
|
+
" // the asking is the connection's half, which reads this off the call.",
|
|
31843
|
+
" agentConsent: 'ask',"
|
|
31844
|
+
] : [],
|
|
31845
|
+
" run: (_context, args) => {",
|
|
31846
|
+
" const tone = toneOf(args?.['tone']);",
|
|
31847
|
+
` ctx.ui.toast({ message: '${weaver.id}.action', kind: tone });`,
|
|
31848
|
+
" return { tone };",
|
|
31849
|
+
" },",
|
|
31850
|
+
" });"
|
|
31851
|
+
].join("\n");
|
|
31852
|
+
}
|
|
31853
|
+
function toneHelper() {
|
|
31854
|
+
return [
|
|
31855
|
+
"type Tone = 'info' | 'success' | 'warning';",
|
|
31856
|
+
"",
|
|
31857
|
+
"// The workbench already refused anything outside the declared choices; this narrows the type.",
|
|
31858
|
+
"function toneOf(value: unknown): Tone {",
|
|
31859
|
+
" return value === 'success' || value === 'warning' ? value : 'info';",
|
|
31860
|
+
"}"
|
|
31861
|
+
].join("\n");
|
|
31862
|
+
}
|
|
31863
|
+
|
|
31864
|
+
// ../devkit/src/recipes/angular-weaver/weaver-plugin.ts
|
|
31625
31865
|
var CONTAINER_EXAMPLE_ID = "example";
|
|
31626
|
-
|
|
31627
|
-
|
|
31866
|
+
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>';
|
|
31867
|
+
function containerChildIds(weaver) {
|
|
31868
|
+
return [`${weaver.id}.canvas`, `${weaver.id}.details`];
|
|
31869
|
+
}
|
|
31870
|
+
function containerSurfaceBlock(weaver) {
|
|
31871
|
+
const children = containerChildIds(weaver).map((id) => `'${id}'`).join(", ");
|
|
31872
|
+
const lines = [
|
|
31873
|
+
" ctx.registerSurface({",
|
|
31874
|
+
` id: '${weaver.id}',`,
|
|
31875
|
+
` title: '${weaver.id}.title',`,
|
|
31876
|
+
` icon: '${weaver.id}',`,
|
|
31877
|
+
` routable: { path: '${weaver.id}/:id' },`,
|
|
31878
|
+
" container: {",
|
|
31879
|
+
` children: [${children}],`,
|
|
31880
|
+
` initial: [${children}],`,
|
|
31881
|
+
" },",
|
|
31882
|
+
...accessLine(weaver),
|
|
31883
|
+
" });"
|
|
31884
|
+
];
|
|
31885
|
+
for (const [suffix, className] of [
|
|
31886
|
+
["canvas", `${weaver.className}CanvasView`],
|
|
31887
|
+
["details", `${weaver.className}DetailsView`]
|
|
31888
|
+
]) {
|
|
31889
|
+
lines.push(
|
|
31890
|
+
" ctx.registerSurface({",
|
|
31891
|
+
` id: '${weaver.id}.${suffix}',`,
|
|
31892
|
+
` title: '${weaver.id}.${suffix}',`,
|
|
31893
|
+
" docks: [],",
|
|
31894
|
+
` component: ${className},`,
|
|
31895
|
+
" });"
|
|
31896
|
+
);
|
|
31897
|
+
}
|
|
31898
|
+
return lines.join("\n");
|
|
31899
|
+
}
|
|
31900
|
+
function surfaceBlock(weaver) {
|
|
31901
|
+
if (weaver.features.container) {
|
|
31902
|
+
return containerSurfaceBlock(weaver);
|
|
31903
|
+
}
|
|
31904
|
+
const lines = [
|
|
31905
|
+
" ctx.registerSurface({",
|
|
31906
|
+
` id: '${weaver.id}',`,
|
|
31907
|
+
` title: '${weaver.id}.title',`,
|
|
31908
|
+
` icon: '${weaver.id}',`,
|
|
31909
|
+
` component: ${weaver.className}View,`
|
|
31910
|
+
];
|
|
31911
|
+
if (weaver.features.instanceable) {
|
|
31912
|
+
lines.push(` docks: ['${LEFT_PANEL_REGION}'],`, " instanceable: true,");
|
|
31913
|
+
} else {
|
|
31914
|
+
lines.push(` routable: { path: '${weaver.id}' },`);
|
|
31915
|
+
}
|
|
31916
|
+
lines.push(...accessLine(weaver), " });");
|
|
31917
|
+
return lines.join("\n");
|
|
31918
|
+
}
|
|
31919
|
+
function railTarget(weaver) {
|
|
31920
|
+
if (weaver.features.container) {
|
|
31921
|
+
return `ctx.navigateContent('${weaver.id}/${CONTAINER_EXAMPLE_ID}')`;
|
|
31922
|
+
}
|
|
31923
|
+
if (weaver.features.instanceable) {
|
|
31924
|
+
return `ctx.revealSurface('${weaver.id}')`;
|
|
31925
|
+
}
|
|
31926
|
+
return `ctx.navigateContent('${weaver.id}')`;
|
|
31927
|
+
}
|
|
31928
|
+
function railBlock(weaver) {
|
|
31929
|
+
const lines = [
|
|
31930
|
+
" ctx.registerRailItem({",
|
|
31931
|
+
` id: '${weaver.id}.rail',`,
|
|
31932
|
+
` rail: '${RAIL_REGION}',`,
|
|
31933
|
+
` icon: '${weaver.id}',`,
|
|
31934
|
+
` title: '${weaver.id}.title',`,
|
|
31935
|
+
` run: () => ${railTarget(weaver)},`,
|
|
31936
|
+
...accessLine(weaver),
|
|
31937
|
+
" });"
|
|
31938
|
+
];
|
|
31939
|
+
return lines.join("\n");
|
|
31940
|
+
}
|
|
31941
|
+
function barItemBlock(weaver) {
|
|
31942
|
+
return [
|
|
31943
|
+
" ctx.registerBarItem({",
|
|
31944
|
+
` id: '${weaver.id}.bar',`,
|
|
31945
|
+
` bar: '${STATUS_BAR_REGION}',`,
|
|
31946
|
+
" slot: 'end',",
|
|
31947
|
+
` icon: '${weaver.id}',`,
|
|
31948
|
+
` tooltip: '${weaver.id}.action',`,
|
|
31949
|
+
` command: '${weaver.id}.hello',`,
|
|
31950
|
+
" });"
|
|
31951
|
+
].join("\n");
|
|
31952
|
+
}
|
|
31953
|
+
function aboutCommandBlock(weaver) {
|
|
31954
|
+
return [
|
|
31955
|
+
" ctx.registerCommand({",
|
|
31956
|
+
` id: '${weaver.id}.about',`,
|
|
31957
|
+
` title: '${weaver.id}.about',`,
|
|
31958
|
+
` run: () => ctx.ui.open(${weaver.className}AboutDialog, { data: ctx.host, title: '${weaver.id}.title' }),`,
|
|
31959
|
+
" });"
|
|
31960
|
+
].join("\n");
|
|
31961
|
+
}
|
|
31962
|
+
function aboutRailBlock(weaver) {
|
|
31963
|
+
return [
|
|
31964
|
+
" ctx.registerRailItem({",
|
|
31965
|
+
` id: '${weaver.id}.rail.about',`,
|
|
31966
|
+
` rail: '${RAIL_REGION}',`,
|
|
31967
|
+
" anchor: 'bottom',",
|
|
31968
|
+
` icon: '${weaver.id}',`,
|
|
31969
|
+
` title: '${weaver.id}.about',`,
|
|
31970
|
+
` command: '${weaver.id}.about',`,
|
|
31971
|
+
" });"
|
|
31972
|
+
].join("\n");
|
|
31973
|
+
}
|
|
31974
|
+
function menuBlock(weaver) {
|
|
31975
|
+
return [
|
|
31976
|
+
" ctx.registerMenuItem({",
|
|
31977
|
+
` menu: '${weaver.features.menuSlot}',`,
|
|
31978
|
+
` command: '${weaver.id}.hello',`,
|
|
31979
|
+
" });"
|
|
31980
|
+
].join("\n");
|
|
31981
|
+
}
|
|
31982
|
+
function settingsBlock(weaver) {
|
|
31983
|
+
return [
|
|
31984
|
+
" ctx.registerSettingsSection({",
|
|
31985
|
+
` id: '${weaver.id}',`,
|
|
31986
|
+
` title: '${weaver.id}.settings.title',`,
|
|
31987
|
+
" rows: [",
|
|
31988
|
+
" {",
|
|
31989
|
+
" id: 'enabled',",
|
|
31990
|
+
` label: '${weaver.id}.settings.enabled',`,
|
|
31991
|
+
` control: { kind: 'toggle', value: () => ${weaver.propertyName}Enabled(), set: (v) => ${weaver.propertyName}Enabled.set(v) },`,
|
|
31992
|
+
" },",
|
|
31993
|
+
" {",
|
|
31994
|
+
" id: 'note',",
|
|
31995
|
+
` label: '${weaver.id}.settings.note',`,
|
|
31996
|
+
` control: { kind: 'text', value: () => ${weaver.propertyName}Note(), set: (v) => ${weaver.propertyName}Note.set(v) },`,
|
|
31997
|
+
" },",
|
|
31998
|
+
" ],",
|
|
31999
|
+
" });"
|
|
32000
|
+
].join("\n");
|
|
32001
|
+
}
|
|
32002
|
+
function pluginImports(weaver) {
|
|
32003
|
+
const imports = ["import { Plugin } from '@loomweaver/plugin-sdk';"];
|
|
32004
|
+
if (weaver.features.container) {
|
|
32005
|
+
imports.push(
|
|
32006
|
+
`import { ${weaver.className}CanvasView } from '../views/${weaver.id}-canvas-view';`,
|
|
32007
|
+
`import { ${weaver.className}DetailsView } from '../views/${weaver.id}-details-view';`
|
|
32008
|
+
);
|
|
32009
|
+
} else {
|
|
32010
|
+
imports.push(`import { ${weaver.className}View } from '../views/${weaver.id}-view';`);
|
|
32011
|
+
}
|
|
32012
|
+
if (weaver.features.about) {
|
|
32013
|
+
imports.push(
|
|
32014
|
+
`import { ${weaver.className}AboutDialog } from '../dialogs/${weaver.id}-about-dialog';`
|
|
32015
|
+
);
|
|
32016
|
+
}
|
|
32017
|
+
if (weaver.features.agent) {
|
|
32018
|
+
imports.push(
|
|
32019
|
+
`import { ${weaver.propertyName}Agent, ${weaver.propertyName}Connection } from '../agent/${weaver.id}-agent';`,
|
|
32020
|
+
`import { ${weaver.className}AgentPanel } from '../agent/${weaver.id}-agent-panel';`
|
|
32021
|
+
);
|
|
32022
|
+
}
|
|
32023
|
+
if (weaver.features.settings) {
|
|
32024
|
+
imports.unshift("import { signal } from '@angular/core';");
|
|
32025
|
+
}
|
|
32026
|
+
return imports;
|
|
32027
|
+
}
|
|
32028
|
+
function pluginConsts(weaver) {
|
|
32029
|
+
const consts = [`const icon =
|
|
32030
|
+
'${SURFACE_ICON}';`];
|
|
32031
|
+
if (weaver.features.settings) {
|
|
32032
|
+
consts.push(
|
|
32033
|
+
`const ${weaver.propertyName}Enabled = signal(true);`,
|
|
32034
|
+
`const ${weaver.propertyName}Note = signal('');`
|
|
32035
|
+
);
|
|
32036
|
+
}
|
|
32037
|
+
if (weaver.features.command) consts.push(toneHelper());
|
|
32038
|
+
return consts;
|
|
32039
|
+
}
|
|
32040
|
+
function pluginBody(weaver) {
|
|
32041
|
+
const body = [` ctx.contributeIcons({ '${weaver.id}': icon });`];
|
|
32042
|
+
if (weaver.features.agent) {
|
|
32043
|
+
body.push(
|
|
32044
|
+
` ${weaver.propertyName}Agent.set(${weaver.propertyName}Connection(ctx));`
|
|
32045
|
+
);
|
|
32046
|
+
}
|
|
32047
|
+
if (weaver.features.command) body.push(commandBlock(weaver));
|
|
32048
|
+
if (weaver.features.about) body.push(aboutCommandBlock(weaver));
|
|
32049
|
+
body.push(surfaceBlock(weaver), railBlock(weaver));
|
|
32050
|
+
if (weaver.features.about) body.push(aboutRailBlock(weaver));
|
|
32051
|
+
if (weaver.features.barItem) body.push(barItemBlock(weaver));
|
|
32052
|
+
if (weaver.features.menuSlot) body.push(menuBlock(weaver));
|
|
32053
|
+
if (weaver.features.settings) body.push(settingsBlock(weaver));
|
|
32054
|
+
if (weaver.features.agent) body.push(agentSurfaceBlock(weaver));
|
|
32055
|
+
return body;
|
|
32056
|
+
}
|
|
32057
|
+
function pluginFile(weaver) {
|
|
32058
|
+
const body = pluginBody(weaver);
|
|
32059
|
+
const deactivate = weaver.features.agent ? `
|
|
32060
|
+
deactivate() {
|
|
32061
|
+
${weaver.propertyName}Agent.set(null);
|
|
32062
|
+
},` : "";
|
|
32063
|
+
return `${pluginImports(weaver).join("\n")}
|
|
32064
|
+
|
|
32065
|
+
${pluginConsts(weaver).join("\n\n")}
|
|
32066
|
+
|
|
32067
|
+
export const ${weaver.propertyName}Plugin: Plugin = {
|
|
32068
|
+
manifest: {
|
|
32069
|
+
id: '${weaver.id}',
|
|
32070
|
+
name: '${weaver.name}',
|
|
32071
|
+
capabilities: [${quotedList(weaver.capabilities)}],
|
|
32072
|
+
},
|
|
32073
|
+
activate(ctx) {
|
|
32074
|
+
${body.join("\n")}
|
|
32075
|
+
},${deactivate}
|
|
32076
|
+
};
|
|
32077
|
+
`;
|
|
32078
|
+
}
|
|
32079
|
+
function accessLine(weaver) {
|
|
32080
|
+
return weaver.features.access ? [` access: ${weaver.features.access},`] : [];
|
|
32081
|
+
}
|
|
32082
|
+
|
|
32083
|
+
// ../devkit/src/recipes/platform-version.ts
|
|
32084
|
+
var PLATFORM_VERSION = "0.14.2";
|
|
32085
|
+
|
|
32086
|
+
// ../devkit/src/recipes/readme-notes.ts
|
|
32087
|
+
var UNTAGGED_NOTE = [
|
|
32088
|
+
"The project is generated **untagged**: Nx tags belong to your `depConstraints`, and inventing",
|
|
32089
|
+
"one would fail a lint policy you never opted this project into. If your workspace enforces",
|
|
32090
|
+
"module boundaries, give it tags your constraints allow \u2014 `--tags` at generation time, or",
|
|
32091
|
+
"`tags` in `project.json` afterwards."
|
|
32092
|
+
];
|
|
32093
|
+
function asBullet(lines) {
|
|
32094
|
+
return lines.map((line, index) => index === 0 ? `- ${line}` : ` ${line}`);
|
|
31628
32095
|
}
|
|
31629
32096
|
|
|
31630
32097
|
// ../devkit/src/recipes/angular-weaver/weaver-readme.ts
|
|
31631
|
-
function surfaceNotes(
|
|
31632
|
-
const railNote = "Rail and bar items reference region ids (`primary
|
|
31633
|
-
if (
|
|
32098
|
+
function surfaceNotes(weaver) {
|
|
32099
|
+
const railNote = "Rail and bar items reference region ids (`primary` rail, `status-bar` bar) that must exist in your layout.";
|
|
32100
|
+
if (weaver.features.container) {
|
|
31634
32101
|
return [
|
|
31635
|
-
`The surface is a **container**: it is routable at \`/${
|
|
32102
|
+
`The surface is a **container**: it is routable at \`/${weaver.id}/:id\`, and its tab holds a`,
|
|
31636
32103
|
"nested pane tree of child surfaces. The host draws the inner tabs, splits and drag targets; this",
|
|
31637
32104
|
"weaver only declares which children it offers.",
|
|
31638
32105
|
"",
|
|
@@ -31652,9 +32119,9 @@ function surfaceNotes(w) {
|
|
|
31652
32119
|
railNote
|
|
31653
32120
|
];
|
|
31654
32121
|
}
|
|
31655
|
-
if (
|
|
32122
|
+
if (weaver.features.instanceable) {
|
|
31656
32123
|
return [
|
|
31657
|
-
`The surface is **docked** into the \`
|
|
32124
|
+
`The surface is **docked** into the \`left-panel\` region and marked \`instanceable\`, so the host shows a`,
|
|
31658
32125
|
"switcher for saving, naming, renaming and deleting several configurations of it, each with its own",
|
|
31659
32126
|
"`VIEW_STATE` blob.",
|
|
31660
32127
|
"",
|
|
@@ -31672,7 +32139,7 @@ function surfaceNotes(w) {
|
|
|
31672
32139
|
];
|
|
31673
32140
|
}
|
|
31674
32141
|
return [
|
|
31675
|
-
`The surface is routable at \`/${
|
|
32142
|
+
`The surface is routable at \`/${weaver.id}\`; ${railNote.charAt(0).toLowerCase()}${railNote.slice(1)}`,
|
|
31676
32143
|
"",
|
|
31677
32144
|
"A routable surface has **no `VIEW_STATE` handle** \u2014 injecting the token there throws. It owns a URL,",
|
|
31678
32145
|
"so anything shareable (a filter, the active sub-tab) belongs in route params or `subRoutes`, where it",
|
|
@@ -31681,18 +32148,18 @@ function surfaceNotes(w) {
|
|
|
31681
32148
|
"flavour instead."
|
|
31682
32149
|
];
|
|
31683
32150
|
}
|
|
31684
|
-
function agentNotes(
|
|
32151
|
+
function agentNotes(weaver) {
|
|
31685
32152
|
return [
|
|
31686
32153
|
"",
|
|
31687
32154
|
"## The agent connection",
|
|
31688
32155
|
"",
|
|
31689
32156
|
`\`src/lib/agent/\` holds three files and one of them is meant to be thrown away.`,
|
|
31690
32157
|
"",
|
|
31691
|
-
`- \`${
|
|
32158
|
+
`- \`${weaver.id}-agent.ts\` is the connection: the workbench's own commands offered as tools, and a`,
|
|
31692
32159
|
" seam where this weaver decides about a call before it runs. Nothing is registered twice \u2014 the",
|
|
31693
32160
|
" list comes from the workbench, already narrowed by everything that would refuse the call.",
|
|
31694
|
-
`- \`${
|
|
31695
|
-
`- \`${
|
|
32161
|
+
`- \`${weaver.id}-agent-panel.ts\` shows what is offered, the call as it streams, and the outcome.`,
|
|
32162
|
+
`- \`${weaver.id}-agent-source.ts\` is a **stand-in**, not an assistant: it produces the protocol's own`,
|
|
31696
32163
|
" events so the whole path runs before you have connected anything. Replace that one file with",
|
|
31697
32164
|
" your transport and nothing else changes. No transport, credential or model is generated for",
|
|
31698
32165
|
" you, because none of them can be guessed.",
|
|
@@ -31705,15 +32172,15 @@ function agentNotes(w) {
|
|
|
31705
32172
|
`The generated weaver needs two packages your project may not carry yet:`,
|
|
31706
32173
|
"",
|
|
31707
32174
|
"```bash",
|
|
31708
|
-
`npm i @loomweaver/ag-ui @ag-ui/core`,
|
|
32175
|
+
`npm i @loomweaver/ag-ui@^${PLATFORM_VERSION} @ag-ui/core@${AG_UI_PROTOCOL_VERSION}`,
|
|
31709
32176
|
"```",
|
|
31710
32177
|
"",
|
|
31711
32178
|
"The Nx generator and the CLI record them for you; the MCP route names them instead."
|
|
31712
32179
|
];
|
|
31713
32180
|
}
|
|
31714
|
-
function readmeFile(
|
|
32181
|
+
function readmeFile(weaver) {
|
|
31715
32182
|
return [
|
|
31716
|
-
`# ${
|
|
32183
|
+
`# ${weaver.name} weaver`,
|
|
31717
32184
|
"",
|
|
31718
32185
|
`A LoomWeaver weaver (a domain plugin bundle). It consumes only the public \`@loomweaver/plugin-sdk\` contract.`,
|
|
31719
32186
|
"",
|
|
@@ -31723,22 +32190,22 @@ function readmeFile(w) {
|
|
|
31723
32190
|
` returns an array, so spread it:`,
|
|
31724
32191
|
"",
|
|
31725
32192
|
" ```ts",
|
|
31726
|
-
` import { ${
|
|
31727
|
-
` ...providePlugins(${
|
|
32193
|
+
` import { ${weaver.propertyName}Plugin } from '${weaver.importPath}'; // Nx: the workspace alias; without one, a relative path to this library's src/index.ts`,
|
|
32194
|
+
` ...providePlugins(${weaver.propertyName}Plugin),`,
|
|
31728
32195
|
" ```",
|
|
31729
32196
|
"",
|
|
31730
32197
|
`2. Grant its capabilities (default-deny) via \`provideCapabilityGrants\`:`,
|
|
31731
32198
|
"",
|
|
31732
32199
|
" ```ts",
|
|
31733
|
-
` provideCapabilityGrants({ '${
|
|
32200
|
+
` provideCapabilityGrants({ '${weaver.id}': [${quotedList(weaver.capabilities)}] });`,
|
|
31734
32201
|
" ```",
|
|
31735
32202
|
"",
|
|
31736
|
-
`3. Compose its translations with \`provideTranslationNamespaces('${
|
|
32203
|
+
`3. Compose its translations with \`provideTranslationNamespaces('${weaver.id}')\` \u2014 and serve the`,
|
|
31737
32204
|
` bundle by adding an assets glob to your application's build target, so the loader can fetch`,
|
|
31738
|
-
` \`/i18n/${
|
|
32205
|
+
` \`/i18n/${weaver.id}/<lang>.json\` (the Nx generator adds this glob for you):`,
|
|
31739
32206
|
"",
|
|
31740
32207
|
" ```json",
|
|
31741
|
-
` { "glob": "**/*.json", "input": "<path to this library>/src/lib/i18n", "output": "i18n/${
|
|
32208
|
+
` { "glob": "**/*.json", "input": "<path to this library>/src/lib/i18n", "output": "i18n/${weaver.id}" }`,
|
|
31742
32209
|
" ```",
|
|
31743
32210
|
"",
|
|
31744
32211
|
`4. If your application compiles the shell's theme with Tailwind, name this library as a source`,
|
|
@@ -31751,459 +32218,65 @@ function readmeFile(w) {
|
|
|
31751
32218
|
` @source '<path from that stylesheet to this library>/src';`,
|
|
31752
32219
|
" ```",
|
|
31753
32220
|
"",
|
|
31754
|
-
...surfaceNotes(
|
|
31755
|
-
...
|
|
32221
|
+
...surfaceNotes(weaver),
|
|
32222
|
+
...weaver.features.agent ? agentNotes(weaver) : [],
|
|
31756
32223
|
"",
|
|
31757
32224
|
"## After scaffolding",
|
|
31758
32225
|
"",
|
|
31759
32226
|
"- `src/lib/i18n/de.json` starts as a copy of the English strings \u2014 translate it.",
|
|
31760
32227
|
`- 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.",
|
|
32228
|
+
...asBullet(UNTAGGED_NOTE),
|
|
31765
32229
|
""
|
|
31766
32230
|
].join("\n");
|
|
31767
32231
|
}
|
|
31768
32232
|
|
|
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) {
|
|
32233
|
+
// ../devkit/src/recipes/angular-weaver/weaver-views.ts
|
|
32234
|
+
function aboutDialogFile(weaver) {
|
|
32158
32235
|
return `import { Component, inject } from '@angular/core';
|
|
32159
32236
|
import { DialogRef, PluginHost } from '@loomweaver/plugin-sdk';
|
|
32160
32237
|
|
|
32161
32238
|
@Component({
|
|
32162
|
-
selector: '${
|
|
32163
|
-
templateUrl: './${
|
|
32239
|
+
selector: '${weaver.prefix}-${weaver.id}-about-dialog',
|
|
32240
|
+
templateUrl: './${weaver.id}-about-dialog.html',
|
|
32164
32241
|
})
|
|
32165
|
-
export class ${
|
|
32242
|
+
export class ${weaver.className}AboutDialog {
|
|
32166
32243
|
protected readonly host = inject(DialogRef).data as PluginHost;
|
|
32167
32244
|
}
|
|
32168
32245
|
`;
|
|
32169
32246
|
}
|
|
32170
|
-
function aboutDialogTemplateFile(
|
|
32247
|
+
function aboutDialogTemplateFile(weaver) {
|
|
32171
32248
|
return `<div class="flex flex-col items-center gap-2 text-center">
|
|
32172
|
-
<h2 class="text-lg font-semibold text-content">${
|
|
32249
|
+
<h2 class="text-lg font-semibold text-content">${weaver.name}</h2>
|
|
32173
32250
|
<span class="text-xs text-content-faint tabular-nums">v{{ host.version() }}</span>
|
|
32174
32251
|
</div>
|
|
32175
32252
|
`;
|
|
32176
32253
|
}
|
|
32177
|
-
function
|
|
32178
|
-
|
|
32179
|
-
`;
|
|
32180
|
-
}
|
|
32181
|
-
function viewFile(w) {
|
|
32182
|
-
if (!w.features.instanceable) {
|
|
32254
|
+
function viewFile(weaver) {
|
|
32255
|
+
if (!weaver.features.instanceable) {
|
|
32183
32256
|
return `import { Component } from '@angular/core';
|
|
32184
32257
|
|
|
32185
32258
|
@Component({
|
|
32186
|
-
selector: '${
|
|
32187
|
-
templateUrl: './${
|
|
32259
|
+
selector: '${weaver.prefix}-${weaver.id}-view',
|
|
32260
|
+
templateUrl: './${weaver.id}-view.html',
|
|
32188
32261
|
})
|
|
32189
|
-
export class ${
|
|
32262
|
+
export class ${weaver.className}View {}
|
|
32190
32263
|
`;
|
|
32191
32264
|
}
|
|
32192
32265
|
return `import { Component, computed, inject } from '@angular/core';
|
|
32193
32266
|
import { VIEW_STATE, type ViewState } from '@loomweaver/plugin-sdk';
|
|
32194
32267
|
|
|
32195
|
-
interface ${
|
|
32268
|
+
interface ${weaver.className}State {
|
|
32196
32269
|
readonly sort: 'natural' | 'alpha';
|
|
32197
32270
|
}
|
|
32198
32271
|
|
|
32199
|
-
const FRESH: ${
|
|
32272
|
+
const FRESH: ${weaver.className}State = { sort: 'natural' };
|
|
32200
32273
|
|
|
32201
32274
|
@Component({
|
|
32202
|
-
selector: '${
|
|
32203
|
-
templateUrl: './${
|
|
32275
|
+
selector: '${weaver.prefix}-${weaver.id}-view',
|
|
32276
|
+
templateUrl: './${weaver.id}-view.html',
|
|
32204
32277
|
})
|
|
32205
|
-
export class ${
|
|
32206
|
-
private readonly viewState = inject(VIEW_STATE) as ViewState<${
|
|
32278
|
+
export class ${weaver.className}View {
|
|
32279
|
+
private readonly viewState = inject(VIEW_STATE) as ViewState<${weaver.className}State>;
|
|
32207
32280
|
|
|
32208
32281
|
// undefined = a fresh instance, so apply your own default.
|
|
32209
32282
|
private readonly state = computed(() => this.viewState.value() ?? FRESH);
|
|
@@ -32220,8 +32293,8 @@ export class ${w.className}View {
|
|
|
32220
32293
|
}
|
|
32221
32294
|
`;
|
|
32222
32295
|
}
|
|
32223
|
-
function viewTemplateFile(
|
|
32224
|
-
const stateNote =
|
|
32296
|
+
function viewTemplateFile(weaver) {
|
|
32297
|
+
const stateNote = weaver.features.instanceable ? ` <button type="button" class="lw-btn lw-btn--default self-start" (click)="toggleSort()">
|
|
32225
32298
|
Sort: {{ sort() }}
|
|
32226
32299
|
</button>
|
|
32227
32300
|
<p class="text-sm text-content-faint">
|
|
@@ -32230,7 +32303,7 @@ function viewTemplateFile(w) {
|
|
|
32230
32303
|
</p>
|
|
32231
32304
|
` : "";
|
|
32232
32305
|
return `<div class="mx-auto flex max-w-2xl flex-col gap-4 p-6">
|
|
32233
|
-
<h2 class="text-lg font-semibold text-content">${
|
|
32306
|
+
<h2 class="text-lg font-semibold text-content">${weaver.name}</h2>
|
|
32234
32307
|
<p class="text-sm text-content-faint">
|
|
32235
32308
|
Your new weaver surface. Register more surfaces, commands, rail items and menus on
|
|
32236
32309
|
<code class="text-content">ctx</code> inside the plugin's
|
|
@@ -32239,13 +32312,13 @@ function viewTemplateFile(w) {
|
|
|
32239
32312
|
${stateNote}</div>
|
|
32240
32313
|
`;
|
|
32241
32314
|
}
|
|
32242
|
-
function childViewFile(
|
|
32315
|
+
function childViewFile(weaver, suffix, className) {
|
|
32243
32316
|
return `import { Component, inject } from '@angular/core';
|
|
32244
32317
|
import { ActivatedRoute } from '@angular/router';
|
|
32245
32318
|
|
|
32246
32319
|
@Component({
|
|
32247
|
-
selector: '${
|
|
32248
|
-
templateUrl: './${
|
|
32320
|
+
selector: '${weaver.prefix}-${weaver.id}-${suffix}-view',
|
|
32321
|
+
templateUrl: './${weaver.id}-${suffix}-view.html',
|
|
32249
32322
|
})
|
|
32250
32323
|
export class ${className} {
|
|
32251
32324
|
private readonly route = inject(ActivatedRoute, { optional: true });
|
|
@@ -32255,7 +32328,7 @@ export class ${className} {
|
|
|
32255
32328
|
}
|
|
32256
32329
|
`;
|
|
32257
32330
|
}
|
|
32258
|
-
function childViewTemplateFile(
|
|
32331
|
+
function childViewTemplateFile(weaver, heading) {
|
|
32259
32332
|
return `<div class="flex h-full flex-col gap-3 p-4">
|
|
32260
32333
|
<h3 class="text-sm font-semibold text-content">${heading}</h3>
|
|
32261
32334
|
<p class="text-sm text-content-faint">
|
|
@@ -32266,60 +32339,66 @@ function childViewTemplateFile(w, heading) {
|
|
|
32266
32339
|
</div>
|
|
32267
32340
|
`;
|
|
32268
32341
|
}
|
|
32269
|
-
function specFile2(
|
|
32270
|
-
return `import { ${
|
|
32342
|
+
function specFile2(weaver) {
|
|
32343
|
+
return `import { ${weaver.propertyName}Plugin } from './${weaver.id}.plugin';
|
|
32271
32344
|
|
|
32272
|
-
describe('${
|
|
32345
|
+
describe('${weaver.propertyName}Plugin', () => {
|
|
32273
32346
|
it('declares its manifest', () => {
|
|
32274
|
-
expect(${
|
|
32275
|
-
expect(${
|
|
32347
|
+
expect(${weaver.propertyName}Plugin.manifest.id).toBe('${weaver.id}');
|
|
32348
|
+
expect(${weaver.propertyName}Plugin.manifest.capabilities).toContain('contributions');
|
|
32276
32349
|
});
|
|
32277
32350
|
});
|
|
32278
32351
|
`;
|
|
32279
32352
|
}
|
|
32353
|
+
|
|
32354
|
+
// ../devkit/src/recipes/angular-weaver/recipe.ts
|
|
32355
|
+
function indexFile(weaver) {
|
|
32356
|
+
return `export { ${weaver.propertyName}Plugin } from './lib/plugin/${weaver.id}.plugin';
|
|
32357
|
+
`;
|
|
32358
|
+
}
|
|
32280
32359
|
var angularWeaver = {
|
|
32281
32360
|
id: "angular-weaver",
|
|
32282
32361
|
build(input) {
|
|
32283
|
-
const
|
|
32362
|
+
const weaver = resolveWeaverInput(input);
|
|
32284
32363
|
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(
|
|
32364
|
+
"src/index.ts": indexFile(weaver),
|
|
32365
|
+
[`src/lib/plugin/${weaver.id}.plugin.ts`]: pluginFile(weaver),
|
|
32366
|
+
"src/lib/i18n/en.json": i18nFile(weaver),
|
|
32367
|
+
"src/lib/i18n/de.json": i18nFile(weaver),
|
|
32368
|
+
"README.md": readmeFile(weaver)
|
|
32290
32369
|
};
|
|
32291
|
-
if (
|
|
32292
|
-
files[`src/lib/views/${
|
|
32293
|
-
|
|
32370
|
+
if (weaver.features.container) {
|
|
32371
|
+
files[`src/lib/views/${weaver.id}-canvas-view.ts`] = childViewFile(
|
|
32372
|
+
weaver,
|
|
32294
32373
|
"canvas",
|
|
32295
|
-
`${
|
|
32374
|
+
`${weaver.className}CanvasView`
|
|
32296
32375
|
);
|
|
32297
|
-
files[`src/lib/views/${
|
|
32298
|
-
|
|
32376
|
+
files[`src/lib/views/${weaver.id}-canvas-view.html`] = childViewTemplateFile(
|
|
32377
|
+
weaver,
|
|
32299
32378
|
"Canvas"
|
|
32300
32379
|
);
|
|
32301
|
-
files[`src/lib/views/${
|
|
32302
|
-
|
|
32380
|
+
files[`src/lib/views/${weaver.id}-details-view.ts`] = childViewFile(
|
|
32381
|
+
weaver,
|
|
32303
32382
|
"details",
|
|
32304
|
-
`${
|
|
32383
|
+
`${weaver.className}DetailsView`
|
|
32305
32384
|
);
|
|
32306
|
-
files[`src/lib/views/${
|
|
32307
|
-
|
|
32385
|
+
files[`src/lib/views/${weaver.id}-details-view.html`] = childViewTemplateFile(
|
|
32386
|
+
weaver,
|
|
32308
32387
|
"Details"
|
|
32309
32388
|
);
|
|
32310
32389
|
} else {
|
|
32311
|
-
files[`src/lib/views/${
|
|
32312
|
-
files[`src/lib/views/${
|
|
32390
|
+
files[`src/lib/views/${weaver.id}-view.ts`] = viewFile(weaver);
|
|
32391
|
+
files[`src/lib/views/${weaver.id}-view.html`] = viewTemplateFile(weaver);
|
|
32313
32392
|
}
|
|
32314
|
-
if (
|
|
32315
|
-
files[`src/lib/dialogs/${
|
|
32316
|
-
files[`src/lib/dialogs/${
|
|
32393
|
+
if (weaver.features.about) {
|
|
32394
|
+
files[`src/lib/dialogs/${weaver.id}-about-dialog.ts`] = aboutDialogFile(weaver);
|
|
32395
|
+
files[`src/lib/dialogs/${weaver.id}-about-dialog.html`] = aboutDialogTemplateFile(weaver);
|
|
32317
32396
|
}
|
|
32318
|
-
if (
|
|
32319
|
-
Object.assign(files, agentFiles(
|
|
32397
|
+
if (weaver.features.agent) {
|
|
32398
|
+
Object.assign(files, agentFiles(weaver));
|
|
32320
32399
|
}
|
|
32321
|
-
if (
|
|
32322
|
-
files[`src/lib/plugin/${
|
|
32400
|
+
if (weaver.features.spec) {
|
|
32401
|
+
files[`src/lib/plugin/${weaver.id}.plugin.spec.ts`] = specFile2(weaver);
|
|
32323
32402
|
}
|
|
32324
32403
|
return files;
|
|
32325
32404
|
}
|
|
@@ -32328,16 +32407,16 @@ var angularWeaver = {
|
|
|
32328
32407
|
// ../devkit/src/recipes/frame-plugin/recipe.ts
|
|
32329
32408
|
function resolveFramePluginInput(input) {
|
|
32330
32409
|
if (!isKebabId(input.id)) {
|
|
32331
|
-
throw new Error(`
|
|
32410
|
+
throw new Error(`Frame plugin id must be kebab-case (e.g. "notes"); got "${input.id}".`);
|
|
32332
32411
|
}
|
|
32333
32412
|
return { id: input.id, name: input.name?.trim() || toTitleCase(input.id) };
|
|
32334
32413
|
}
|
|
32335
|
-
function pluginHtml(
|
|
32414
|
+
function pluginHtml(plugin) {
|
|
32336
32415
|
return `<!doctype html>
|
|
32337
32416
|
<html lang="en">
|
|
32338
32417
|
<head>
|
|
32339
32418
|
<meta charset="utf-8" />
|
|
32340
|
-
<title>${
|
|
32419
|
+
<title>${plugin.name} \u2014 frame plugin (logic)</title>
|
|
32341
32420
|
</head>
|
|
32342
32421
|
<body>
|
|
32343
32422
|
<script src="/frame-kit/penpal.global.js"></script>
|
|
@@ -32346,7 +32425,7 @@ function pluginHtml(p) {
|
|
|
32346
32425
|
</html>
|
|
32347
32426
|
`;
|
|
32348
32427
|
}
|
|
32349
|
-
function pluginJs(
|
|
32428
|
+
function pluginJs(plugin) {
|
|
32350
32429
|
return `(function () {
|
|
32351
32430
|
const Penpal = globalThis.Penpal;
|
|
32352
32431
|
const messenger = new Penpal.WindowMessenger({
|
|
@@ -32358,27 +32437,27 @@ function pluginJs(p) {
|
|
|
32358
32437
|
connection.promise
|
|
32359
32438
|
.then(function (ctx) {
|
|
32360
32439
|
return Promise.all([
|
|
32361
|
-
ctx.toast({ message: '${
|
|
32440
|
+
ctx.toast({ message: '${plugin.name} ready', kind: 'success', timeoutMs: 4000 }),
|
|
32362
32441
|
ctx.registerSurface({
|
|
32363
|
-
id: '${
|
|
32364
|
-
title: '${
|
|
32365
|
-
iframe: '/${
|
|
32366
|
-
routable: { path: '${
|
|
32442
|
+
id: '${plugin.id}.view',
|
|
32443
|
+
title: '${plugin.name}',
|
|
32444
|
+
iframe: '/${plugin.id}/view.html',
|
|
32445
|
+
routable: { path: '${plugin.id}', titleIsLiteral: true },
|
|
32367
32446
|
}),
|
|
32368
32447
|
]);
|
|
32369
32448
|
})
|
|
32370
32449
|
.catch(function (error) {
|
|
32371
|
-
console.error('[${
|
|
32450
|
+
console.error('[${plugin.id}] frame plugin failed', error);
|
|
32372
32451
|
});
|
|
32373
32452
|
})();
|
|
32374
32453
|
`;
|
|
32375
32454
|
}
|
|
32376
|
-
function viewHtml(
|
|
32455
|
+
function viewHtml(plugin) {
|
|
32377
32456
|
return `<!doctype html>
|
|
32378
32457
|
<html lang="en">
|
|
32379
32458
|
<head>
|
|
32380
32459
|
<meta charset="utf-8" />
|
|
32381
|
-
<title>${
|
|
32460
|
+
<title>${plugin.name}</title>
|
|
32382
32461
|
<link rel="stylesheet" href="/frame-kit/lw-frame.css" />
|
|
32383
32462
|
<style>
|
|
32384
32463
|
body {
|
|
@@ -32394,7 +32473,7 @@ function viewHtml(p) {
|
|
|
32394
32473
|
</head>
|
|
32395
32474
|
<body>
|
|
32396
32475
|
<div class="wrap">
|
|
32397
|
-
<h1>${
|
|
32476
|
+
<h1>${plugin.name}</h1>
|
|
32398
32477
|
<p>
|
|
32399
32478
|
Your sandboxed surface. It runs isolated in its own iframe, so its body can be built with
|
|
32400
32479
|
any framework (React, Vue, Svelte, vanilla). The frame UI kit (served by the distribution
|
|
@@ -32422,16 +32501,16 @@ function viewHtml(p) {
|
|
|
32422
32501
|
</html>
|
|
32423
32502
|
`;
|
|
32424
32503
|
}
|
|
32425
|
-
function readme(
|
|
32504
|
+
function readme(plugin) {
|
|
32426
32505
|
return [
|
|
32427
|
-
`# ${
|
|
32506
|
+
`# ${plugin.name} \u2014 frame plugin`,
|
|
32428
32507
|
"",
|
|
32429
32508
|
`A framework-agnostic LoomWeaver plugin: it runs in an isolated \`<iframe sandbox>\` and`,
|
|
32430
32509
|
`receives \`ctx\` over Penpal RPC through the same default-deny broker a trusted plugin uses.`,
|
|
32431
32510
|
"",
|
|
32432
32511
|
"## Serve it + wire it into a distribution",
|
|
32433
32512
|
"",
|
|
32434
|
-
`1. Put these files under the distribution's static dir, e.g. \`public/${
|
|
32513
|
+
`1. Put these files under the distribution's static dir, e.g. \`public/${plugin.id}/\`. The plugin`,
|
|
32435
32514
|
` references the **frame UI kit** (\`@loomweaver/frame-kit\`) at \`/frame-kit/\` \u2014`,
|
|
32436
32515
|
` the distribution serves it via an assets glob (generated distributions already do):`,
|
|
32437
32516
|
"",
|
|
@@ -32442,43 +32521,40 @@ function readme(p) {
|
|
|
32442
32521
|
`2. Register + grant it in the composition root:`,
|
|
32443
32522
|
"",
|
|
32444
32523
|
" ```ts",
|
|
32445
|
-
` provideFramePlugins({ id: '${
|
|
32446
|
-
` provideCapabilityGrants({ '${
|
|
32524
|
+
` provideFramePlugins({ id: '${plugin.id}', entryUrl: '/${plugin.id}/plugin.html', capabilities: ['contributions', 'ui'] });`,
|
|
32525
|
+
` provideCapabilityGrants({ '${plugin.id}': ['contributions', 'ui'] });`,
|
|
32447
32526
|
" ```",
|
|
32448
32527
|
"",
|
|
32449
|
-
`The surface is routable at \`/${
|
|
32528
|
+
`The surface is routable at \`/${plugin.id}\`. Replace \`view.html\` with your own UI in any framework.`,
|
|
32450
32529
|
""
|
|
32451
32530
|
].join("\n");
|
|
32452
32531
|
}
|
|
32453
32532
|
var framePlugin = {
|
|
32454
32533
|
id: "frame-plugin",
|
|
32455
32534
|
build(input) {
|
|
32456
|
-
const
|
|
32535
|
+
const plugin = resolveFramePluginInput(input);
|
|
32457
32536
|
return {
|
|
32458
|
-
"plugin.html": pluginHtml(
|
|
32459
|
-
"plugin.js": pluginJs(
|
|
32460
|
-
"view.html": viewHtml(
|
|
32461
|
-
"README.md": readme(
|
|
32537
|
+
"plugin.html": pluginHtml(plugin),
|
|
32538
|
+
"plugin.js": pluginJs(plugin),
|
|
32539
|
+
"view.html": viewHtml(plugin),
|
|
32540
|
+
"README.md": readme(plugin)
|
|
32462
32541
|
};
|
|
32463
32542
|
}
|
|
32464
32543
|
};
|
|
32465
32544
|
|
|
32466
|
-
// ../devkit/src/recipes/platform-version.ts
|
|
32467
|
-
var PLATFORM_VERSION = "0.14.1";
|
|
32468
|
-
|
|
32469
32545
|
// ../devkit/src/recipes/angular-distribution/amendments.ts
|
|
32470
32546
|
var DISTRIBUTION_INITIAL_BUDGET = {
|
|
32471
32547
|
warning: "1.5MB",
|
|
32472
32548
|
error: "2MB"
|
|
32473
32549
|
};
|
|
32474
|
-
function distributionAmendments(
|
|
32550
|
+
function distributionAmendments(distribution) {
|
|
32475
32551
|
return [
|
|
32476
32552
|
{
|
|
32477
32553
|
kind: "package",
|
|
32478
32554
|
name: "@loomweaver/frame-kit",
|
|
32479
32555
|
version: `^${PLATFORM_VERSION}`
|
|
32480
32556
|
},
|
|
32481
|
-
...
|
|
32557
|
+
...distribution.styles === "tailwind" ? [
|
|
32482
32558
|
{
|
|
32483
32559
|
kind: "postcss",
|
|
32484
32560
|
file: ".postcssrc.json",
|
|
@@ -32510,26 +32586,13 @@ function distributionAmendments(d) {
|
|
|
32510
32586
|
];
|
|
32511
32587
|
}
|
|
32512
32588
|
|
|
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
32589
|
// ../devkit/src/recipes/angular-distribution/logo.ts
|
|
32527
32590
|
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
32591
|
`;
|
|
32529
32592
|
|
|
32530
32593
|
// ../devkit/src/recipes/angular-distribution/readme.ts
|
|
32531
|
-
function stylesNotes(
|
|
32532
|
-
if (
|
|
32594
|
+
function stylesNotes(distribution) {
|
|
32595
|
+
if (distribution.styles === "precompiled") {
|
|
32533
32596
|
return [
|
|
32534
32597
|
"`src/styles.css` imports the stylesheet **we** compiled \u2014 tokens, the `.lw-*` class contracts",
|
|
32535
32598
|
"and every utility the shell's own templates use, 67 KB minified and 11 KB over the wire. There",
|
|
@@ -32567,9 +32630,9 @@ function stylesNotes(d) {
|
|
|
32567
32630
|
"Bootstrap, Bulma or hand-written CSS."
|
|
32568
32631
|
];
|
|
32569
32632
|
}
|
|
32570
|
-
function readme2(
|
|
32633
|
+
function readme2(distribution) {
|
|
32571
32634
|
return [
|
|
32572
|
-
`# ${
|
|
32635
|
+
`# ${distribution.title} \u2014 a LoomWeaver distribution`,
|
|
32573
32636
|
"",
|
|
32574
32637
|
`The composition root that assembles the platform into a shippable product. It renders the bare`,
|
|
32575
32638
|
`shell out of the box; add your weavers and branding below. Your own README is untouched \u2014 the`,
|
|
@@ -32577,29 +32640,23 @@ function readme2(d) {
|
|
|
32577
32640
|
"",
|
|
32578
32641
|
"## Run it",
|
|
32579
32642
|
"",
|
|
32580
|
-
"In an Nx workspace (the generator wired the project):",
|
|
32581
|
-
"",
|
|
32582
32643
|
"```sh",
|
|
32583
|
-
`nx serve ${
|
|
32644
|
+
`nx serve ${distribution.name} # in an Nx workspace`,
|
|
32645
|
+
"ng serve # in an Angular CLI application",
|
|
32584
32646
|
"```",
|
|
32585
32647
|
"",
|
|
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.",
|
|
32648
|
+
"The files keep Angular's own shape: `main.ts` bootstraps `App`, `App` renders `<lw-shell />`, and",
|
|
32649
|
+
"everything this product is made of lives in `app.config.ts`. Nothing of the generated app is",
|
|
32650
|
+
"deleted. Over an existing application `--force` replaces exactly the files above, all of them",
|
|
32651
|
+
"bootstrap wiring and none of them content you authored. Whether the build around them was wired",
|
|
32652
|
+
'as well depends on how they arrived; see "Build wiring" below.',
|
|
32593
32653
|
"",
|
|
32594
32654
|
"Two leftovers from `ng new` are no longer referenced and can go: `src/app/app.routes.ts` (the",
|
|
32595
32655
|
"shell owns content routing via `provideShellRouter()`) and `src/app/app.css`. The starter test",
|
|
32596
32656
|
"`src/app/app.spec.ts` was replaced by one that boots the shell with this composition root, so",
|
|
32597
32657
|
"`ng test` is green as generated; test your own components beside it.",
|
|
32598
32658
|
"",
|
|
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.",
|
|
32659
|
+
...UNTAGGED_NOTE,
|
|
32603
32660
|
"",
|
|
32604
32661
|
"## Compose weavers + branding (in `src/app/app.config.ts`)",
|
|
32605
32662
|
"",
|
|
@@ -32647,7 +32704,7 @@ function readme2(d) {
|
|
|
32647
32704
|
"",
|
|
32648
32705
|
"```ts",
|
|
32649
32706
|
"provideCommandPaletteEntry(); // top bar, end slot, order 5",
|
|
32650
|
-
|
|
32707
|
+
`provideQuickOpenEntry({ bar: '${TOP_BAR_REGION}', order: 4 }); // \u2026or put it wherever you want`,
|
|
32651
32708
|
"```",
|
|
32652
32709
|
"",
|
|
32653
32710
|
"A badge never outlives what it opens. Drop the search and its badge goes with it; the same",
|
|
@@ -32680,13 +32737,21 @@ function readme2(d) {
|
|
|
32680
32737
|
"",
|
|
32681
32738
|
"## Styles",
|
|
32682
32739
|
"",
|
|
32683
|
-
...stylesNotes(
|
|
32740
|
+
...stylesNotes(distribution),
|
|
32684
32741
|
"",
|
|
32685
32742
|
"## Build wiring",
|
|
32686
32743
|
"",
|
|
32687
|
-
"The
|
|
32688
|
-
"
|
|
32689
|
-
"
|
|
32744
|
+
"The build target needs the stylesheet, three asset globs, the service worker and two production",
|
|
32745
|
+
"settings. Who added them depends on how these files arrived:",
|
|
32746
|
+
"",
|
|
32747
|
+
"- **The Nx generator** wrote them into the project's `project.json`. Composing over an existing",
|
|
32748
|
+
" project with `--force`, it merged them in value by value: every value the project already had",
|
|
32749
|
+
" stays, and what the workbench needs is added beside it.",
|
|
32750
|
+
"- **The CLI inside an Angular CLI application** added what was missing to the build target in",
|
|
32751
|
+
" `angular.json` and listed each line it added. Anything you had already set was left as you set",
|
|
32752
|
+
" it.",
|
|
32753
|
+
"- **The MCP server**, and the CLI anywhere else, cannot reach your build configuration, so they",
|
|
32754
|
+
" returned these as steps still to do by hand.",
|
|
32690
32755
|
"",
|
|
32691
32756
|
"What each is for, so that nobody removes one as clutter. The **`@loomweaver/shell/i18n` glob**",
|
|
32692
32757
|
"serves the strings the shell fetches at runtime; without it every label in the chrome renders as",
|
|
@@ -32746,7 +32811,7 @@ function readme2(d) {
|
|
|
32746
32811
|
}
|
|
32747
32812
|
|
|
32748
32813
|
// ../devkit/src/recipes/angular-distribution/recipe.ts
|
|
32749
|
-
var
|
|
32814
|
+
var DISTRIBUTION_STYLES = ["tailwind", "precompiled"];
|
|
32750
32815
|
function resolveDistributionInput(input) {
|
|
32751
32816
|
if (!isKebabId(input.name)) {
|
|
32752
32817
|
throw new Error(
|
|
@@ -32754,9 +32819,9 @@ function resolveDistributionInput(input) {
|
|
|
32754
32819
|
);
|
|
32755
32820
|
}
|
|
32756
32821
|
const styles = input.styles ?? "tailwind";
|
|
32757
|
-
if (!
|
|
32822
|
+
if (!DISTRIBUTION_STYLES.includes(styles)) {
|
|
32758
32823
|
throw new Error(
|
|
32759
|
-
`Unknown styles option "${styles}"; expected one of ${
|
|
32824
|
+
`Unknown styles option "${styles}"; expected one of ${DISTRIBUTION_STYLES.join(", ")}.`
|
|
32760
32825
|
);
|
|
32761
32826
|
}
|
|
32762
32827
|
const directory = input.directory === void 0 ? `apps/${input.name}` : input.directory.trim();
|
|
@@ -32777,7 +32842,7 @@ import { App } from './app/app';
|
|
|
32777
32842
|
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
|
|
32778
32843
|
`;
|
|
32779
32844
|
}
|
|
32780
|
-
function appConfigTs(
|
|
32845
|
+
function appConfigTs(distribution) {
|
|
32781
32846
|
return `import { ApplicationConfig } from '@angular/core';
|
|
32782
32847
|
import {
|
|
32783
32848
|
provideCommandPaletteEntry,
|
|
@@ -32790,8 +32855,8 @@ import {
|
|
|
32790
32855
|
import { provideProductIdentity } from '@loomweaver/plugin-sdk';
|
|
32791
32856
|
|
|
32792
32857
|
/* 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
|
-
'
|
|
32858
|
+
weaver names but this layout omits renders nothing \u2014 silently. '${RAIL_REGION}' (rail) and
|
|
32859
|
+
'${STATUS_BAR_REGION}' (bar) are what the scaffolded weaver targets. */
|
|
32795
32860
|
export const layout: ShellLayout = {
|
|
32796
32861
|
regions: [
|
|
32797
32862
|
${renderRegions(" ".repeat(4))}
|
|
@@ -32813,7 +32878,7 @@ export const appConfig: ApplicationConfig = {
|
|
|
32813
32878
|
provideCommandPaletteEntry(),
|
|
32814
32879
|
provideQuickOpenEntry(),
|
|
32815
32880
|
provideProductIdentity({
|
|
32816
|
-
name: '${
|
|
32881
|
+
name: '${distribution.title}',
|
|
32817
32882
|
tagline: 'Built on LoomWeaver',
|
|
32818
32883
|
logoUrl: 'logo.svg',
|
|
32819
32884
|
}),
|
|
@@ -32846,7 +32911,7 @@ function appConfigSpec() {
|
|
|
32846
32911
|
describe('layout', () => {
|
|
32847
32912
|
it('declares the regions contributions target', () => {
|
|
32848
32913
|
const ids = layout.regions.map((region) => region.id);
|
|
32849
|
-
for (const id of ['
|
|
32914
|
+
for (const id of ['${RAIL_REGION}', '${STATUS_BAR_REGION}', '${CONTENT_REGION}']) {
|
|
32850
32915
|
expect(ids).toContain(id);
|
|
32851
32916
|
}
|
|
32852
32917
|
});
|
|
@@ -32878,12 +32943,12 @@ describe('App', () => {
|
|
|
32878
32943
|
});
|
|
32879
32944
|
`;
|
|
32880
32945
|
}
|
|
32881
|
-
function indexHtml(
|
|
32946
|
+
function indexHtml(distribution) {
|
|
32882
32947
|
return `<!DOCTYPE html>
|
|
32883
32948
|
<html lang="en">
|
|
32884
32949
|
<head>
|
|
32885
32950
|
<meta charset="utf-8" />
|
|
32886
|
-
<title>${
|
|
32951
|
+
<title>${distribution.title}</title>
|
|
32887
32952
|
<base href="/" />
|
|
32888
32953
|
<meta
|
|
32889
32954
|
http-equiv="Content-Security-Policy"
|
|
@@ -32921,7 +32986,7 @@ function precompiledCss() {
|
|
|
32921
32986
|
@import '@loomweaver/shell/styles/shell.css';
|
|
32922
32987
|
`;
|
|
32923
32988
|
}
|
|
32924
|
-
function tailwindCss(
|
|
32989
|
+
function tailwindCss(distribution) {
|
|
32925
32990
|
return `@import 'tailwindcss';
|
|
32926
32991
|
|
|
32927
32992
|
/* LoomWeaver design tokens + theme (light/dark, brand colors). Every @import has to precede the
|
|
@@ -32934,12 +32999,12 @@ function tailwindCss(d) {
|
|
|
32934
32999
|
/* Generate the utility classes the shell (and your own components) use. The @source path must
|
|
32935
33000
|
reach your workspace's node_modules FROM THIS FILE \u2014 adjust the ../ hops if this project does
|
|
32936
33001
|
not sit at that depth, or Tailwind silently emits none of the shell's classes. */
|
|
32937
|
-
@source '${
|
|
33002
|
+
@source '${distribution.nodeModulesFromSrc}/@loomweaver/shell';
|
|
32938
33003
|
@source './';
|
|
32939
33004
|
`;
|
|
32940
33005
|
}
|
|
32941
|
-
function stylesCss(
|
|
32942
|
-
return
|
|
33006
|
+
function stylesCss(distribution) {
|
|
33007
|
+
return distribution.styles === "precompiled" ? precompiledCss() : tailwindCss(distribution);
|
|
32943
33008
|
}
|
|
32944
33009
|
function ngswConfig() {
|
|
32945
33010
|
return JSON.stringify(
|
|
@@ -32978,12 +33043,12 @@ function ngswConfig() {
|
|
|
32978
33043
|
2
|
|
32979
33044
|
);
|
|
32980
33045
|
}
|
|
32981
|
-
function manifest(
|
|
33046
|
+
function manifest(distribution) {
|
|
32982
33047
|
return JSON.stringify(
|
|
32983
33048
|
{
|
|
32984
|
-
name:
|
|
32985
|
-
short_name:
|
|
32986
|
-
description: `${
|
|
33049
|
+
name: distribution.title,
|
|
33050
|
+
short_name: distribution.title,
|
|
33051
|
+
description: `${distribution.title} \u2014 a LoomWeaver distribution.`,
|
|
32987
33052
|
start_url: "/",
|
|
32988
33053
|
display: "standalone",
|
|
32989
33054
|
background_color: "#ffffff",
|
|
@@ -33000,22 +33065,22 @@ var angularDistribution = {
|
|
|
33000
33065
|
return distributionAmendments(resolveDistributionInput(input));
|
|
33001
33066
|
},
|
|
33002
33067
|
build(input) {
|
|
33003
|
-
const
|
|
33068
|
+
const distribution = resolveDistributionInput(input);
|
|
33004
33069
|
return {
|
|
33005
33070
|
"src/main.ts": mainTs(),
|
|
33006
|
-
"src/app/app.config.ts": appConfigTs(
|
|
33007
|
-
...
|
|
33071
|
+
"src/app/app.config.ts": appConfigTs(distribution),
|
|
33072
|
+
...distribution.withTests && {
|
|
33008
33073
|
"src/app/app.config.spec.ts": appConfigSpec(),
|
|
33009
33074
|
"src/app/app.spec.ts": appSpec()
|
|
33010
33075
|
},
|
|
33011
33076
|
"src/app/app.ts": appTs(),
|
|
33012
33077
|
"src/app/app.html": appHtml(),
|
|
33013
|
-
"src/index.html": indexHtml(
|
|
33014
|
-
"src/styles.css": stylesCss(
|
|
33078
|
+
"src/index.html": indexHtml(distribution),
|
|
33079
|
+
"src/styles.css": stylesCss(distribution),
|
|
33015
33080
|
"public/logo.svg": PLACEHOLDER_LOGO_SVG,
|
|
33016
|
-
"public/manifest.webmanifest": manifest(
|
|
33081
|
+
"public/manifest.webmanifest": manifest(distribution) + "\n",
|
|
33017
33082
|
"ngsw-config.json": ngswConfig() + "\n",
|
|
33018
|
-
"LOOMWEAVER.md": readme2(
|
|
33083
|
+
"LOOMWEAVER.md": readme2(distribution)
|
|
33019
33084
|
};
|
|
33020
33085
|
}
|
|
33021
33086
|
};
|
|
@@ -33033,12 +33098,12 @@ function resolveAuthSourceInput(input) {
|
|
|
33033
33098
|
bare: input.bare ?? false
|
|
33034
33099
|
};
|
|
33035
33100
|
}
|
|
33036
|
-
function sessionPluginSymbol(
|
|
33037
|
-
return `${
|
|
33101
|
+
function sessionPluginSymbol(source) {
|
|
33102
|
+
return `${source.propertyName}SessionPlugin`;
|
|
33038
33103
|
}
|
|
33039
|
-
function moduleFile(
|
|
33104
|
+
function moduleFile(source) {
|
|
33040
33105
|
return `// Provider-neutral AuthSource. LoomWeaver owns no authentication \u2014 it only reacts to
|
|
33041
|
-
// a session snapshot. Wire it with: provideAuthSource(() => ${
|
|
33106
|
+
// a session snapshot. Wire it with: provideAuthSource(() => ${source.propertyName}AuthSource()).
|
|
33042
33107
|
// Replace the dev switcher below by mapping your product's real session onto an AuthSnapshot.
|
|
33043
33108
|
import { signal, Signal } from '@angular/core';
|
|
33044
33109
|
import { ANONYMOUS, AuthSnapshot } from '@loomweaver/plugin-sdk';
|
|
@@ -33059,52 +33124,40 @@ const ADMIN: AuthSnapshot = {
|
|
|
33059
33124
|
|
|
33060
33125
|
const state = signal<AuthSnapshot>(ANONYMOUS);
|
|
33061
33126
|
|
|
33062
|
-
export function ${
|
|
33127
|
+
export function ${source.propertyName}AuthSource(): Signal<AuthSnapshot> {
|
|
33063
33128
|
return state.asReadonly();
|
|
33064
33129
|
}
|
|
33065
33130
|
|
|
33066
|
-
export function
|
|
33067
|
-
|
|
33068
|
-
|
|
33069
|
-
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33131
|
+
export function signIn${source.className}User(): void {
|
|
33132
|
+
state.set(USER);
|
|
33133
|
+
}
|
|
33134
|
+
|
|
33135
|
+
export function switch${source.className}Account(): void {
|
|
33136
|
+
state.set(state().roles.includes('admin') ? USER : ADMIN);
|
|
33137
|
+
}
|
|
33138
|
+
|
|
33139
|
+
export function signOut${source.className}User(): void {
|
|
33140
|
+
state.set(ANONYMOUS);
|
|
33074
33141
|
}
|
|
33075
33142
|
`;
|
|
33076
33143
|
}
|
|
33077
|
-
function pluginFile2(
|
|
33144
|
+
function pluginFile2(source) {
|
|
33078
33145
|
return `// The verbs a user needs to operate the stand-in session: sign in, switch the account, sign
|
|
33079
33146
|
// out, from a rail item whose menu carries them. This is presentation for a product that has no
|
|
33080
33147
|
// backend yet; nothing here protects anything. Delete it once your own session arrives.
|
|
33081
33148
|
import type { Disposable, Plugin, PluginContext } from '@loomweaver/plugin-sdk';
|
|
33082
|
-
import {
|
|
33149
|
+
import {
|
|
33150
|
+
${source.propertyName}AuthSource,
|
|
33151
|
+
signIn${source.className}User,
|
|
33152
|
+
signOut${source.className}User,
|
|
33153
|
+
switch${source.className}Account,
|
|
33154
|
+
} from './${source.name}-auth-source';
|
|
33083
33155
|
|
|
33084
|
-
const MENU = '
|
|
33085
|
-
const snapshot = ${
|
|
33156
|
+
const MENU = '${SESSION_PLUGIN_ID}.account/menu';
|
|
33157
|
+
const snapshot = ${source.propertyName}AuthSource();
|
|
33086
33158
|
|
|
33087
33159
|
let drawn: Disposable[] = [];
|
|
33088
33160
|
|
|
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
33161
|
function initialsOf(name: string): string {
|
|
33109
33162
|
return name
|
|
33110
33163
|
.split(' ')
|
|
@@ -33123,7 +33176,7 @@ function draw(ctx: PluginContext): void {
|
|
|
33123
33176
|
drawn = [
|
|
33124
33177
|
ctx.registerRailItem({
|
|
33125
33178
|
id: 'session.account',
|
|
33126
|
-
rail: '
|
|
33179
|
+
rail: '${RAIL_REGION}',
|
|
33127
33180
|
icon: 'account',
|
|
33128
33181
|
title: current.authenticated ? name : 'session.signIn',
|
|
33129
33182
|
anchor: 'bottom',
|
|
@@ -33168,10 +33221,10 @@ function draw(ctx: PluginContext): void {
|
|
|
33168
33221
|
];
|
|
33169
33222
|
}
|
|
33170
33223
|
|
|
33171
|
-
export const ${sessionPluginSymbol(
|
|
33224
|
+
export const ${sessionPluginSymbol(source)}: Plugin = {
|
|
33172
33225
|
manifest: { id: 'session', name: 'Account', capabilities: ['contributions'] },
|
|
33173
33226
|
activate(ctx) {
|
|
33174
|
-
const
|
|
33227
|
+
const andRedraw = (step: () => void) => () => {
|
|
33175
33228
|
step();
|
|
33176
33229
|
draw(ctx);
|
|
33177
33230
|
};
|
|
@@ -33180,21 +33233,21 @@ export const ${sessionPluginSymbol(a)}: Plugin = {
|
|
|
33180
33233
|
title: 'session.signIn',
|
|
33181
33234
|
icon: 'account',
|
|
33182
33235
|
access: { authenticated: false },
|
|
33183
|
-
run:
|
|
33236
|
+
run: andRedraw(signIn${source.className}User),
|
|
33184
33237
|
});
|
|
33185
33238
|
ctx.registerCommand({
|
|
33186
33239
|
id: 'session.switchAccount',
|
|
33187
33240
|
title: 'session.switchAccount',
|
|
33188
33241
|
icon: 'account',
|
|
33189
33242
|
access: { authenticated: true },
|
|
33190
|
-
run:
|
|
33243
|
+
run: andRedraw(switch${source.className}Account),
|
|
33191
33244
|
});
|
|
33192
33245
|
ctx.registerCommand({
|
|
33193
33246
|
id: 'session.signOut',
|
|
33194
33247
|
title: 'session.signOut',
|
|
33195
33248
|
icon: 'signOut',
|
|
33196
33249
|
access: { authenticated: true },
|
|
33197
|
-
run:
|
|
33250
|
+
run: andRedraw(signOut${source.className}User),
|
|
33198
33251
|
});
|
|
33199
33252
|
draw(ctx);
|
|
33200
33253
|
},
|
|
@@ -33207,9 +33260,14 @@ export const ${sessionPluginSymbol(a)}: Plugin = {
|
|
|
33207
33260
|
};
|
|
33208
33261
|
`;
|
|
33209
33262
|
}
|
|
33210
|
-
function indexFile2(
|
|
33211
|
-
return `export {
|
|
33212
|
-
|
|
33263
|
+
function indexFile2(source) {
|
|
33264
|
+
return `export {
|
|
33265
|
+
${source.propertyName}AuthSource,
|
|
33266
|
+
signIn${source.className}User,
|
|
33267
|
+
signOut${source.className}User,
|
|
33268
|
+
switch${source.className}Account,
|
|
33269
|
+
} from './${source.name}-auth-source';
|
|
33270
|
+
export { ${sessionPluginSymbol(source)} } from './${source.name}-session.plugin';
|
|
33213
33271
|
`;
|
|
33214
33272
|
}
|
|
33215
33273
|
var I18N = {
|
|
@@ -33231,15 +33289,15 @@ var I18N = {
|
|
|
33231
33289
|
var authSource = {
|
|
33232
33290
|
id: "auth-source",
|
|
33233
33291
|
build(input) {
|
|
33234
|
-
const
|
|
33235
|
-
const
|
|
33236
|
-
if (
|
|
33237
|
-
return
|
|
33292
|
+
const source = resolveAuthSourceInput(input);
|
|
33293
|
+
const moduleFiles = { [`${source.name}-auth-source.ts`]: moduleFile(source) };
|
|
33294
|
+
if (source.bare) {
|
|
33295
|
+
return moduleFiles;
|
|
33238
33296
|
}
|
|
33239
33297
|
return {
|
|
33240
|
-
...
|
|
33241
|
-
[`${
|
|
33242
|
-
"index.ts": indexFile2(
|
|
33298
|
+
...moduleFiles,
|
|
33299
|
+
[`${source.name}-session.plugin.ts`]: pluginFile2(source),
|
|
33300
|
+
"index.ts": indexFile2(source),
|
|
33243
33301
|
"i18n/en.json": `${JSON.stringify(I18N.en, null, 2)}
|
|
33244
33302
|
`,
|
|
33245
33303
|
"i18n/de.json": `${JSON.stringify(I18N.de, null, 2)}
|
|
@@ -33257,14 +33315,14 @@ function resolveSettingsStoreInput(input) {
|
|
|
33257
33315
|
}
|
|
33258
33316
|
return { name: input.name, className: toPascalCase(input.name) };
|
|
33259
33317
|
}
|
|
33260
|
-
function moduleFile2(
|
|
33318
|
+
function moduleFile2(store) {
|
|
33261
33319
|
return `// A backend-backed settings store for the SETTINGS_STORE port. The platform
|
|
33262
33320
|
// ships local defaults; the product persists settings against its own backend. Only genuine
|
|
33263
33321
|
// settings flow through this port \u2014 working state stays on WORKING_STATE_STORE. Wire it with:
|
|
33264
|
-
// provideSettingsStore(new ${
|
|
33322
|
+
// provideSettingsStore(new ${store.className}SettingsStore()).
|
|
33265
33323
|
import { KeyValueStore } from '@loomweaver/shell';
|
|
33266
33324
|
|
|
33267
|
-
export class ${
|
|
33325
|
+
export class ${store.className}SettingsStore implements KeyValueStore {
|
|
33268
33326
|
constructor(private readonly baseUrl = '/api/settings') {}
|
|
33269
33327
|
|
|
33270
33328
|
async get(key: string): Promise<string | undefined> {
|
|
@@ -33292,28 +33350,28 @@ export class ${s.className}SettingsStore implements KeyValueStore {
|
|
|
33292
33350
|
var settingsStore = {
|
|
33293
33351
|
id: "settings-store",
|
|
33294
33352
|
build(input) {
|
|
33295
|
-
const
|
|
33296
|
-
return { [`${
|
|
33353
|
+
const store = resolveSettingsStoreInput(input);
|
|
33354
|
+
return { [`${store.name}-settings-store.ts`]: moduleFile2(store) };
|
|
33297
33355
|
}
|
|
33298
33356
|
};
|
|
33299
33357
|
|
|
33300
33358
|
// ../devkit/src/recipes/theme/recipe.ts
|
|
33301
|
-
var
|
|
33359
|
+
var THEME_PRESETS = ["literal", "bootstrap"];
|
|
33302
33360
|
function resolveThemeInput(input) {
|
|
33303
33361
|
if (!isKebabId(input.name)) {
|
|
33304
33362
|
throw new Error(`Theme name must be kebab-case (e.g. "midnight"); got "${input.name}".`);
|
|
33305
33363
|
}
|
|
33306
33364
|
const preset = input.preset ?? "literal";
|
|
33307
|
-
if (!
|
|
33308
|
-
throw new Error(`Unknown theme preset "${preset}"; expected one of ${
|
|
33365
|
+
if (!THEME_PRESETS.includes(preset)) {
|
|
33366
|
+
throw new Error(`Unknown theme preset "${preset}"; expected one of ${THEME_PRESETS.join(", ")}.`);
|
|
33309
33367
|
}
|
|
33310
33368
|
return { name: input.name, title: toTitleCase(input.name), preset };
|
|
33311
33369
|
}
|
|
33312
|
-
function cssFile(
|
|
33313
|
-
return `/* ${
|
|
33370
|
+
function cssFile(theme2) {
|
|
33371
|
+
return `/* ${theme2.title} theme \u2014 overrides LoomWeaver's --lw-* design tokens.
|
|
33314
33372
|
Import it AFTER the shell theme in your distribution styles.css:
|
|
33315
33373
|
@import '@loomweaver/shell/styles/theme.css';
|
|
33316
|
-
@import './themes/${
|
|
33374
|
+
@import './themes/${theme2.name}.css';
|
|
33317
33375
|
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
33318
33376
|
(Product default < Plugin < Tenant). The --lw-* ladder flips in :root.dark, so override both. */
|
|
33319
33377
|
|
|
@@ -33348,15 +33406,15 @@ function cssFile(t) {
|
|
|
33348
33406
|
}
|
|
33349
33407
|
`;
|
|
33350
33408
|
}
|
|
33351
|
-
function bootstrapFile(
|
|
33352
|
-
return `/* ${
|
|
33409
|
+
function bootstrapFile(theme2) {
|
|
33410
|
+
return `/* ${theme2.title} theme \u2014 maps LoomWeaver's --lw-* design tokens onto Bootstrap 5.3's --bs-*.
|
|
33353
33411
|
Import it AFTER the shell theme in your distribution styles.css, and import Bootstrap itself
|
|
33354
33412
|
INTO A LAYER \u2014 unlayered CSS outranks layered CSS whatever its specificity, so Bootstrap's
|
|
33355
33413
|
Reboot (button { border-radius: 0 }) would otherwise beat our .lw-* component classes:
|
|
33356
33414
|
@layer vendor;
|
|
33357
33415
|
@import 'bootstrap/dist/css/bootstrap.css' layer(vendor);
|
|
33358
33416
|
@import '@loomweaver/shell/styles/shell.css'; (or styles/theme.css if you run Tailwind)
|
|
33359
|
-
@import './themes/${
|
|
33417
|
+
@import './themes/${theme2.name}.css';
|
|
33360
33418
|
Declared in @layer lw-tenant-theme so it beats any plugin's ctx.contributeTheme
|
|
33361
33419
|
(Product default < Plugin < Tenant).
|
|
33362
33420
|
|
|
@@ -33431,9 +33489,9 @@ function bootstrapFile(t) {
|
|
|
33431
33489
|
var theme = {
|
|
33432
33490
|
id: "theme",
|
|
33433
33491
|
build(input) {
|
|
33434
|
-
const
|
|
33435
|
-
const content =
|
|
33436
|
-
return { [`${
|
|
33492
|
+
const theme2 = resolveThemeInput(input);
|
|
33493
|
+
const content = theme2.preset === "bootstrap" ? bootstrapFile(theme2) : cssFile(theme2);
|
|
33494
|
+
return { [`${theme2.name}.css`]: content };
|
|
33437
33495
|
}
|
|
33438
33496
|
};
|
|
33439
33497
|
|
|
@@ -33445,13 +33503,13 @@ function resolveLayoutInput(input) {
|
|
|
33445
33503
|
}
|
|
33446
33504
|
return { name, propertyName: toCamelCase(name) };
|
|
33447
33505
|
}
|
|
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 '
|
|
33506
|
+
function moduleFile3(layout2) {
|
|
33507
|
+
return `// A base layout for the distribution. Pass it to provideLayout(${layout2.propertyName}Layout)
|
|
33508
|
+
// in src/app/app.config.ts. Region ids are what contributions target \u2014 '${RAIL_REGION}' (rail) and '${STATUS_BAR_REGION}' (bar) match
|
|
33451
33509
|
// the devkit weaver defaults, so a scaffolded weaver's rail + bar items land here.
|
|
33452
33510
|
import { ShellLayout } from '@loomweaver/shell';
|
|
33453
33511
|
|
|
33454
|
-
export const ${
|
|
33512
|
+
export const ${layout2.propertyName}Layout: ShellLayout = {
|
|
33455
33513
|
regions: [
|
|
33456
33514
|
${renderRegions(" ".repeat(4))}
|
|
33457
33515
|
],
|
|
@@ -33461,19 +33519,109 @@ ${renderRegions(" ".repeat(4))}
|
|
|
33461
33519
|
var layout = {
|
|
33462
33520
|
id: "layout",
|
|
33463
33521
|
build(input) {
|
|
33464
|
-
const
|
|
33465
|
-
return { [`${
|
|
33522
|
+
const layout2 = resolveLayoutInput(input);
|
|
33523
|
+
return { [`${layout2.name}-layout.ts`]: moduleFile3(layout2) };
|
|
33466
33524
|
}
|
|
33467
33525
|
};
|
|
33468
33526
|
|
|
33527
|
+
// ../devkit/src/lib/scaffolds/scaffold-values.ts
|
|
33528
|
+
function stringValue(values, name) {
|
|
33529
|
+
const value = values[name];
|
|
33530
|
+
return typeof value === "string" ? value : void 0;
|
|
33531
|
+
}
|
|
33532
|
+
function booleanValue(values, name) {
|
|
33533
|
+
const value = values[name];
|
|
33534
|
+
return typeof value === "boolean" ? value : void 0;
|
|
33535
|
+
}
|
|
33536
|
+
var PLACEMENT_OPTIONS = [
|
|
33537
|
+
{
|
|
33538
|
+
name: "directory",
|
|
33539
|
+
type: "string",
|
|
33540
|
+
description: "Project root, relative to the workspace root.",
|
|
33541
|
+
workspaceOnly: true
|
|
33542
|
+
},
|
|
33543
|
+
{
|
|
33544
|
+
name: "tags",
|
|
33545
|
+
type: "string",
|
|
33546
|
+
description: "Comma-separated Nx tags for the project.",
|
|
33547
|
+
workspaceOnly: true
|
|
33548
|
+
},
|
|
33549
|
+
{
|
|
33550
|
+
name: "prefix",
|
|
33551
|
+
type: "string",
|
|
33552
|
+
description: "Selector prefix for generated components and directives.",
|
|
33553
|
+
default: "lw",
|
|
33554
|
+
pattern: KEBAB_ID_PATTERN,
|
|
33555
|
+
workspaceOnly: true
|
|
33556
|
+
},
|
|
33557
|
+
{
|
|
33558
|
+
name: "unitTestRunner",
|
|
33559
|
+
type: "string",
|
|
33560
|
+
description: "Test wiring to emit. 'vitest' uses @nx/angular:unit-test; 'none' emits no test target.",
|
|
33561
|
+
choices: ["vitest", "none"],
|
|
33562
|
+
default: "vitest",
|
|
33563
|
+
workspaceOnly: true
|
|
33564
|
+
}
|
|
33565
|
+
];
|
|
33566
|
+
var APP_OPTION = {
|
|
33567
|
+
name: "app",
|
|
33568
|
+
type: "string",
|
|
33569
|
+
description: "Application to drop into. Inferred when the workspace has exactly one.",
|
|
33570
|
+
workspaceOnly: true
|
|
33571
|
+
};
|
|
33572
|
+
|
|
33573
|
+
// ../devkit/src/recipes/angular-distribution/scaffold.ts
|
|
33574
|
+
function distributionInput(values) {
|
|
33575
|
+
return {
|
|
33576
|
+
name: stringValue(values, "name") ?? "",
|
|
33577
|
+
title: stringValue(values, "title"),
|
|
33578
|
+
directory: stringValue(values, "directory"),
|
|
33579
|
+
styles: stringValue(values, "styles") ?? "tailwind"
|
|
33580
|
+
};
|
|
33581
|
+
}
|
|
33582
|
+
var distributionScaffold = {
|
|
33583
|
+
name: "distribution",
|
|
33584
|
+
summary: "a runnable composition root that boots the shell",
|
|
33585
|
+
options: [
|
|
33586
|
+
{
|
|
33587
|
+
name: "name",
|
|
33588
|
+
type: "string",
|
|
33589
|
+
description: "Distribution name in kebab-case, e.g. 'acme-studio'.",
|
|
33590
|
+
required: true,
|
|
33591
|
+
pattern: KEBAB_ID_PATTERN
|
|
33592
|
+
},
|
|
33593
|
+
{
|
|
33594
|
+
name: "title",
|
|
33595
|
+
type: "string",
|
|
33596
|
+
description: "Product display title. Defaults to a title-cased name."
|
|
33597
|
+
},
|
|
33598
|
+
{
|
|
33599
|
+
name: "styles",
|
|
33600
|
+
type: "string",
|
|
33601
|
+
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.",
|
|
33602
|
+
choices: DISTRIBUTION_STYLES,
|
|
33603
|
+
default: "tailwind"
|
|
33604
|
+
},
|
|
33605
|
+
{
|
|
33606
|
+
name: "force",
|
|
33607
|
+
type: "boolean",
|
|
33608
|
+
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.",
|
|
33609
|
+
workspaceOnly: true
|
|
33610
|
+
},
|
|
33611
|
+
...PLACEMENT_OPTIONS
|
|
33612
|
+
],
|
|
33613
|
+
build: (values) => generate(angularDistribution, distributionInput(values)),
|
|
33614
|
+
amend: (values) => amendments(angularDistribution, distributionInput(values))
|
|
33615
|
+
};
|
|
33616
|
+
|
|
33469
33617
|
// ../devkit/src/recipes/angular-weaver/amendments.ts
|
|
33470
33618
|
function weaverAmendments(input, where) {
|
|
33471
|
-
const
|
|
33472
|
-
const packages =
|
|
33619
|
+
const weaver = resolveWeaverInput(input);
|
|
33620
|
+
const packages = weaver.features.agent ? [
|
|
33473
33621
|
{
|
|
33474
33622
|
kind: "package",
|
|
33475
33623
|
name: "@loomweaver/ag-ui",
|
|
33476
|
-
version: `^${
|
|
33624
|
+
version: `^${PLATFORM_VERSION}`
|
|
33477
33625
|
},
|
|
33478
33626
|
{
|
|
33479
33627
|
kind: "package",
|
|
@@ -33495,25 +33643,145 @@ function weaverAmendments(input, where) {
|
|
|
33495
33643
|
glob: "**/*.json",
|
|
33496
33644
|
input: joinProjectPath(directory, "src/lib/i18n"),
|
|
33497
33645
|
from: "workspace",
|
|
33498
|
-
output: `i18n/${
|
|
33646
|
+
output: `i18n/${weaver.id}`
|
|
33499
33647
|
}
|
|
33500
33648
|
]
|
|
33501
33649
|
},
|
|
33502
33650
|
{ kind: "stylesheet-source", sourceRoot: joinProjectPath(directory, "src") },
|
|
33503
33651
|
{
|
|
33504
33652
|
kind: "compose-plugin",
|
|
33505
|
-
id:
|
|
33506
|
-
symbol: `${
|
|
33507
|
-
capabilities:
|
|
33653
|
+
id: weaver.id,
|
|
33654
|
+
symbol: `${weaver.propertyName}Plugin`,
|
|
33655
|
+
capabilities: weaver.capabilities,
|
|
33508
33656
|
sourceRoot: joinProjectPath(directory, "src")
|
|
33509
33657
|
}
|
|
33510
33658
|
];
|
|
33511
33659
|
}
|
|
33512
33660
|
|
|
33513
|
-
// ../devkit/src/recipes/
|
|
33661
|
+
// ../devkit/src/recipes/angular-weaver/scaffold.ts
|
|
33662
|
+
function weaverInput(values) {
|
|
33663
|
+
return {
|
|
33664
|
+
id: stringValue(values, "id") ?? "",
|
|
33665
|
+
name: stringValue(values, "name"),
|
|
33666
|
+
prefix: stringValue(values, "prefix"),
|
|
33667
|
+
importPath: stringValue(values, "importPath"),
|
|
33668
|
+
features: {
|
|
33669
|
+
command: booleanValue(values, "command"),
|
|
33670
|
+
shortcut: stringValue(values, "shortcut"),
|
|
33671
|
+
menu: stringValue(values, "menu"),
|
|
33672
|
+
barItem: booleanValue(values, "barItem"),
|
|
33673
|
+
settings: booleanValue(values, "settings"),
|
|
33674
|
+
about: booleanValue(values, "about"),
|
|
33675
|
+
instanceable: booleanValue(values, "instanceable"),
|
|
33676
|
+
container: booleanValue(values, "container"),
|
|
33677
|
+
agent: booleanValue(values, "agent"),
|
|
33678
|
+
access: stringValue(values, "access"),
|
|
33679
|
+
spec: booleanValue(values, "spec")
|
|
33680
|
+
}
|
|
33681
|
+
};
|
|
33682
|
+
}
|
|
33683
|
+
var weaverScaffold = {
|
|
33684
|
+
name: "weaver",
|
|
33685
|
+
summary: "an Angular in-process plugin: manifest, surface, rail item, i18n, test",
|
|
33686
|
+
options: [
|
|
33687
|
+
{
|
|
33688
|
+
name: "id",
|
|
33689
|
+
type: "string",
|
|
33690
|
+
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33691
|
+
required: true,
|
|
33692
|
+
pattern: KEBAB_ID_PATTERN
|
|
33693
|
+
},
|
|
33694
|
+
{
|
|
33695
|
+
name: "name",
|
|
33696
|
+
type: "string",
|
|
33697
|
+
description: "Human-readable name. Defaults to a title-cased id."
|
|
33698
|
+
},
|
|
33699
|
+
{
|
|
33700
|
+
name: "command",
|
|
33701
|
+
type: "boolean",
|
|
33702
|
+
description: "Also scaffold a registered command the weaver can trigger.",
|
|
33703
|
+
default: false
|
|
33704
|
+
},
|
|
33705
|
+
{
|
|
33706
|
+
name: "shortcut",
|
|
33707
|
+
type: "string",
|
|
33708
|
+
description: "Keyboard chord for the command, e.g. 'mod+shift+k'. Implies --command."
|
|
33709
|
+
},
|
|
33710
|
+
{
|
|
33711
|
+
name: "menu",
|
|
33712
|
+
type: "string",
|
|
33713
|
+
description: "Hook a menu item into a slot, e.g. 'content/tab/context'. Implies --command."
|
|
33714
|
+
},
|
|
33715
|
+
{
|
|
33716
|
+
name: "barItem",
|
|
33717
|
+
type: "boolean",
|
|
33718
|
+
description: "Also add a status-bar button that triggers the command. Implies --command.",
|
|
33719
|
+
default: false
|
|
33720
|
+
},
|
|
33721
|
+
{
|
|
33722
|
+
name: "settings",
|
|
33723
|
+
type: "boolean",
|
|
33724
|
+
description: "Also scaffold a settings section with signal-backed value owners.",
|
|
33725
|
+
default: false
|
|
33726
|
+
},
|
|
33727
|
+
{
|
|
33728
|
+
name: "about",
|
|
33729
|
+
type: "boolean",
|
|
33730
|
+
description: "Also scaffold an About dialog that reads ctx.host, plus a bottom rail item.",
|
|
33731
|
+
default: false
|
|
33732
|
+
},
|
|
33733
|
+
{
|
|
33734
|
+
name: "instanceable",
|
|
33735
|
+
type: "boolean",
|
|
33736
|
+
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.",
|
|
33737
|
+
default: false
|
|
33738
|
+
},
|
|
33739
|
+
{
|
|
33740
|
+
name: "container",
|
|
33741
|
+
type: "boolean",
|
|
33742
|
+
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.",
|
|
33743
|
+
default: false
|
|
33744
|
+
},
|
|
33745
|
+
{
|
|
33746
|
+
name: "agent",
|
|
33747
|
+
type: "boolean",
|
|
33748
|
+
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.",
|
|
33749
|
+
default: false
|
|
33750
|
+
},
|
|
33751
|
+
{
|
|
33752
|
+
name: "access",
|
|
33753
|
+
type: "string",
|
|
33754
|
+
description: "Auth-gate the surface and rail item: 'authenticated', 'anonymous' or 'role:<name>'."
|
|
33755
|
+
},
|
|
33756
|
+
{
|
|
33757
|
+
name: "spec",
|
|
33758
|
+
type: "boolean",
|
|
33759
|
+
description: "Generate a starter unit test. Use --no-spec to skip it.",
|
|
33760
|
+
default: true
|
|
33761
|
+
},
|
|
33762
|
+
{
|
|
33763
|
+
name: "projectName",
|
|
33764
|
+
type: "string",
|
|
33765
|
+
description: "Nx project name. Defaults to '<id>-weaver'.",
|
|
33766
|
+
workspaceOnly: true
|
|
33767
|
+
},
|
|
33768
|
+
{
|
|
33769
|
+
name: "importPath",
|
|
33770
|
+
type: "string",
|
|
33771
|
+
description: "Import path for the workspace alias. Defaults to the workspace scope plus the project name.",
|
|
33772
|
+
workspaceOnly: true
|
|
33773
|
+
},
|
|
33774
|
+
APP_OPTION,
|
|
33775
|
+
...PLACEMENT_OPTIONS
|
|
33776
|
+
],
|
|
33777
|
+
build: (values) => generate(angularWeaver, weaverInput(values)),
|
|
33778
|
+
amend: (values) => weaverAmendments(weaverInput(values), stringValue(values, "directory"))
|
|
33779
|
+
};
|
|
33780
|
+
|
|
33781
|
+
// ../devkit/src/recipes/auth-source/amendments.ts
|
|
33514
33782
|
function authSourceAmendments(input, where) {
|
|
33515
|
-
const
|
|
33516
|
-
if (
|
|
33783
|
+
const source = resolveAuthSourceInput(input);
|
|
33784
|
+
if (source.bare || where === void 0 || where === "") {
|
|
33517
33785
|
return [];
|
|
33518
33786
|
}
|
|
33519
33787
|
const directory = normalizeProjectRoot(where);
|
|
@@ -33533,14 +33801,14 @@ function authSourceAmendments(input, where) {
|
|
|
33533
33801
|
{
|
|
33534
33802
|
kind: "compose-plugin",
|
|
33535
33803
|
id: SESSION_PLUGIN_ID,
|
|
33536
|
-
symbol: sessionPluginSymbol(
|
|
33804
|
+
symbol: sessionPluginSymbol(source),
|
|
33537
33805
|
capabilities: ["contributions"],
|
|
33538
33806
|
sourceRoot: directory,
|
|
33539
33807
|
providers: [
|
|
33540
33808
|
{
|
|
33541
|
-
line: `provideAuthSource(() => ${
|
|
33809
|
+
line: `provideAuthSource(() => ${source.propertyName}AuthSource()),`,
|
|
33542
33810
|
shell: ["provideAuthSource"],
|
|
33543
|
-
own: [`${
|
|
33811
|
+
own: [`${source.propertyName}AuthSource`],
|
|
33544
33812
|
unless: "provideAuthSource("
|
|
33545
33813
|
},
|
|
33546
33814
|
{
|
|
@@ -33558,325 +33826,135 @@ function authSourceAmendments(input, where) {
|
|
|
33558
33826
|
];
|
|
33559
33827
|
}
|
|
33560
33828
|
|
|
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
|
-
};
|
|
33829
|
+
// ../devkit/src/recipes/auth-source/scaffold.ts
|
|
33830
|
+
function authSourceInput(values) {
|
|
33831
|
+
return { name: stringValue(values, "name") ?? "", bare: booleanValue(values, "bare") };
|
|
33590
33832
|
}
|
|
33833
|
+
var authSourceScaffold = {
|
|
33834
|
+
name: "auth-source",
|
|
33835
|
+
summary: "a stand-in session a user can operate: the AuthSource, the sign-in, switch and sign-out verbs in the rail, composed in",
|
|
33836
|
+
options: [
|
|
33837
|
+
{
|
|
33838
|
+
name: "name",
|
|
33839
|
+
type: "string",
|
|
33840
|
+
description: "Source name in kebab-case, e.g. 'dev'.",
|
|
33841
|
+
required: true,
|
|
33842
|
+
pattern: KEBAB_ID_PATTERN
|
|
33843
|
+
},
|
|
33844
|
+
{
|
|
33845
|
+
name: "bare",
|
|
33846
|
+
type: "boolean",
|
|
33847
|
+
description: "Write the AuthSource alone, without the verbs, for a product that maps a session of its own onto it.",
|
|
33848
|
+
default: false
|
|
33849
|
+
},
|
|
33850
|
+
APP_OPTION,
|
|
33851
|
+
{
|
|
33852
|
+
name: "directory",
|
|
33853
|
+
type: "string",
|
|
33854
|
+
description: "Where the files land, relative to the workspace root.",
|
|
33855
|
+
workspaceOnly: true
|
|
33856
|
+
}
|
|
33857
|
+
],
|
|
33858
|
+
build: (values) => generate(authSource, authSourceInput(values)),
|
|
33859
|
+
amend: (values) => authSourceAmendments(authSourceInput(values), stringValue(values, "directory"))
|
|
33860
|
+
};
|
|
33591
33861
|
|
|
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
|
|
33862
|
+
// ../devkit/src/recipes/frame-plugin/scaffold.ts
|
|
33863
|
+
var framePluginScaffold = {
|
|
33864
|
+
name: "frame-plugin",
|
|
33865
|
+
summary: "a framework-agnostic iframe plugin (Penpal + the frame UI kit)",
|
|
33866
|
+
options: [
|
|
33867
|
+
{
|
|
33868
|
+
name: "id",
|
|
33869
|
+
type: "string",
|
|
33870
|
+
description: "Plugin id in kebab-case, e.g. 'notes'.",
|
|
33871
|
+
required: true,
|
|
33872
|
+
pattern: KEBAB_ID_PATTERN
|
|
33873
|
+
},
|
|
33874
|
+
{
|
|
33875
|
+
name: "name",
|
|
33876
|
+
type: "string",
|
|
33877
|
+
description: "Human-readable name. Defaults to a title-cased id."
|
|
33878
|
+
},
|
|
33879
|
+
APP_OPTION
|
|
33880
|
+
],
|
|
33881
|
+
build: (values) => generate(framePlugin, {
|
|
33882
|
+
id: stringValue(values, "id") ?? "",
|
|
33883
|
+
name: stringValue(values, "name")
|
|
33884
|
+
})
|
|
33637
33885
|
};
|
|
33638
|
-
|
|
33639
|
-
|
|
33640
|
-
|
|
33886
|
+
|
|
33887
|
+
// ../devkit/src/recipes/layout/scaffold.ts
|
|
33888
|
+
var layoutScaffold = {
|
|
33889
|
+
name: "layout",
|
|
33890
|
+
summary: "a ShellLayout with the regions a weaver expects",
|
|
33891
|
+
options: [
|
|
33892
|
+
{
|
|
33893
|
+
name: "name",
|
|
33894
|
+
type: "string",
|
|
33895
|
+
description: "Layout name. Defaults to a base layout."
|
|
33896
|
+
},
|
|
33897
|
+
APP_OPTION
|
|
33898
|
+
],
|
|
33899
|
+
build: (values) => generate(layout, { name: stringValue(values, "name") })
|
|
33900
|
+
};
|
|
33901
|
+
|
|
33902
|
+
// ../devkit/src/recipes/settings-store/scaffold.ts
|
|
33903
|
+
var settingsStoreScaffold = {
|
|
33904
|
+
name: "settings-store",
|
|
33905
|
+
summary: "a settings-store implementation backed by your API",
|
|
33906
|
+
options: [
|
|
33907
|
+
{
|
|
33908
|
+
name: "name",
|
|
33909
|
+
type: "string",
|
|
33910
|
+
description: "Store name in kebab-case, e.g. 'backend'.",
|
|
33911
|
+
required: true,
|
|
33912
|
+
pattern: KEBAB_ID_PATTERN
|
|
33913
|
+
},
|
|
33914
|
+
APP_OPTION
|
|
33915
|
+
],
|
|
33916
|
+
build: (values) => generate(settingsStore, { name: stringValue(values, "name") ?? "" })
|
|
33917
|
+
};
|
|
33918
|
+
|
|
33919
|
+
// ../devkit/src/recipes/theme/scaffold.ts
|
|
33920
|
+
var themeScaffold = {
|
|
33921
|
+
name: "theme",
|
|
33922
|
+
summary: "a token-override stylesheet in @layer lw-tenant-theme",
|
|
33923
|
+
options: [
|
|
33924
|
+
{
|
|
33925
|
+
name: "name",
|
|
33926
|
+
type: "string",
|
|
33927
|
+
description: "Theme name in kebab-case, e.g. 'midnight'.",
|
|
33928
|
+
required: true,
|
|
33929
|
+
pattern: KEBAB_ID_PATTERN
|
|
33930
|
+
},
|
|
33931
|
+
{
|
|
33932
|
+
name: "preset",
|
|
33933
|
+
type: "string",
|
|
33934
|
+
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.",
|
|
33935
|
+
choices: THEME_PRESETS,
|
|
33936
|
+
default: "literal"
|
|
33937
|
+
},
|
|
33938
|
+
APP_OPTION
|
|
33939
|
+
],
|
|
33940
|
+
build: (values) => generate(theme, {
|
|
33941
|
+
name: stringValue(values, "name") ?? "",
|
|
33942
|
+
preset: stringValue(values, "preset") ?? "literal"
|
|
33943
|
+
})
|
|
33944
|
+
};
|
|
33945
|
+
|
|
33946
|
+
// ../devkit/src/lib/scaffolds/scaffolds.ts
|
|
33641
33947
|
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
|
-
}
|
|
33948
|
+
weaverScaffold,
|
|
33949
|
+
framePluginScaffold,
|
|
33950
|
+
distributionScaffold,
|
|
33951
|
+
authSourceScaffold,
|
|
33952
|
+
settingsStoreScaffold,
|
|
33953
|
+
themeScaffold,
|
|
33954
|
+
layoutScaffold
|
|
33877
33955
|
];
|
|
33878
33956
|
|
|
33879
|
-
// ../devkit/src/lib/scaffolds/
|
|
33957
|
+
// ../devkit/src/lib/scaffolds/adapter-options.ts
|
|
33880
33958
|
function portableOptions(scaffold2) {
|
|
33881
33959
|
return scaffold2.options.filter((option) => !option.workspaceOnly);
|
|
33882
33960
|
}
|
|
@@ -33938,41 +34016,50 @@ var CATALOG_ENTRY_KEYS = [
|
|
|
33938
34016
|
"readmeUrl"
|
|
33939
34017
|
];
|
|
33940
34018
|
var SAME_ORIGIN_FIELDS = ["entryUrl", "iconUrl", "readmeUrl"];
|
|
33941
|
-
function
|
|
34019
|
+
function pathOf(index, field) {
|
|
33942
34020
|
return field ? `catalog[${index}].${field}` : `catalog[${index}]`;
|
|
33943
34021
|
}
|
|
33944
34022
|
function isPlainObject3(raw) {
|
|
33945
34023
|
return typeof raw === "object" && raw !== null && !Array.isArray(raw);
|
|
33946
34024
|
}
|
|
33947
|
-
function
|
|
34025
|
+
function requiredUrlFinding(value, index, field) {
|
|
33948
34026
|
if (typeof value !== "string" || value.length === 0) {
|
|
33949
|
-
return
|
|
34027
|
+
return {
|
|
33950
34028
|
level: "error",
|
|
33951
34029
|
code: "catalog.entryUrl",
|
|
33952
|
-
message: `${
|
|
33953
|
-
path:
|
|
33954
|
-
}
|
|
34030
|
+
message: `${pathOf(index, field)} must be a non-empty string; the host drops the whole entry without it.`,
|
|
34031
|
+
path: pathOf(index, field)
|
|
34032
|
+
};
|
|
34033
|
+
}
|
|
34034
|
+
return schemeFinding(value, index, field, "drops the entry");
|
|
34035
|
+
}
|
|
34036
|
+
function optionalUrlFinding(value, index, field) {
|
|
34037
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
34038
|
+
return {
|
|
33955
34039
|
level: "warning",
|
|
33956
34040
|
code: "catalog.url.empty",
|
|
33957
|
-
message: `${
|
|
33958
|
-
path:
|
|
34041
|
+
message: `${pathOf(index, field)} is present but not a non-empty string, so the host ignores it.`,
|
|
34042
|
+
path: pathOf(index, field)
|
|
33959
34043
|
};
|
|
33960
34044
|
}
|
|
34045
|
+
return schemeFinding(value, index, field, "ignores the field");
|
|
34046
|
+
}
|
|
34047
|
+
function schemeFinding(value, index, field, refusal) {
|
|
33961
34048
|
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value)?.[1]?.toLowerCase();
|
|
33962
34049
|
if (scheme && scheme !== "http" && scheme !== "https") {
|
|
33963
34050
|
return {
|
|
33964
34051
|
level: "error",
|
|
33965
34052
|
code: "catalog.url.scheme",
|
|
33966
|
-
message: `${
|
|
33967
|
-
path:
|
|
34053
|
+
message: `${pathOf(index, field)} uses the "${scheme}:" scheme. The host accepts same-origin http(s) URLs only and ${refusal}.`,
|
|
34054
|
+
path: pathOf(index, field)
|
|
33968
34055
|
};
|
|
33969
34056
|
}
|
|
33970
34057
|
if (scheme) {
|
|
33971
34058
|
return {
|
|
33972
34059
|
level: "warning",
|
|
33973
34060
|
code: "catalog.url.absolute",
|
|
33974
|
-
message: `${
|
|
33975
|
-
path:
|
|
34061
|
+
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.`,
|
|
34062
|
+
path: pathOf(index, field)
|
|
33976
34063
|
};
|
|
33977
34064
|
}
|
|
33978
34065
|
return void 0;
|
|
@@ -33983,8 +34070,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
33983
34070
|
{
|
|
33984
34071
|
level: "warning",
|
|
33985
34072
|
code: "catalog.capabilities.missing",
|
|
33986
|
-
message: `${
|
|
33987
|
-
path:
|
|
34073
|
+
message: `${pathOf(index)} declares no capabilities. Accepting the install dialog grants exactly what is declared, so the plugin will be denied everything at runtime.`,
|
|
34074
|
+
path: pathOf(index, "capabilities")
|
|
33988
34075
|
}
|
|
33989
34076
|
];
|
|
33990
34077
|
}
|
|
@@ -33993,8 +34080,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
33993
34080
|
{
|
|
33994
34081
|
level: "error",
|
|
33995
34082
|
code: "catalog.capabilities",
|
|
33996
|
-
message: `${
|
|
33997
|
-
path:
|
|
34083
|
+
message: `${pathOf(index, "capabilities")} must be an array; the host ignores any other shape and grants nothing.`,
|
|
34084
|
+
path: pathOf(index, "capabilities")
|
|
33998
34085
|
}
|
|
33999
34086
|
];
|
|
34000
34087
|
}
|
|
@@ -34004,8 +34091,8 @@ function validateCapabilities2(value, index, known) {
|
|
|
34004
34091
|
findings.push({
|
|
34005
34092
|
level: "error",
|
|
34006
34093
|
code: "catalog.capability.unknown",
|
|
34007
|
-
message: `${
|
|
34008
|
-
path:
|
|
34094
|
+
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(", ")}.`,
|
|
34095
|
+
path: pathOf(index, "capabilities")
|
|
34009
34096
|
});
|
|
34010
34097
|
}
|
|
34011
34098
|
}
|
|
@@ -34017,58 +34104,69 @@ function validateEntry(raw, index, known) {
|
|
|
34017
34104
|
{
|
|
34018
34105
|
level: "error",
|
|
34019
34106
|
code: "catalog.entry",
|
|
34020
|
-
message: `${
|
|
34021
|
-
path:
|
|
34107
|
+
message: `${pathOf(index)} is not an object, so the host drops it.`,
|
|
34108
|
+
path: pathOf(index)
|
|
34022
34109
|
}
|
|
34023
34110
|
];
|
|
34024
34111
|
}
|
|
34025
34112
|
return [
|
|
34026
|
-
...
|
|
34113
|
+
...validateId(raw, index),
|
|
34114
|
+
...validateSameOriginUrls(raw, index),
|
|
34115
|
+
...validateNameAndVersion(raw, index),
|
|
34027
34116
|
...validateCapabilities2(raw["capabilities"], index, known),
|
|
34028
34117
|
...validateEntryMetadata(raw, index),
|
|
34029
34118
|
...validateEntryKeys(raw, index)
|
|
34030
34119
|
];
|
|
34031
34120
|
}
|
|
34032
|
-
function
|
|
34033
|
-
|
|
34034
|
-
|
|
34035
|
-
findings.push({
|
|
34121
|
+
function validateId(raw, index) {
|
|
34122
|
+
return typeof raw["id"] === "string" && raw["id"].length > 0 ? [] : [
|
|
34123
|
+
{
|
|
34036
34124
|
level: "error",
|
|
34037
34125
|
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;
|
|
34126
|
+
message: `${pathOf(index, "id")} must be a non-empty string; the host drops the whole entry without it.`,
|
|
34127
|
+
path: pathOf(index, "id")
|
|
34045
34128
|
}
|
|
34046
|
-
|
|
34129
|
+
];
|
|
34130
|
+
}
|
|
34131
|
+
function validateSameOriginUrls(raw, index) {
|
|
34132
|
+
const findings = [];
|
|
34133
|
+
for (const field of SAME_ORIGIN_FIELDS) {
|
|
34134
|
+
const finding = sameOriginUrlFinding(raw, index, field);
|
|
34047
34135
|
if (finding) {
|
|
34048
34136
|
findings.push(finding);
|
|
34049
34137
|
}
|
|
34050
34138
|
}
|
|
34139
|
+
return findings;
|
|
34140
|
+
}
|
|
34141
|
+
function sameOriginUrlFinding(raw, index, field) {
|
|
34142
|
+
if (field === "entryUrl") {
|
|
34143
|
+
return requiredUrlFinding(raw[field], index, field);
|
|
34144
|
+
}
|
|
34145
|
+
return raw[field] === void 0 ? void 0 : optionalUrlFinding(raw[field], index, field);
|
|
34146
|
+
}
|
|
34147
|
+
function validateNameAndVersion(raw, index) {
|
|
34148
|
+
const findings = [];
|
|
34051
34149
|
if (raw["name"] === void 0) {
|
|
34052
34150
|
findings.push({
|
|
34053
34151
|
level: "warning",
|
|
34054
34152
|
code: "catalog.name.missing",
|
|
34055
|
-
message: `${
|
|
34056
|
-
path:
|
|
34153
|
+
message: `${pathOf(index)} has no name, so the store falls back to showing the id.`,
|
|
34154
|
+
path: pathOf(index, "name")
|
|
34057
34155
|
});
|
|
34058
34156
|
} else if (typeof raw["name"] !== "string" || raw["name"].length === 0) {
|
|
34059
34157
|
findings.push({
|
|
34060
34158
|
level: "warning",
|
|
34061
34159
|
code: "catalog.name",
|
|
34062
|
-
message: `${
|
|
34063
|
-
path:
|
|
34160
|
+
message: `${pathOf(index, "name")} is not a non-empty string, so the store falls back to showing the id.`,
|
|
34161
|
+
path: pathOf(index, "name")
|
|
34064
34162
|
});
|
|
34065
34163
|
}
|
|
34066
34164
|
if (raw["version"] === void 0) {
|
|
34067
34165
|
findings.push({
|
|
34068
34166
|
level: "warning",
|
|
34069
34167
|
code: "catalog.version.missing",
|
|
34070
|
-
message: `${
|
|
34071
|
-
path:
|
|
34168
|
+
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.`,
|
|
34169
|
+
path: pathOf(index, "version")
|
|
34072
34170
|
});
|
|
34073
34171
|
}
|
|
34074
34172
|
return findings;
|
|
@@ -34079,24 +34177,24 @@ function validateEntryMetadata(raw, index) {
|
|
|
34079
34177
|
findings.push({
|
|
34080
34178
|
level: "warning",
|
|
34081
34179
|
code: "catalog.downloads",
|
|
34082
|
-
message: `${
|
|
34083
|
-
path:
|
|
34180
|
+
message: `${pathOf(index, "downloads")} must be a non-negative number, so the host ignores it.`,
|
|
34181
|
+
path: pathOf(index, "downloads")
|
|
34084
34182
|
});
|
|
34085
34183
|
}
|
|
34086
34184
|
if (raw["updated"] !== void 0 && !isRenderableDate(raw["updated"])) {
|
|
34087
34185
|
findings.push({
|
|
34088
34186
|
level: "warning",
|
|
34089
34187
|
code: "catalog.updated",
|
|
34090
|
-
message: `${
|
|
34091
|
-
path:
|
|
34188
|
+
message: `${pathOf(index, "updated")} is not a date the store can parse, so it renders the raw string instead of "2 days ago".`,
|
|
34189
|
+
path: pathOf(index, "updated")
|
|
34092
34190
|
});
|
|
34093
34191
|
}
|
|
34094
34192
|
if (raw["repository"] !== void 0 && !isHttpUrl(raw["repository"])) {
|
|
34095
34193
|
findings.push({
|
|
34096
34194
|
level: "warning",
|
|
34097
34195
|
code: "catalog.repository",
|
|
34098
|
-
message: `${
|
|
34099
|
-
path:
|
|
34196
|
+
message: `${pathOf(index, "repository")} must be an http(s) URL, so the host drops it and the detail pane shows no link.`,
|
|
34197
|
+
path: pathOf(index, "repository")
|
|
34100
34198
|
});
|
|
34101
34199
|
}
|
|
34102
34200
|
return findings;
|
|
@@ -34108,8 +34206,8 @@ function validateEntryKeys(raw, index) {
|
|
|
34108
34206
|
findings.push({
|
|
34109
34207
|
level: "warning",
|
|
34110
34208
|
code: "catalog.unknown-key",
|
|
34111
|
-
message: `${
|
|
34112
|
-
path:
|
|
34209
|
+
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.`,
|
|
34210
|
+
path: pathOf(index, key)
|
|
34113
34211
|
});
|
|
34114
34212
|
}
|
|
34115
34213
|
}
|
|
@@ -34150,8 +34248,8 @@ function validateCatalog(catalog, known = KNOWN_CAPABILITIES) {
|
|
|
34150
34248
|
findings.push({
|
|
34151
34249
|
level: "warning",
|
|
34152
34250
|
code: "catalog.id.duplicate",
|
|
34153
|
-
message: `${
|
|
34154
|
-
path:
|
|
34251
|
+
message: `${pathOf(index, "id")} repeats "${id}". The host keeps the first entry with an id and drops the rest.`,
|
|
34252
|
+
path: pathOf(index, "id")
|
|
34155
34253
|
});
|
|
34156
34254
|
}
|
|
34157
34255
|
seen.add(id);
|
|
@@ -34167,7 +34265,7 @@ function validateCommands(sources, ts) {
|
|
|
34167
34265
|
for (const source of sources) {
|
|
34168
34266
|
const file2 = ts.createSourceFile(source.path, source.text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
34169
34267
|
for (const call of registerCommandCalls(ts, file2)) {
|
|
34170
|
-
findings.push(...
|
|
34268
|
+
findings.push(...findingsFor(readRegistration(ts, file2, call)));
|
|
34171
34269
|
}
|
|
34172
34270
|
}
|
|
34173
34271
|
findings.push({ level: "info", code: "commands.runtime", message: RUNTIME_NOTE });
|
|
@@ -34184,11 +34282,11 @@ function registerCommandCalls(ts, file2) {
|
|
|
34184
34282
|
visit(file2);
|
|
34185
34283
|
return calls;
|
|
34186
34284
|
}
|
|
34187
|
-
function
|
|
34285
|
+
function readRegistration(ts, file2, call) {
|
|
34188
34286
|
const line = file2.getLineAndCharacterOfPosition(call.getStart(file2)).line + 1;
|
|
34189
|
-
const
|
|
34287
|
+
const at = `${file2.fileName}:${line}`;
|
|
34190
34288
|
const literal2 = call.arguments[0];
|
|
34191
|
-
const empty = { at
|
|
34289
|
+
const empty = { at, callable: false, described: false, answers: false, returnsValue: false, arguments: [] };
|
|
34192
34290
|
if (!literal2 || !ts.isObjectLiteralExpression(literal2)) {
|
|
34193
34291
|
return { ...empty, unreadable: "its argument is not an object literal" };
|
|
34194
34292
|
}
|
|
@@ -34210,7 +34308,7 @@ function read(ts, file2, call) {
|
|
|
34210
34308
|
const callable = properties.get("callable");
|
|
34211
34309
|
const consent = properties.get("agentConsent");
|
|
34212
34310
|
return {
|
|
34213
|
-
at
|
|
34311
|
+
at,
|
|
34214
34312
|
id: id.text,
|
|
34215
34313
|
callable: callable?.kind === ts.SyntaxKind.TrueKeyword,
|
|
34216
34314
|
described: properties.has("description"),
|
|
@@ -34230,25 +34328,27 @@ function unwrapped(ts, value) {
|
|
|
34230
34328
|
}
|
|
34231
34329
|
return ts.isAsExpression(value) || ts.isSatisfiesExpression(value) || ts.isParenthesizedExpression(value) ? unwrapped(ts, value.expression) : value;
|
|
34232
34330
|
}
|
|
34233
|
-
var
|
|
34331
|
+
var CONSENT_LINES = /* @__PURE__ */ new Map([
|
|
34332
|
+
["allow", "says an agent's word is enough to run it."],
|
|
34333
|
+
[
|
|
34334
|
+
"ask",
|
|
34335
|
+
"says the person is asked first; whoever runs it on an agent's behalf does the asking."
|
|
34336
|
+
],
|
|
34337
|
+
[
|
|
34338
|
+
"ask-always",
|
|
34339
|
+
"says the person is asked every time; whoever runs it on an agent's behalf does the asking."
|
|
34340
|
+
],
|
|
34341
|
+
[
|
|
34342
|
+
"never",
|
|
34343
|
+
"says it is not to be run on an agent's word; closing it to other callers is what enforces that."
|
|
34344
|
+
]
|
|
34345
|
+
]);
|
|
34346
|
+
var STATED_CONSENTS = [...CONSENT_LINES.keys()];
|
|
34234
34347
|
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
|
-
}
|
|
34348
|
+
if (consent === void 0) {
|
|
34349
|
+
return "says nothing about whether an agent's word is enough to run it.";
|
|
34251
34350
|
}
|
|
34351
|
+
return CONSENT_LINES.get(consent) ?? `declares "${consent}" about an agent's word, which is none of ${STATED_CONSENTS.join(", ")}, so nothing reads it.`;
|
|
34252
34352
|
}
|
|
34253
34353
|
function readArguments(ts, value) {
|
|
34254
34354
|
if (!value || !ts.isArrayLiteralExpression(value)) {
|
|
@@ -34295,15 +34395,15 @@ function returnsValue(ts, run) {
|
|
|
34295
34395
|
visit(run);
|
|
34296
34396
|
return found;
|
|
34297
34397
|
}
|
|
34298
|
-
function
|
|
34299
|
-
const { at
|
|
34398
|
+
function findingsFor(registration) {
|
|
34399
|
+
const { at, id } = registration;
|
|
34300
34400
|
if (registration.unreadable) {
|
|
34301
34401
|
return [
|
|
34302
34402
|
{
|
|
34303
34403
|
level: "info",
|
|
34304
34404
|
code: "command.unreadable",
|
|
34305
|
-
path:
|
|
34306
|
-
message: `A registerCommand call at ${
|
|
34405
|
+
path: at,
|
|
34406
|
+
message: `A registerCommand call at ${at} could not be read: ${registration.unreadable}. What it offers an agent is unknown here.`
|
|
34307
34407
|
}
|
|
34308
34408
|
];
|
|
34309
34409
|
}
|
|
@@ -34312,7 +34412,7 @@ function judge(registration) {
|
|
|
34312
34412
|
{
|
|
34313
34413
|
level: "info",
|
|
34314
34414
|
code: "command.private",
|
|
34315
|
-
path:
|
|
34415
|
+
path: at,
|
|
34316
34416
|
message: `${id}: not offered to an agent, because it is not callable; an agent never sees it.`
|
|
34317
34417
|
}
|
|
34318
34418
|
];
|
|
@@ -34322,7 +34422,7 @@ function judge(registration) {
|
|
|
34322
34422
|
findings.push({
|
|
34323
34423
|
level: "warning",
|
|
34324
34424
|
code: "command.description",
|
|
34325
|
-
path:
|
|
34425
|
+
path: at,
|
|
34326
34426
|
message: `${id}: offered to an agent without a description; the agent sees only the id and has to guess what it does.`
|
|
34327
34427
|
});
|
|
34328
34428
|
}
|
|
@@ -34331,7 +34431,7 @@ function judge(registration) {
|
|
|
34331
34431
|
findings.push({
|
|
34332
34432
|
level: "info",
|
|
34333
34433
|
code: "command.argument",
|
|
34334
|
-
path:
|
|
34434
|
+
path: at,
|
|
34335
34435
|
message: `${id}: argument "${argument.name}" has no description; an agent sees a bare name and guesses its value.`
|
|
34336
34436
|
});
|
|
34337
34437
|
}
|
|
@@ -34340,7 +34440,7 @@ function judge(registration) {
|
|
|
34340
34440
|
findings.push({
|
|
34341
34441
|
level: "info",
|
|
34342
34442
|
code: "command.answers",
|
|
34343
|
-
path:
|
|
34443
|
+
path: at,
|
|
34344
34444
|
message: `${id}: run returns a value but declares no answers; an agent hears that the command ran, not what it answered.`
|
|
34345
34445
|
});
|
|
34346
34446
|
}
|
|
@@ -34349,14 +34449,14 @@ function judge(registration) {
|
|
|
34349
34449
|
findings.push({
|
|
34350
34450
|
level: "info",
|
|
34351
34451
|
code: "command.offered",
|
|
34352
|
-
path:
|
|
34452
|
+
path: at,
|
|
34353
34453
|
message: `${id}: offered to an agent, with ${count} described argument${count === 1 ? "" : "s"}${registration.answers ? " and a declared answer" : ""}.`
|
|
34354
34454
|
});
|
|
34355
34455
|
}
|
|
34356
34456
|
findings.push({
|
|
34357
34457
|
level: "info",
|
|
34358
34458
|
code: "command.consent",
|
|
34359
|
-
path:
|
|
34459
|
+
path: at,
|
|
34360
34460
|
message: `${id}: ${consentLine(registration.agentConsent)}`
|
|
34361
34461
|
});
|
|
34362
34462
|
return findings;
|
|
@@ -34394,7 +34494,7 @@ function listGenerators() {
|
|
|
34394
34494
|
}))
|
|
34395
34495
|
});
|
|
34396
34496
|
}
|
|
34397
|
-
function
|
|
34497
|
+
function portableValuesFrom(scaffold2, args) {
|
|
34398
34498
|
const values = {};
|
|
34399
34499
|
for (const option of portableOptions(scaffold2)) {
|
|
34400
34500
|
const value = args[option.name];
|
|
@@ -34409,31 +34509,31 @@ function valuesFor(scaffold2, args) {
|
|
|
34409
34509
|
}
|
|
34410
34510
|
var WHERE_THE_FILES_LAND = "<the directory you wrote these files into>";
|
|
34411
34511
|
function scaffold(scaffold2, args) {
|
|
34412
|
-
const values =
|
|
34512
|
+
const values = portableValuesFrom(scaffold2, args);
|
|
34413
34513
|
const remaining = (scaffold2.amend?.({ ...values, directory: WHERE_THE_FILES_LAND }) ?? []).map((amendment) => describeAmendment(amendment));
|
|
34414
34514
|
return ok({
|
|
34415
34515
|
files: scaffold2.build(values),
|
|
34416
34516
|
...remaining.length > 0 && { remaining }
|
|
34417
34517
|
});
|
|
34418
34518
|
}
|
|
34419
|
-
function validateManifestTool(
|
|
34519
|
+
function validateManifestTool(args) {
|
|
34420
34520
|
return ok({
|
|
34421
34521
|
findings: validateManifest({
|
|
34422
|
-
id:
|
|
34423
|
-
name:
|
|
34424
|
-
capabilities:
|
|
34522
|
+
id: args["id"],
|
|
34523
|
+
name: args["name"],
|
|
34524
|
+
capabilities: args["capabilities"]
|
|
34425
34525
|
})
|
|
34426
34526
|
});
|
|
34427
34527
|
}
|
|
34428
|
-
function validateCatalogTool(
|
|
34429
|
-
return ok({ findings: validateCatalog(
|
|
34528
|
+
function validateCatalogTool(args) {
|
|
34529
|
+
return ok({ findings: validateCatalog(args["catalog"]) });
|
|
34430
34530
|
}
|
|
34431
|
-
function validateI18nTool(
|
|
34432
|
-
const bundles =
|
|
34531
|
+
function validateI18nTool(args) {
|
|
34532
|
+
const bundles = args["bundles"] ?? {};
|
|
34433
34533
|
return ok({ findings: validateI18nParity(bundles) });
|
|
34434
34534
|
}
|
|
34435
|
-
function validateCommandsTool(
|
|
34436
|
-
const files =
|
|
34535
|
+
function validateCommandsTool(args) {
|
|
34536
|
+
const files = args["files"] ?? {};
|
|
34437
34537
|
const ts = loadTypeScript(process.cwd());
|
|
34438
34538
|
if (!ts) {
|
|
34439
34539
|
return ok({
|
|
@@ -34470,7 +34570,7 @@ function inputSchema(descriptor) {
|
|
|
34470
34570
|
function createMcpServer() {
|
|
34471
34571
|
const server = new McpServer({
|
|
34472
34572
|
name: "loomweaver-devkit",
|
|
34473
|
-
version: "0.14.
|
|
34573
|
+
version: "0.14.2"
|
|
34474
34574
|
});
|
|
34475
34575
|
server.registerTool(
|
|
34476
34576
|
"list_generators",
|