@floless/app 0.83.0 → 0.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/floless-server.cjs +188 -89
- package/dist/templates/steel-model.flo +6 -0
- package/dist/templates/vectorize.flo +6 -0
- package/dist/web/analytics.js +31 -0
- package/dist/web/app.css +19 -2
- package/dist/web/aware.js +167 -17
- package/dist/web/index.html +13 -2
- package/dist/web/steel-3d-view.js +10 -0
- package/dist/web/steel-editor.html +5 -0
- package/dist/web/vector-editor.html +29 -7
- package/dist/web/workspaces.js +283 -37
- package/package.json +1 -1
package/dist/floless-server.cjs
CHANGED
|
@@ -6317,14 +6317,14 @@ var require_tools = __commonJS({
|
|
|
6317
6317
|
}
|
|
6318
6318
|
return point < 32 ? JSON.stringify(str2) : '"' + result + '"';
|
|
6319
6319
|
}
|
|
6320
|
-
function asJson(obj, msg,
|
|
6320
|
+
function asJson(obj, msg, num4, time) {
|
|
6321
6321
|
if (asJsonChan.hasSubscribers === false) {
|
|
6322
|
-
return _asJson.call(this, obj, msg,
|
|
6322
|
+
return _asJson.call(this, obj, msg, num4, time);
|
|
6323
6323
|
}
|
|
6324
6324
|
const store = { instance: this, arguments };
|
|
6325
|
-
return asJsonChan.traceSync(_asJson, store, this, obj, msg,
|
|
6325
|
+
return asJsonChan.traceSync(_asJson, store, this, obj, msg, num4, time);
|
|
6326
6326
|
}
|
|
6327
|
-
function _asJson(obj, msg,
|
|
6327
|
+
function _asJson(obj, msg, num4, time) {
|
|
6328
6328
|
const stringify2 = this[stringifySym];
|
|
6329
6329
|
const stringifySafe = this[stringifySafeSym];
|
|
6330
6330
|
const stringifiers = this[stringifiersSym];
|
|
@@ -6334,7 +6334,7 @@ var require_tools = __commonJS({
|
|
|
6334
6334
|
const formatters = this[formattersSym];
|
|
6335
6335
|
const messageKey = this[messageKeySym];
|
|
6336
6336
|
const errorKey = this[errorKeySym];
|
|
6337
|
-
let data = this[lsCacheSym][
|
|
6337
|
+
let data = this[lsCacheSym][num4] + time;
|
|
6338
6338
|
data = data + chindings;
|
|
6339
6339
|
let value;
|
|
6340
6340
|
if (formatters.log) {
|
|
@@ -6959,7 +6959,7 @@ var require_proto = __commonJS({
|
|
|
6959
6959
|
function defaultMixinMergeStrategy(mergeObject, mixinObject) {
|
|
6960
6960
|
return Object.assign(mixinObject, mergeObject);
|
|
6961
6961
|
}
|
|
6962
|
-
function write(_obj, msg,
|
|
6962
|
+
function write(_obj, msg, num4) {
|
|
6963
6963
|
const t = this[timeSym]();
|
|
6964
6964
|
const mixin = this[mixinSym];
|
|
6965
6965
|
const errorKey = this[errorKeySym];
|
|
@@ -6981,12 +6981,12 @@ var require_proto = __commonJS({
|
|
|
6981
6981
|
}
|
|
6982
6982
|
}
|
|
6983
6983
|
if (mixin) {
|
|
6984
|
-
obj = mixinMergeStrategy(obj, mixin(obj,
|
|
6984
|
+
obj = mixinMergeStrategy(obj, mixin(obj, num4, this));
|
|
6985
6985
|
}
|
|
6986
|
-
const s = this[asJsonSym](obj, msg,
|
|
6986
|
+
const s = this[asJsonSym](obj, msg, num4, t);
|
|
6987
6987
|
const stream = this[streamSym];
|
|
6988
6988
|
if (stream[needsMetadataGsym] === true) {
|
|
6989
|
-
stream.lastLevel =
|
|
6989
|
+
stream.lastLevel = num4;
|
|
6990
6990
|
stream.lastObj = obj;
|
|
6991
6991
|
stream.lastMsg = msg;
|
|
6992
6992
|
stream.lastTime = t.slice(this[timeSliceIndexSym]);
|
|
@@ -8513,13 +8513,13 @@ var require_serializer = __commonJS({
|
|
|
8513
8513
|
return "" + integer;
|
|
8514
8514
|
}
|
|
8515
8515
|
asNumber(i) {
|
|
8516
|
-
const
|
|
8517
|
-
if (
|
|
8516
|
+
const num4 = Number(i);
|
|
8517
|
+
if (num4 !== num4) {
|
|
8518
8518
|
throw new Error(`The value "${i}" cannot be converted to a number.`);
|
|
8519
|
-
} else if (
|
|
8519
|
+
} else if (num4 === Infinity || num4 === -Infinity) {
|
|
8520
8520
|
return "null";
|
|
8521
8521
|
} else {
|
|
8522
|
-
return "" +
|
|
8522
|
+
return "" + num4;
|
|
8523
8523
|
}
|
|
8524
8524
|
}
|
|
8525
8525
|
asBoolean(bool) {
|
|
@@ -24673,9 +24673,9 @@ var require_semver = __commonJS({
|
|
|
24673
24673
|
} else {
|
|
24674
24674
|
this.prerelease = m[4].split(".").map((id) => {
|
|
24675
24675
|
if (/^[0-9]+$/.test(id)) {
|
|
24676
|
-
const
|
|
24677
|
-
if (
|
|
24678
|
-
return
|
|
24676
|
+
const num4 = +id;
|
|
24677
|
+
if (num4 >= 0 && num4 < MAX_SAFE_INTEGER) {
|
|
24678
|
+
return num4;
|
|
24679
24679
|
}
|
|
24680
24680
|
}
|
|
24681
24681
|
return id;
|
|
@@ -41036,24 +41036,24 @@ var require_dist5 = __commonJS({
|
|
|
41036
41036
|
var DAY = HOUR * 24;
|
|
41037
41037
|
var YEAR = DAY * 365.25;
|
|
41038
41038
|
function parse(val) {
|
|
41039
|
-
var
|
|
41040
|
-
if (arr != null && (
|
|
41041
|
-
if (arr[3] != null) return
|
|
41042
|
-
if (arr[4] != null) return
|
|
41043
|
-
if (arr[5] != null) return
|
|
41044
|
-
if (arr[6] != null) return
|
|
41045
|
-
if (arr[7] != null) return
|
|
41046
|
-
if (arr[8] != null) return
|
|
41047
|
-
return
|
|
41039
|
+
var num4, arr = val.toLowerCase().match(RGX);
|
|
41040
|
+
if (arr != null && (num4 = parseFloat(arr[1]))) {
|
|
41041
|
+
if (arr[3] != null) return num4 * SEC;
|
|
41042
|
+
if (arr[4] != null) return num4 * MIN;
|
|
41043
|
+
if (arr[5] != null) return num4 * HOUR;
|
|
41044
|
+
if (arr[6] != null) return num4 * DAY;
|
|
41045
|
+
if (arr[7] != null) return num4 * DAY * 7;
|
|
41046
|
+
if (arr[8] != null) return num4 * YEAR;
|
|
41047
|
+
return num4;
|
|
41048
41048
|
}
|
|
41049
41049
|
}
|
|
41050
41050
|
function fmt(val, pfx, str2, long) {
|
|
41051
|
-
var
|
|
41052
|
-
return pfx +
|
|
41051
|
+
var num4 = (val | 0) === val ? val : ~~(val + 0.5);
|
|
41052
|
+
return pfx + num4 + (long ? " " + str2 + (num4 != 1 ? "s" : "") : str2[0]);
|
|
41053
41053
|
}
|
|
41054
|
-
function format(
|
|
41055
|
-
var pfx =
|
|
41056
|
-
if (abs < SEC) return
|
|
41054
|
+
function format(num4, long) {
|
|
41055
|
+
var pfx = num4 < 0 ? "-" : "", abs = num4 < 0 ? -num4 : num4;
|
|
41056
|
+
if (abs < SEC) return num4 + (long ? " ms" : "ms");
|
|
41057
41057
|
if (abs < MIN) return fmt(abs / SEC, pfx, "second", long);
|
|
41058
41058
|
if (abs < HOUR) return fmt(abs / MIN, pfx, "minute", long);
|
|
41059
41059
|
if (abs < DAY) return fmt(abs / HOUR, pfx, "hour", long);
|
|
@@ -45784,9 +45784,9 @@ var require_stringifyNumber = __commonJS({
|
|
|
45784
45784
|
function stringifyNumber({ format, minFractionDigits, tag, value }) {
|
|
45785
45785
|
if (typeof value === "bigint")
|
|
45786
45786
|
return String(value);
|
|
45787
|
-
const
|
|
45788
|
-
if (!isFinite(
|
|
45789
|
-
return isNaN(
|
|
45787
|
+
const num4 = typeof value === "number" ? value : Number(value);
|
|
45788
|
+
if (!isFinite(num4))
|
|
45789
|
+
return isNaN(num4) ? ".nan" : num4 < 0 ? "-.inf" : ".inf";
|
|
45790
45790
|
let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
|
|
45791
45791
|
if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
|
|
45792
45792
|
let i = n.indexOf(".");
|
|
@@ -45826,8 +45826,8 @@ var require_float = __commonJS({
|
|
|
45826
45826
|
test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
|
|
45827
45827
|
resolve: (str2) => parseFloat(str2),
|
|
45828
45828
|
stringify(node) {
|
|
45829
|
-
const
|
|
45830
|
-
return isFinite(
|
|
45829
|
+
const num4 = Number(node.value);
|
|
45830
|
+
return isFinite(num4) ? num4.toExponential() : stringifyNumber.stringifyNumber(node);
|
|
45831
45831
|
}
|
|
45832
45832
|
};
|
|
45833
45833
|
var float = {
|
|
@@ -46266,8 +46266,8 @@ var require_float2 = __commonJS({
|
|
|
46266
46266
|
test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
|
|
46267
46267
|
resolve: (str2) => parseFloat(str2.replace(/_/g, "")),
|
|
46268
46268
|
stringify(node) {
|
|
46269
|
-
const
|
|
46270
|
-
return isFinite(
|
|
46269
|
+
const num4 = Number(node.value);
|
|
46270
|
+
return isFinite(num4) ? num4.toExponential() : stringifyNumber.stringifyNumber(node);
|
|
46271
46271
|
}
|
|
46272
46272
|
};
|
|
46273
46273
|
var float = {
|
|
@@ -46469,23 +46469,23 @@ var require_timestamp2 = __commonJS({
|
|
|
46469
46469
|
function parseSexagesimal(str2, asBigInt) {
|
|
46470
46470
|
const sign = str2[0];
|
|
46471
46471
|
const parts = sign === "-" || sign === "+" ? str2.substring(1) : str2;
|
|
46472
|
-
const
|
|
46473
|
-
const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 *
|
|
46474
|
-
return sign === "-" ?
|
|
46472
|
+
const num4 = (n) => asBigInt ? BigInt(n) : Number(n);
|
|
46473
|
+
const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num4(60) + num4(p), num4(0));
|
|
46474
|
+
return sign === "-" ? num4(-1) * res : res;
|
|
46475
46475
|
}
|
|
46476
46476
|
function stringifySexagesimal(node) {
|
|
46477
46477
|
let { value } = node;
|
|
46478
|
-
let
|
|
46478
|
+
let num4 = (n) => n;
|
|
46479
46479
|
if (typeof value === "bigint")
|
|
46480
|
-
|
|
46480
|
+
num4 = (n) => BigInt(n);
|
|
46481
46481
|
else if (isNaN(value) || !isFinite(value))
|
|
46482
46482
|
return stringifyNumber.stringifyNumber(node);
|
|
46483
46483
|
let sign = "";
|
|
46484
46484
|
if (value < 0) {
|
|
46485
46485
|
sign = "-";
|
|
46486
|
-
value *=
|
|
46486
|
+
value *= num4(-1);
|
|
46487
46487
|
}
|
|
46488
|
-
const _60 =
|
|
46488
|
+
const _60 = num4(60);
|
|
46489
46489
|
const parts = [value % _60];
|
|
46490
46490
|
if (value < 60) {
|
|
46491
46491
|
parts.unshift(0);
|
|
@@ -51176,6 +51176,17 @@ function appBaked(id) {
|
|
|
51176
51176
|
return false;
|
|
51177
51177
|
}
|
|
51178
51178
|
}
|
|
51179
|
+
function appWorkspace(id) {
|
|
51180
|
+
try {
|
|
51181
|
+
const dir = appDir(id);
|
|
51182
|
+
const sourcePath = firstWithExt(dir, ".app") ?? firstWithExt(dir, ".flo");
|
|
51183
|
+
if (!sourcePath) return false;
|
|
51184
|
+
const src = asRecord((0, import_yaml.parse)((0, import_node_fs5.readFileSync)(sourcePath, "utf8")));
|
|
51185
|
+
return typeof src.floless === "object" && src.floless !== null && src.floless.workspace === true;
|
|
51186
|
+
} catch {
|
|
51187
|
+
return false;
|
|
51188
|
+
}
|
|
51189
|
+
}
|
|
51179
51190
|
function readApp(id) {
|
|
51180
51191
|
const dir = appDir(id);
|
|
51181
51192
|
const sourcePath = firstWithExt(dir, ".app") ?? firstWithExt(dir, ".flo");
|
|
@@ -51279,6 +51290,8 @@ function readApp(id) {
|
|
|
51279
51290
|
runnable: runState === "ready",
|
|
51280
51291
|
triggerSource: detectTriggerSource(nodes, connections, readCommandSpec),
|
|
51281
51292
|
baked: src["exposes-as-agent"] === true,
|
|
51293
|
+
// FloLess-namespaced `floless: { workspace: true }` — a FloLess flag AWARE ignores.
|
|
51294
|
+
workspace: typeof src.floless === "object" && src.floless !== null && src.floless.workspace === true,
|
|
51282
51295
|
rebakeInput: findRebakeInput(inputs)
|
|
51283
51296
|
};
|
|
51284
51297
|
}
|
|
@@ -53093,7 +53106,7 @@ function appVersion() {
|
|
|
53093
53106
|
return resolveVersion({
|
|
53094
53107
|
isSea: isSea2(),
|
|
53095
53108
|
sqVersionXml: readSqVersionXml(),
|
|
53096
|
-
define: true ? "0.
|
|
53109
|
+
define: true ? "0.85.0" : void 0,
|
|
53097
53110
|
pkgVersion: readPkgVersion()
|
|
53098
53111
|
});
|
|
53099
53112
|
}
|
|
@@ -53103,7 +53116,7 @@ function resolveChannel(s) {
|
|
|
53103
53116
|
return "dev";
|
|
53104
53117
|
}
|
|
53105
53118
|
function appChannel() {
|
|
53106
|
-
return resolveChannel({ isSea: isSea2(), define: true ? "0.
|
|
53119
|
+
return resolveChannel({ isSea: isSea2(), define: true ? "0.85.0" : void 0 });
|
|
53107
53120
|
}
|
|
53108
53121
|
|
|
53109
53122
|
// workflow-update.ts
|
|
@@ -53400,7 +53413,7 @@ function extractReportHtml(events) {
|
|
|
53400
53413
|
var import_node_child_process3 = require("node:child_process");
|
|
53401
53414
|
var import_node_fs12 = require("node:fs");
|
|
53402
53415
|
var import_node_path11 = require("node:path");
|
|
53403
|
-
var OPEN_EXTS = /* @__PURE__ */ new Set([".xls", ".xlsx", ".html", ".htm", ".csv"]);
|
|
53416
|
+
var OPEN_EXTS = /* @__PURE__ */ new Set([".xls", ".xlsx", ".html", ".htm", ".csv", ".svg"]);
|
|
53404
53417
|
var REVEAL_EXTS = /* @__PURE__ */ new Set([...OPEN_EXTS, ".ifc", ".nc1", ".dstv"]);
|
|
53405
53418
|
function hasOpenableExt(path) {
|
|
53406
53419
|
return OPEN_EXTS.has((0, import_node_path11.extname)(path).toLowerCase());
|
|
@@ -54055,16 +54068,16 @@ function sanitizeRecipe(raw) {
|
|
|
54055
54068
|
if (!raw || typeof raw !== "object") return void 0;
|
|
54056
54069
|
const r = raw;
|
|
54057
54070
|
if (typeof r.kind !== "string" || !r.kind || !r.params || typeof r.params !== "object") return void 0;
|
|
54058
|
-
const
|
|
54071
|
+
const num4 = {};
|
|
54059
54072
|
for (const [k, v] of Object.entries(r.params)) {
|
|
54060
|
-
if (typeof v === "number" && isFinite(v))
|
|
54073
|
+
if (typeof v === "number" && isFinite(v)) num4[k] = v;
|
|
54061
54074
|
}
|
|
54062
54075
|
if (r.kind === "base-plate") {
|
|
54063
54076
|
const params = {};
|
|
54064
54077
|
for (const [k, [lo, hi]] of Object.entries(BASE_PLATE_PARAMS)) {
|
|
54065
|
-
if (k in
|
|
54066
|
-
if (!(
|
|
54067
|
-
params[k] =
|
|
54078
|
+
if (k in num4) {
|
|
54079
|
+
if (!(num4[k] >= lo && num4[k] <= hi)) return void 0;
|
|
54080
|
+
params[k] = num4[k];
|
|
54068
54081
|
}
|
|
54069
54082
|
}
|
|
54070
54083
|
return Object.keys(params).length ? { kind: r.kind, params } : void 0;
|
|
@@ -54072,14 +54085,14 @@ function sanitizeRecipe(raw) {
|
|
|
54072
54085
|
if (r.kind === "shear-plate") {
|
|
54073
54086
|
const params = {};
|
|
54074
54087
|
for (const [k, [lo, hi]] of Object.entries(SHEAR_PLATE_PARAMS)) {
|
|
54075
|
-
if (k in
|
|
54076
|
-
if (!(
|
|
54077
|
-
params[k] =
|
|
54088
|
+
if (k in num4) {
|
|
54089
|
+
if (!(num4[k] >= lo && num4[k] <= hi)) return void 0;
|
|
54090
|
+
params[k] = num4[k];
|
|
54078
54091
|
}
|
|
54079
54092
|
}
|
|
54080
54093
|
return Object.keys(params).length ? { kind: r.kind, params } : void 0;
|
|
54081
54094
|
}
|
|
54082
|
-
return Object.keys(
|
|
54095
|
+
return Object.keys(num4).length ? { kind: r.kind, params: num4 } : void 0;
|
|
54083
54096
|
}
|
|
54084
54097
|
async function extractConnection(companionId, ifcPath, id) {
|
|
54085
54098
|
await ensureConnectionReader();
|
|
@@ -54798,6 +54811,9 @@ function diffContracts(from, to) {
|
|
|
54798
54811
|
}
|
|
54799
54812
|
|
|
54800
54813
|
// version-diff.ts
|
|
54814
|
+
function appSupportsDiff(app) {
|
|
54815
|
+
return app === "steel-model";
|
|
54816
|
+
}
|
|
54801
54817
|
function computeVersionDiff(id, n, base) {
|
|
54802
54818
|
if (!Number.isInteger(n) || n < 1) return { code: 400, body: { ok: false, error: "n must be a positive integer version number" } };
|
|
54803
54819
|
const head = listVersions(id)[0]?.n ?? 0;
|
|
@@ -54973,9 +54989,60 @@ function deriveLayers(elements, existing) {
|
|
|
54973
54989
|
}
|
|
54974
54990
|
return [...counts.entries()].map(([name, count]) => onByName.has(name) ? { name, count, on: onByName.get(name) } : { name, count }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
54975
54991
|
}
|
|
54992
|
+
function pickSheet(contract, sheetId) {
|
|
54993
|
+
const sheets = contract.sheets ?? [];
|
|
54994
|
+
if (sheetId) return sheets.find((s) => s.id === sheetId);
|
|
54995
|
+
const active = contract.active ?? 0;
|
|
54996
|
+
return sheets[active] ?? sheets[0];
|
|
54997
|
+
}
|
|
54998
|
+
function escAttr(s) {
|
|
54999
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
55000
|
+
}
|
|
55001
|
+
function escText(s) {
|
|
55002
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
55003
|
+
}
|
|
55004
|
+
function num2(v, fallback) {
|
|
55005
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
55006
|
+
return Number.isFinite(n) ? n : fallback;
|
|
55007
|
+
}
|
|
55008
|
+
function ptsToPath(pts) {
|
|
55009
|
+
if (!pts || pts.length < 2) return "";
|
|
55010
|
+
return pts.map((p, i) => `${i === 0 ? "M" : "L"}${num2(p[0], 0)} ${num2(p[1], 0)}`).join(" ");
|
|
55011
|
+
}
|
|
55012
|
+
function contractToSvg(contract, sheetId) {
|
|
55013
|
+
const sheet = pickSheet(contract, sheetId);
|
|
55014
|
+
if (!sheet) return '<svg xmlns="http://www.w3.org/2000/svg"></svg>';
|
|
55015
|
+
const w = num2(sheet.page?.w, 100);
|
|
55016
|
+
const h = num2(sheet.page?.h, 100);
|
|
55017
|
+
const parts = [];
|
|
55018
|
+
for (const el of sheet.elements) {
|
|
55019
|
+
if (el.kind === "text") {
|
|
55020
|
+
if (!el.text) continue;
|
|
55021
|
+
const bbox = el.bbox;
|
|
55022
|
+
const ox = num2(el.origin?.[0] ?? bbox?.[0], 0);
|
|
55023
|
+
const oy = num2(el.origin?.[1] ?? bbox?.[3] ?? bbox?.[1], 0);
|
|
55024
|
+
const fs2 = el.size != null ? num2(el.size, 4) : num2(el.w, 1) * 6;
|
|
55025
|
+
const font = el.font ? ` font-family="${escAttr(el.font)}"` : "";
|
|
55026
|
+
const ang = num2(el.angle, 0);
|
|
55027
|
+
const rot = ang ? ` transform="rotate(${ang} ${ox} ${oy})"` : "";
|
|
55028
|
+
parts.push(
|
|
55029
|
+
`<text x="${ox}" y="${oy}" font-size="${fs2}" fill="${escAttr(el.color ?? "#94a3b8")}"${font}${rot}>${escText(el.text)}</text>`
|
|
55030
|
+
);
|
|
55031
|
+
continue;
|
|
55032
|
+
}
|
|
55033
|
+
const d = el.d || ptsToPath(el.pts);
|
|
55034
|
+
if (!d) continue;
|
|
55035
|
+
const sw = num2(el.w, 1);
|
|
55036
|
+
const dash = el.dashed ? ` stroke-dasharray="${sw * 3} ${sw * 2}"` : "";
|
|
55037
|
+
parts.push(
|
|
55038
|
+
`<path d="${escAttr(d)}" fill="none" stroke="${escAttr(el.color ?? "#e2e8f0")}" stroke-width="${sw}"${dash}/>`
|
|
55039
|
+
);
|
|
55040
|
+
}
|
|
55041
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${w} ${h}" width="${w}" height="${h}">${parts.join("")}</svg>`;
|
|
55042
|
+
}
|
|
54976
55043
|
function postProcess(contract, opts = {}) {
|
|
54977
55044
|
const snapTol = opts.snapTol ?? 1;
|
|
54978
|
-
const sheets = contract.sheets.map((sheet, i) => {
|
|
55045
|
+
const sheets = (contract.sheets ?? []).map((sheet, i) => {
|
|
54979
55046
|
let elements = sheet.elements ?? [];
|
|
54980
55047
|
elements = snapEndpoints(elements, snapTol);
|
|
54981
55048
|
elements = dedupeElements(elements);
|
|
@@ -55226,16 +55293,16 @@ var GROUPS = {
|
|
|
55226
55293
|
custom: { key: "custom", label: "Imported connections", color: "#8a97a8" }
|
|
55227
55294
|
};
|
|
55228
55295
|
var EMBED = 1;
|
|
55229
|
-
function
|
|
55296
|
+
function num3(params, key, def) {
|
|
55230
55297
|
const v = params?.[key];
|
|
55231
55298
|
return typeof v === "number" && isFinite(v) ? v : def;
|
|
55232
55299
|
}
|
|
55233
55300
|
function pos(params, key, def) {
|
|
55234
|
-
const v =
|
|
55301
|
+
const v = num3(params, key, def);
|
|
55235
55302
|
return v > 0 ? v : def;
|
|
55236
55303
|
}
|
|
55237
55304
|
function intMin1(params, key, def) {
|
|
55238
|
-
return Math.max(1, Math.round(
|
|
55305
|
+
return Math.max(1, Math.round(num3(params, key, def)));
|
|
55239
55306
|
}
|
|
55240
55307
|
function finite3(a) {
|
|
55241
55308
|
return Array.isArray(a) && a.length >= 3 && a.every((n) => typeof n === "number" && isFinite(n));
|
|
@@ -55290,18 +55357,18 @@ function expandBasePlate(joint, col) {
|
|
|
55290
55357
|
const [bx, by, bz] = base;
|
|
55291
55358
|
const { w, d } = col.dims;
|
|
55292
55359
|
const thickness = pos(p, "thickness", 25);
|
|
55293
|
-
const margin = Math.max(0,
|
|
55360
|
+
const margin = Math.max(0, num3(p, "margin", 75));
|
|
55294
55361
|
const width = pos(p, "plateWidth", Math.max(w, d) + 2 * margin);
|
|
55295
55362
|
const depth = pos(p, "plateDepth", Math.max(w, d) + 2 * margin);
|
|
55296
55363
|
const boltDia = pos(p, "boltDia", 24);
|
|
55297
|
-
const clearance =
|
|
55298
|
-
const edge =
|
|
55364
|
+
const clearance = num3(p, "holeClearance", 6);
|
|
55365
|
+
const edge = num3(p, "edgeDist", Math.max(1.5 * boltDia, 40));
|
|
55299
55366
|
const cols = intMin1(p, "boltCols", 2);
|
|
55300
55367
|
const rows = intMin1(p, "boltRows", 2);
|
|
55301
|
-
const weldLeg =
|
|
55302
|
-
const projBelow =
|
|
55303
|
-
const embedment = Math.max(0,
|
|
55304
|
-
const grout = Math.max(0,
|
|
55368
|
+
const weldLeg = num3(p, "weldLeg", 8);
|
|
55369
|
+
const projBelow = num3(p, "projBelow", 75);
|
|
55370
|
+
const embedment = Math.max(0, num3(p, "embedment", 0));
|
|
55371
|
+
const grout = Math.max(0, num3(p, "grout", 0));
|
|
55305
55372
|
const rodBelow = embedment > 0 ? grout + embedment : projBelow;
|
|
55306
55373
|
const nutH = pos(p, "nutHeight", 0.8 * boltDia);
|
|
55307
55374
|
const nutAF = pos(p, "nutAcrossFlats", 1.6 * boltDia);
|
|
@@ -55411,14 +55478,14 @@ function beamCopes(beam, support, at, params) {
|
|
|
55411
55478
|
if (!finite3(beam.from) || !finite3(beam.to) || !beam.dims || !(beam.dims.d > 0) || !(beam.dims.w > 0)) return {};
|
|
55412
55479
|
if (profileKind(support.profile) !== "I") return {};
|
|
55413
55480
|
const endPt = at === "end1" ? beam.to : beam.from;
|
|
55414
|
-
const gap = Math.max(0,
|
|
55481
|
+
const gap = Math.max(0, num3(params, "clearance", 12.7));
|
|
55415
55482
|
const beamMidZ = endPt[2] + (typeof beam.posOffset === "number" && isFinite(beam.posOffset) ? beam.posOffset : 0);
|
|
55416
55483
|
const beamTopZ = beamMidZ + beam.dims.d / 2, beamBotZ = beamMidZ - beam.dims.d / 2;
|
|
55417
55484
|
const supMidZ = supportWorkZAt(support, endPt) + (typeof support.posOffset === "number" && isFinite(support.posOffset) ? support.posOffset : 0);
|
|
55418
55485
|
const supTopZ = supMidZ + support.dims.d / 2, supBotZ = supMidZ - support.dims.d / 2;
|
|
55419
55486
|
const tfS = flangeThk(support.profile, support.dims.d);
|
|
55420
|
-
const kClear = Math.max(0,
|
|
55421
|
-
const minLen = Math.max(0,
|
|
55487
|
+
const kClear = Math.max(0, num3(params, "copeKClear", 12.7));
|
|
55488
|
+
const minLen = Math.max(0, num3(params, "copeMinLength", 31.75));
|
|
55422
55489
|
const kBand = tfS + kClear;
|
|
55423
55490
|
const webLo = supBotZ + kBand, webHi = supTopZ - kBand;
|
|
55424
55491
|
if (!(webLo < webHi && beamTopZ > webLo && beamBotZ < webHi)) return {};
|
|
@@ -55461,14 +55528,14 @@ function expandShearPlate(joint, beam, support) {
|
|
|
55461
55528
|
const cols = intMin1(p, "boltCols", 1);
|
|
55462
55529
|
const rows = intMin1(p, "boltRows", 3);
|
|
55463
55530
|
const pitch = pos(p, "boltPitch", 70);
|
|
55464
|
-
const holeClr =
|
|
55465
|
-
const edge = Math.max(0,
|
|
55466
|
-
const weldLeg =
|
|
55467
|
-
const clearance = Math.max(0,
|
|
55531
|
+
const holeClr = num3(p, "holeClearance", 2);
|
|
55532
|
+
const edge = Math.max(0, num3(p, "edgeDist", Math.max(1.5 * boltDia, 30)));
|
|
55533
|
+
const weldLeg = num3(p, "weldLeg", 6);
|
|
55534
|
+
const clearance = Math.max(0, num3(p, "clearance", 12.7));
|
|
55468
55535
|
const stiffener = p?.["stiffener"] === true;
|
|
55469
55536
|
const height = pos(p, "plateHeight", (rows - 1) * pitch + 2 * edge);
|
|
55470
55537
|
const twHalf = webHalfThk(beam.profile, beam.dims.w);
|
|
55471
|
-
const webSide =
|
|
55538
|
+
const webSide = num3(p, "webSide", 1) >= 0 ? 1 : -1;
|
|
55472
55539
|
const pOff = webSide * (twHalf + thickness / 2);
|
|
55473
55540
|
const u0 = support ? webHalfThk(support.profile, support.dims.w) : 0;
|
|
55474
55541
|
const edgeV = Math.min(edge, height * 0.4);
|
|
@@ -60348,16 +60415,16 @@ function memberTons(contract, m) {
|
|
|
60348
60415
|
}
|
|
60349
60416
|
function rollup(members, countWeighted = false) {
|
|
60350
60417
|
const counts = emptyCounts();
|
|
60351
|
-
let
|
|
60418
|
+
let num4 = 0, den = 0, tons = 0;
|
|
60352
60419
|
for (const m of members) {
|
|
60353
60420
|
counts[m.band]++;
|
|
60354
60421
|
if (m.band === "rfi") continue;
|
|
60355
60422
|
const w = countWeighted ? 1 : m.tons;
|
|
60356
|
-
|
|
60423
|
+
num4 += w * BAND_WEIGHT[m.band];
|
|
60357
60424
|
den += w;
|
|
60358
60425
|
tons += m.tons;
|
|
60359
60426
|
}
|
|
60360
|
-
return { score: den > 0 ? Math.round(
|
|
60427
|
+
return { score: den > 0 ? Math.round(num4 / den * 100) : null, tons, counts };
|
|
60361
60428
|
}
|
|
60362
60429
|
function bandEnd(end, rows) {
|
|
60363
60430
|
const row = end.conn ? rows.get(end.conn) : void 0;
|
|
@@ -60393,14 +60460,14 @@ function scoreConnections(contractInput) {
|
|
|
60393
60460
|
}
|
|
60394
60461
|
});
|
|
60395
60462
|
const counts = emptyCounts();
|
|
60396
|
-
let
|
|
60463
|
+
let num4 = 0, den = 0;
|
|
60397
60464
|
for (const e of byEnd) {
|
|
60398
60465
|
counts[e.band]++;
|
|
60399
60466
|
if (e.band === "rfi") continue;
|
|
60400
|
-
|
|
60467
|
+
num4 += BAND_WEIGHT[e.band];
|
|
60401
60468
|
den += 1;
|
|
60402
60469
|
}
|
|
60403
|
-
const category = { score: den > 0 ? Math.round(
|
|
60470
|
+
const category = { score: den > 0 ? Math.round(num4 / den * 100) : null, tons: 0, counts };
|
|
60404
60471
|
return { byEnd, category };
|
|
60405
60472
|
}
|
|
60406
60473
|
function scoreContract(contractInput) {
|
|
@@ -60447,7 +60514,7 @@ function scoreContract(contractInput) {
|
|
|
60447
60514
|
const columns = rollup(byMember.filter((m) => m.role === "column"));
|
|
60448
60515
|
const details = rollupDetails(byDetail);
|
|
60449
60516
|
const scored = byMember.filter((m) => m.band !== "rfi");
|
|
60450
|
-
const
|
|
60517
|
+
const num4 = scored.reduce((s, m) => s + m.tons * BAND_WEIGHT[m.band], 0);
|
|
60451
60518
|
const den = scored.reduce((s, m) => s + m.tons, 0);
|
|
60452
60519
|
return {
|
|
60453
60520
|
byMember,
|
|
@@ -60465,7 +60532,7 @@ function scoreContract(contractInput) {
|
|
|
60465
60532
|
note: "Approximate in v1 \u2014 precise per-segment binding is Slice 2."
|
|
60466
60533
|
},
|
|
60467
60534
|
overall: {
|
|
60468
|
-
score: den > 0 ? Math.round(
|
|
60535
|
+
score: den > 0 ? Math.round(num4 / den * 100) : null,
|
|
60469
60536
|
tons: den,
|
|
60470
60537
|
rfiCount: byMember.filter((m) => m.band === "rfi").length
|
|
60471
60538
|
}
|
|
@@ -60477,13 +60544,13 @@ function detailSheet(text) {
|
|
|
60477
60544
|
}
|
|
60478
60545
|
function rollupDetails(details) {
|
|
60479
60546
|
const counts = emptyCounts();
|
|
60480
|
-
let
|
|
60547
|
+
let num4 = 0, den = 0;
|
|
60481
60548
|
for (const d of details) {
|
|
60482
60549
|
counts[d.band]++;
|
|
60483
|
-
|
|
60550
|
+
num4 += BAND_WEIGHT[d.band];
|
|
60484
60551
|
den += 1;
|
|
60485
60552
|
}
|
|
60486
|
-
return { score: den > 0 ? Math.round(
|
|
60553
|
+
return { score: den > 0 ? Math.round(num4 / den * 100) : null, tons: 0, counts };
|
|
60487
60554
|
}
|
|
60488
60555
|
|
|
60489
60556
|
// contract-score.ts
|
|
@@ -64755,7 +64822,7 @@ async function startServer() {
|
|
|
64755
64822
|
});
|
|
64756
64823
|
app.get("/api/apps", async () => {
|
|
64757
64824
|
const apps = await aware.list();
|
|
64758
|
-
return { ok: true, apps: apps.map((a) => ({ ...a, provider: appProvider(a.id), baked: appBaked(a.id), ...appOrigin(a.id) })) };
|
|
64825
|
+
return { ok: true, apps: apps.map((a) => ({ ...a, provider: appProvider(a.id), baked: appBaked(a.id), workspace: appWorkspace(a.id), ...appOrigin(a.id) })) };
|
|
64759
64826
|
});
|
|
64760
64827
|
app.get("/api/aisc-shapes", async () => ({ ok: true, shapes: Object.keys(aisc_shapes_default) }));
|
|
64761
64828
|
app.get("/api/workflows/updates", async () => {
|
|
@@ -65023,6 +65090,9 @@ async function startServer() {
|
|
|
65023
65090
|
async (req, reply) => {
|
|
65024
65091
|
const project = getProject(req.params.id);
|
|
65025
65092
|
if (!project) return reply.status(404).send({ ok: false, error: `unknown project: ${req.params.id}` });
|
|
65093
|
+
if (!appSupportsDiff(project.app)) {
|
|
65094
|
+
return reply.status(409).send({ ok: false, error: `the version diff is not supported for "${project.app}" projects` });
|
|
65095
|
+
}
|
|
65026
65096
|
const n = Number(req.params.n);
|
|
65027
65097
|
const base = req.query.base !== void 0 && req.query.base !== "" ? Number(req.query.base) : void 0;
|
|
65028
65098
|
const { code, body } = computeVersionDiff(project.id, n, base);
|
|
@@ -65140,6 +65210,9 @@ async function startServer() {
|
|
|
65140
65210
|
return null;
|
|
65141
65211
|
}
|
|
65142
65212
|
function projectExportFiles(appId) {
|
|
65213
|
+
if (appId === "vectorize") {
|
|
65214
|
+
return [{ kind: "svg", filename: `${appId}.svg` }];
|
|
65215
|
+
}
|
|
65143
65216
|
return [
|
|
65144
65217
|
{ kind: "bom-csv", filename: `${appId}-bom.csv` },
|
|
65145
65218
|
{ kind: "bom-xlsx", filename: `${appId}-bom.xlsx` },
|
|
@@ -65150,7 +65223,7 @@ async function startServer() {
|
|
|
65150
65223
|
const p = getProject(project);
|
|
65151
65224
|
if (!p) return { status: 404, error: `unknown project: ${project}` };
|
|
65152
65225
|
if (p.app !== appId) return { status: 404, error: `project "${project}" belongs to app "${p.app}", not "${appId}"` };
|
|
65153
|
-
if (!p.approvedAt) return { status: 409, error: "Approve
|
|
65226
|
+
if (!p.approvedAt) return { status: 409, error: "Approve this version before exporting \u2014 exports go out only after sign-off." };
|
|
65154
65227
|
return null;
|
|
65155
65228
|
}
|
|
65156
65229
|
app.get("/api/contract/:appId", async (req, reply) => {
|
|
@@ -65216,7 +65289,7 @@ async function startServer() {
|
|
|
65216
65289
|
broadcast({ type: "compiled", id: req.params.appId, lockPath: result.lockPath });
|
|
65217
65290
|
let approvedAt;
|
|
65218
65291
|
if (project) {
|
|
65219
|
-
createVersion(project, doc, { author: "You", message: "Approved
|
|
65292
|
+
createVersion(project, doc, { author: "You", message: "Approved this version", gate: "model", kind: "approve" });
|
|
65220
65293
|
approvedAt = markApproved(project).approvedAt;
|
|
65221
65294
|
}
|
|
65222
65295
|
return { ok: true, result, approvedAt };
|
|
@@ -65508,6 +65581,32 @@ async function startServer() {
|
|
|
65508
65581
|
return reply.status(500).send({ ok: false, error: `BOM ${format.toUpperCase()} export failed: ${e instanceof Error ? e.message : "write error"}` });
|
|
65509
65582
|
}
|
|
65510
65583
|
});
|
|
65584
|
+
app.post("/api/contract/:appId/export-svg", async (req, reply) => {
|
|
65585
|
+
const project = req.query.project || void 0;
|
|
65586
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(req.params.appId)) {
|
|
65587
|
+
return reply.status(400).send({ ok: false, error: `invalid appId: ${req.params.appId}` });
|
|
65588
|
+
}
|
|
65589
|
+
if (project) {
|
|
65590
|
+
const g = projectExportGate(req.params.appId, project);
|
|
65591
|
+
if (g) return reply.status(g.status).send({ ok: false, error: g.error });
|
|
65592
|
+
}
|
|
65593
|
+
const doc = project ? readContract(req.params.appId, project) : readContractForApp(req.params.appId);
|
|
65594
|
+
if (doc == null) return reply.status(404).send({ ok: false, error: "no contract to export" });
|
|
65595
|
+
const v = validateContract(doc);
|
|
65596
|
+
if (!v.valid) return reply.status(400).send({ ok: false, error: "stored contract is invalid \u2014 re-save it in the editor" });
|
|
65597
|
+
const cleaned = postProcess(doc);
|
|
65598
|
+
const svg = contractToSvg(cleaned);
|
|
65599
|
+
const filename = `${req.params.appId}.svg`;
|
|
65600
|
+
const outPath = project ? (0, import_node_path33.join)(projectExportsDir(project), filename) : (0, import_node_path33.join)(appPath(req.params.appId), filename);
|
|
65601
|
+
try {
|
|
65602
|
+
(0, import_node_fs35.mkdirSync)((0, import_node_path33.dirname)(outPath), { recursive: true });
|
|
65603
|
+
(0, import_node_fs35.writeFileSync)(outPath, svg, "utf8");
|
|
65604
|
+
return { ok: true, filename, encoding: "utf8", content: svg, bytes: Buffer.byteLength(svg), savedTo: outPath };
|
|
65605
|
+
} catch (e) {
|
|
65606
|
+
app.log.error({ appId: req.params.appId, err: e instanceof Error ? e.message : e }, "export-svg: write failed");
|
|
65607
|
+
return reply.status(500).send({ ok: false, error: `SVG export failed: ${e instanceof Error ? e.message : "write error"}` });
|
|
65608
|
+
}
|
|
65609
|
+
});
|
|
65511
65610
|
async function stopForegroundSession(id) {
|
|
65512
65611
|
try {
|
|
65513
65612
|
const sid = foregroundSessionId(id);
|
|
@@ -3,6 +3,12 @@ version: 0.3.0
|
|
|
3
3
|
display-name: Steel Model
|
|
4
4
|
publisher: floless
|
|
5
5
|
module: steel-detailer
|
|
6
|
+
# FloLess-namespaced flag (AWARE ignores it — the .flo is FloLess's authoring format, not AWARE's).
|
|
7
|
+
# workspace:true makes this app show up in the app's Workspaces mode as project cards (drawings →
|
|
8
|
+
# model → exports → History). Steel previously relied on a hard-coded client constant; declaring the
|
|
9
|
+
# flag here is the source of truth. Presentation (which editor, which steps) is decided app-side.
|
|
10
|
+
floless:
|
|
11
|
+
workspace: true
|
|
6
12
|
description: |
|
|
7
13
|
Turn a structural steel drawing into a verifiable takeoff — overlay + properties + a confidence
|
|
8
14
|
report you can judge — then an interactive 3D model (bake to IFC/Tekla) and a formatted BOM, all
|
|
@@ -2,6 +2,12 @@ app: vectorize
|
|
|
2
2
|
version: 0.2.0
|
|
3
3
|
display-name: Vectorize
|
|
4
4
|
publisher: floless
|
|
5
|
+
# FloLess-namespaced flag (AWARE ignores it — the .flo is FloLess's authoring format, not AWARE's).
|
|
6
|
+
# workspace:true makes this app show up in the app's Workspaces mode as a free document workspace —
|
|
7
|
+
# project cards → the 2D vector editor → Approve → SVG export → version History — instead of only in
|
|
8
|
+
# the node-graph Workflows mode. Presentation (which editor, which steps) is decided app-side.
|
|
9
|
+
floless:
|
|
10
|
+
workspace: true
|
|
5
11
|
description: |
|
|
6
12
|
Turn any drawing — a sketch, a PDF, a photo — into clean, editable 2D vectors. Your terminal AI
|
|
7
13
|
reads the drawing ONCE at compose time and bakes it into a drawing.vector contract: a vector PDF
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* analytics.js — a minimal product-analytics seam.
|
|
3
|
+
*
|
|
4
|
+
* The app has no analytics layer today. This is a REAL seam that stays a no-op
|
|
5
|
+
* until a PostHog client is configured on the page (globalThis.posthog with a
|
|
6
|
+
* .capture): wiring the full PostHog SDK pulls in a project key + consent/privacy
|
|
7
|
+
* handling for a desktop app — a dedicated later pass (spec §5.2). Until then,
|
|
8
|
+
* trackEvent() is defined and callable everywhere, and simply does nothing.
|
|
9
|
+
*
|
|
10
|
+
* Loaded as a classic <script> (like app.js/aware.js/panels.js/workspaces.js) and
|
|
11
|
+
* exposed on window — NOT an ESM export — to match the codebase's module pattern.
|
|
12
|
+
* Callers: workspaces.js emits the Slice-6 Workspaces events through window.trackEvent.
|
|
13
|
+
*
|
|
14
|
+
* Contract: analytics must NEVER break UX — every call is wrapped so a missing or
|
|
15
|
+
* throwing client can't surface an error to the user or interrupt a flow.
|
|
16
|
+
* ========================================================================== */
|
|
17
|
+
(() => {
|
|
18
|
+
// Emit a product-analytics event. No-op until a PostHog client is present.
|
|
19
|
+
// name — the event name (snake_case, e.g. 'workspace_project_approved')
|
|
20
|
+
// props — a flat properties object (e.g. { app: 'vectorize', kind: 'svg' })
|
|
21
|
+
function trackEvent(name, props = {}) {
|
|
22
|
+
try {
|
|
23
|
+
if (globalThis.posthog && typeof globalThis.posthog.capture === 'function') {
|
|
24
|
+
globalThis.posthog.capture(name, props);
|
|
25
|
+
}
|
|
26
|
+
} catch { /* never break UX for analytics */ }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Expose the way the other web/*.js modules expose their API (window global, not ESM).
|
|
30
|
+
window.trackEvent = trackEvent;
|
|
31
|
+
})();
|