@bitcobblers/wod-wiki-ui 0.10.42 → 0.11.43
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/{chunk-S77JRUQM.mjs → chunk-PZXX6GBK.mjs} +375 -343
- package/dist/chunk-PZXX6GBK.mjs.map +1 -0
- package/dist/extensions/index.cjs +371 -334
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.mjs +1 -1
- package/dist/index.cjs +377 -356
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -83
- package/dist/index.d.ts +20 -83
- package/dist/index.mjs +8 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-S77JRUQM.mjs.map +0 -1
|
@@ -2820,7 +2820,6 @@ wodWikiWql.WQL_SOURCES.filter((s) => s !== "metrics");
|
|
|
2820
2820
|
function sourcePlane(source) {
|
|
2821
2821
|
return source === "metrics" ? "metrics" : "content";
|
|
2822
2822
|
}
|
|
2823
|
-
var METRICS_ONLY_TYPES = /* @__PURE__ */ new Set(["agg", "metric", "groupby", "rollup", "unit"]);
|
|
2824
2823
|
var SOURCE_OPTIONS = [
|
|
2825
2824
|
{ value: "journal", label: "Journal", description: "Find notes in the personal journal" },
|
|
2826
2825
|
{ value: "collections", label: "Collections", description: "Find notes in workout catalogs" },
|
|
@@ -2896,258 +2895,6 @@ function getClauseMeta(type) {
|
|
|
2896
2895
|
description: ""
|
|
2897
2896
|
};
|
|
2898
2897
|
}
|
|
2899
|
-
function clauseToWql(clause) {
|
|
2900
|
-
if (!clause.value.trim()) return {};
|
|
2901
|
-
const val = clause.value.trim();
|
|
2902
|
-
switch (clause.type) {
|
|
2903
|
-
case "text": {
|
|
2904
|
-
const needsQuotes = /\s/.test(val) && !(val.startsWith('"') && val.endsWith('"'));
|
|
2905
|
-
return { filterStr: `text:${needsQuotes ? `"${val}"` : val}` };
|
|
2906
|
-
}
|
|
2907
|
-
case "catalog":
|
|
2908
|
-
return { filterStr: `catalog:${val}` };
|
|
2909
|
-
case "tag":
|
|
2910
|
-
return { filterStr: `tags:${val}` };
|
|
2911
|
-
case "effort":
|
|
2912
|
-
return { filterStr: `effort:${val}` };
|
|
2913
|
-
case "discipline":
|
|
2914
|
-
return { filterStr: `discipline:${val}` };
|
|
2915
|
-
case "intensity":
|
|
2916
|
-
return { filterStr: `intensity:${val}` };
|
|
2917
|
-
case "origin":
|
|
2918
|
-
return { filterStr: `origin:${val}` };
|
|
2919
|
-
case "type":
|
|
2920
|
-
return { filterStr: `type:${val}` };
|
|
2921
|
-
case "has":
|
|
2922
|
-
return { filterStr: `has:${val}` };
|
|
2923
|
-
case "result":
|
|
2924
|
-
return { filterStr: `result:${val}` };
|
|
2925
|
-
case "block":
|
|
2926
|
-
return { filterStr: `block:${val}` };
|
|
2927
|
-
case "note":
|
|
2928
|
-
return { filterStr: `note:${val}` };
|
|
2929
|
-
default: {
|
|
2930
|
-
const def = composerRegistry.getSlot(clause.type);
|
|
2931
|
-
if (!def) return {};
|
|
2932
|
-
if (!def.parseValue) return {};
|
|
2933
|
-
const value = def.parseValue(val);
|
|
2934
|
-
if (value === void 0) return {};
|
|
2935
|
-
const fragment = def.wqlGenerator(value);
|
|
2936
|
-
return fragment.trim() ? { filterStr: fragment } : {};
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
function clausesToWql(clauses) {
|
|
2941
|
-
const whereClause = clauses.find((c) => c.type === "where");
|
|
2942
|
-
const whereStr = whereClause?.value?.trim() ? `where ${whereClause.value.trim()}` : "";
|
|
2943
|
-
const filterParts = [];
|
|
2944
|
-
for (const c of clauses) {
|
|
2945
|
-
const res = clauseToWql(c);
|
|
2946
|
-
if (res.filterStr) {
|
|
2947
|
-
filterParts.push(res.filterStr);
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
const filterBraces = filterParts.length > 0 ? `{${filterParts.join(", ")}}` : "";
|
|
2951
|
-
const source = clauseValue(clauses, "source", "notes");
|
|
2952
|
-
if (source === "rows") {
|
|
2953
|
-
const output = clauseValue(clauses, "output", "") || "all";
|
|
2954
|
-
const timeClause2 = clauses.find((c) => c.type === "time");
|
|
2955
|
-
let timeStr2 = "";
|
|
2956
|
-
if (timeClause2 && timeClause2.value && timeClause2.value !== "all") {
|
|
2957
|
-
const rawVal = timeClause2.value.trim();
|
|
2958
|
-
timeStr2 = rawVal.startsWith("last") ? rawVal : `last ${rawVal}`;
|
|
2959
|
-
}
|
|
2960
|
-
return [
|
|
2961
|
-
`rows:${output}${filterBraces}`,
|
|
2962
|
-
timeStr2
|
|
2963
|
-
].filter(Boolean).join(" ").trim();
|
|
2964
|
-
}
|
|
2965
|
-
if (source === "metrics") {
|
|
2966
|
-
const agg = clauseValue(clauses, "agg", "sum");
|
|
2967
|
-
const metric = clauseValue(clauses, "metric", "");
|
|
2968
|
-
const dims = clauses.filter((c) => c.type === "groupby" && c.value.trim()).map((c) => c.value.trim());
|
|
2969
|
-
const rollup = clauseValue(clauses, "rollup", "");
|
|
2970
|
-
const unit = clauseValue(clauses, "unit", "");
|
|
2971
|
-
const byStr = dims.length ? `by {${dims.join(", ")}}` : "";
|
|
2972
|
-
const rollupStr = rollup ? `.rollup(${rollup})` : "";
|
|
2973
|
-
const head = byStr ? `${agg}:${metric}${filterBraces}` : `${agg}:${metric}${filterBraces}${rollupStr}`;
|
|
2974
|
-
return [
|
|
2975
|
-
head,
|
|
2976
|
-
`${byStr}${byStr ? rollupStr : ""}`,
|
|
2977
|
-
unit ? `in ${unit}` : "",
|
|
2978
|
-
whereStr
|
|
2979
|
-
].filter(Boolean).join(" ").trim();
|
|
2980
|
-
}
|
|
2981
|
-
const target = source === "blocks" ? "block" : source === "efforts" ? "effort" : "note";
|
|
2982
|
-
const scope = source === "notes" || source === "blocks" || source === "efforts" ? "all" : source;
|
|
2983
|
-
const timeClause = clauses.find((c) => c.type === "time");
|
|
2984
|
-
let timeStr = "";
|
|
2985
|
-
if (timeClause && timeClause.value && timeClause.value !== "all") {
|
|
2986
|
-
const rawVal = timeClause.value.trim();
|
|
2987
|
-
timeStr = rawVal.startsWith("last") ? rawVal : `last ${rawVal}`;
|
|
2988
|
-
}
|
|
2989
|
-
return [
|
|
2990
|
-
`find:${target}${filterBraces}`,
|
|
2991
|
-
`in ${scope}`,
|
|
2992
|
-
timeStr,
|
|
2993
|
-
whereStr
|
|
2994
|
-
].filter(Boolean).join(" ").trim();
|
|
2995
|
-
}
|
|
2996
|
-
var POSITIONAL_CLAUSE_TYPES = /* @__PURE__ */ new Set(["source", "time", "where", "agg", "metric", "groupby", "rollup", "unit", "output"]);
|
|
2997
|
-
var FILTER_KEY_TO_CLAUSE_TYPE = Object.fromEntries(
|
|
2998
|
-
Object.entries(CLAUSE_META).filter(([type]) => !POSITIONAL_CLAUSE_TYPES.has(type)).map(([type, meta]) => [meta.prefix.replace(/:$/, ""), type])
|
|
2999
|
-
);
|
|
3000
|
-
function splitTopLevel(body) {
|
|
3001
|
-
const parts = [];
|
|
3002
|
-
let depth = 0;
|
|
3003
|
-
let inQuote = false;
|
|
3004
|
-
let start = 0;
|
|
3005
|
-
for (let i = 0; i < body.length; i++) {
|
|
3006
|
-
const c = body[i];
|
|
3007
|
-
if (c === '"') inQuote = !inQuote;
|
|
3008
|
-
else if (!inQuote && c === "{") depth++;
|
|
3009
|
-
else if (!inQuote && c === "}") depth = Math.max(0, depth - 1);
|
|
3010
|
-
else if (!inQuote && c === "," && depth === 0) {
|
|
3011
|
-
parts.push(body.slice(start, i));
|
|
3012
|
-
start = i + 1;
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
parts.push(body.slice(start));
|
|
3016
|
-
return parts;
|
|
3017
|
-
}
|
|
3018
|
-
var RESTORE_FIND_HEAD_RE = /^find:([a-zA-Z0-9_-]*)/;
|
|
3019
|
-
var RESTORE_AGG_HEAD_RE = /^([a-zA-Z0-9_-]+):([a-zA-Z0-9_.-]*)/;
|
|
3020
|
-
function restoreClause(id, type, value) {
|
|
3021
|
-
return { id, type, ...getClauseMeta(type), value };
|
|
3022
|
-
}
|
|
3023
|
-
function filterFragmentToClause(fragment, index) {
|
|
3024
|
-
if (fragment.startsWith("!")) return null;
|
|
3025
|
-
const colon2 = fragment.indexOf(":");
|
|
3026
|
-
if (colon2 <= 0) return null;
|
|
3027
|
-
const key = fragment.slice(0, colon2);
|
|
3028
|
-
const value = fragment.slice(colon2 + 1);
|
|
3029
|
-
if (!value.trim()) return null;
|
|
3030
|
-
if (key === "source") {
|
|
3031
|
-
return restoreClause(`c-source-${index}`, "source", value);
|
|
3032
|
-
}
|
|
3033
|
-
const builtin = FILTER_KEY_TO_CLAUSE_TYPE[key];
|
|
3034
|
-
if (builtin) {
|
|
3035
|
-
const restoredValue = builtin === "text" && value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
3036
|
-
return restoreClause(`c-${builtin}-${index}`, builtin, restoredValue);
|
|
3037
|
-
}
|
|
3038
|
-
for (const slot of composerRegistry.getAllSlots()) {
|
|
3039
|
-
if (!slot.parseValue || !slot.formatValue) continue;
|
|
3040
|
-
const typed = slot.parseValue(value);
|
|
3041
|
-
if (typed !== void 0 && slot.wqlGenerator(typed) === fragment) {
|
|
3042
|
-
return restoreClause(`c-${slot.type}-${index}`, slot.type, slot.formatValue(typed));
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
return null;
|
|
3046
|
-
}
|
|
3047
|
-
function restoreFilters(rest) {
|
|
3048
|
-
if (!rest) return [];
|
|
3049
|
-
if (!rest.startsWith("{") || !rest.endsWith("}")) return null;
|
|
3050
|
-
const body = rest.slice(1, -1);
|
|
3051
|
-
const fragments = splitTopLevel(body).map((f) => f.trim()).filter(Boolean);
|
|
3052
|
-
const clauses = [];
|
|
3053
|
-
for (let i = 0; i < fragments.length; i++) {
|
|
3054
|
-
const clause = filterFragmentToClause(fragments[i], i);
|
|
3055
|
-
if (!clause) return null;
|
|
3056
|
-
clauses.push(clause);
|
|
3057
|
-
}
|
|
3058
|
-
return clauses;
|
|
3059
|
-
}
|
|
3060
|
-
function wqlToClauses(wql2) {
|
|
3061
|
-
const suffixes = wodWikiWql.parseWqlSuffixes(wql2.trim());
|
|
3062
|
-
if (suffixes.conflicts?.length) return null;
|
|
3063
|
-
const { where, displayUnit, groupBy, rollup, window: window2, legacyScope, primaryText } = suffixes;
|
|
3064
|
-
if (window2?.kind === "range") return null;
|
|
3065
|
-
const rowsMatch = /^rows(?:\:([a-zA-Z0-9_-]+))?\:?/.exec(primaryText);
|
|
3066
|
-
if (rowsMatch) {
|
|
3067
|
-
if (where || groupBy || rollup) return null;
|
|
3068
|
-
const rest2 = primaryText.slice(rowsMatch[0].length).trim();
|
|
3069
|
-
const filterClauses2 = restoreFilters(rest2);
|
|
3070
|
-
if (!filterClauses2) return null;
|
|
3071
|
-
const clauses2 = [
|
|
3072
|
-
restoreClause("c-source", "source", "rows"),
|
|
3073
|
-
restoreClause("c-time", "time", window2?.kind === "relative" ? `last ${window2.size}${window2.unit}` : "all"),
|
|
3074
|
-
...rowsMatch[1] ? [restoreClause("c-output", "output", rowsMatch[1])] : [],
|
|
3075
|
-
...filterClauses2
|
|
3076
|
-
];
|
|
3077
|
-
return clauses2;
|
|
3078
|
-
}
|
|
3079
|
-
if (primaryText.startsWith("find:")) {
|
|
3080
|
-
const headMatch2 = RESTORE_FIND_HEAD_RE.exec(primaryText);
|
|
3081
|
-
if (!headMatch2) return null;
|
|
3082
|
-
const targetValue = headMatch2[1];
|
|
3083
|
-
if (!wodWikiWql.WQL_FIND_TARGETS.includes(targetValue)) return null;
|
|
3084
|
-
const rest2 = primaryText.slice(headMatch2[0].length).trim();
|
|
3085
|
-
const filterClauses2 = restoreFilters(rest2);
|
|
3086
|
-
if (!filterClauses2) return null;
|
|
3087
|
-
const timeValue = window2?.kind === "relative" ? `last ${window2.size}${window2.unit}` : "all";
|
|
3088
|
-
const sourceClauseIdx = filterClauses2.findIndex((c) => c.type === "source");
|
|
3089
|
-
const sourceFromFilter = sourceClauseIdx !== -1 ? filterClauses2[sourceClauseIdx].value : void 0;
|
|
3090
|
-
const scopeValue = legacyScope ?? sourceFromFilter;
|
|
3091
|
-
const remainingFilterClauses = sourceClauseIdx !== -1 ? filterClauses2.filter((_, idx) => idx !== sourceClauseIdx) : filterClauses2;
|
|
3092
|
-
const sourceValue = targetValue === "block" ? "blocks" : targetValue === "effort" ? "efforts" : !scopeValue || scopeValue === "all" ? "notes" : scopeValue;
|
|
3093
|
-
const clauses2 = [
|
|
3094
|
-
restoreClause("c-source", "source", sourceValue),
|
|
3095
|
-
restoreClause("c-time", "time", timeValue),
|
|
3096
|
-
...remainingFilterClauses
|
|
3097
|
-
];
|
|
3098
|
-
if (where) clauses2.push(restoreClause("c-where", "where", where));
|
|
3099
|
-
return clauses2;
|
|
3100
|
-
}
|
|
3101
|
-
if (window2) return null;
|
|
3102
|
-
const headMatch = RESTORE_AGG_HEAD_RE.exec(primaryText);
|
|
3103
|
-
if (!headMatch) return null;
|
|
3104
|
-
const aggValue = headMatch[1];
|
|
3105
|
-
const metricValue = headMatch[2] ?? "";
|
|
3106
|
-
const rest = primaryText.slice(headMatch[0].length).trim();
|
|
3107
|
-
const filterClauses = restoreFilters(rest);
|
|
3108
|
-
if (!filterClauses) return null;
|
|
3109
|
-
const dimValues = groupBy ?? [];
|
|
3110
|
-
const rollupValue = rollup?.raw ?? "";
|
|
3111
|
-
const unitValue = displayUnit ?? "";
|
|
3112
|
-
const clauses = [
|
|
3113
|
-
restoreClause("c-source", "source", "metrics"),
|
|
3114
|
-
restoreClause("c-agg", "agg", aggValue),
|
|
3115
|
-
restoreClause("c-metric", "metric", metricValue),
|
|
3116
|
-
...filterClauses,
|
|
3117
|
-
...dimValues.map((d, i) => restoreClause(`c-groupby-${i}`, "groupby", d))
|
|
3118
|
-
];
|
|
3119
|
-
if (rollupValue) clauses.push(restoreClause("c-rollup", "rollup", rollupValue));
|
|
3120
|
-
if (unitValue) clauses.push(restoreClause("c-unit", "unit", unitValue));
|
|
3121
|
-
if (where) clauses.push(restoreClause("c-where", "where", where));
|
|
3122
|
-
return clauses;
|
|
3123
|
-
}
|
|
3124
|
-
function pivotClauses(clauses, source) {
|
|
3125
|
-
const next = clauses.filter((c) => !METRICS_ONLY_TYPES.has(c.type) && c.type !== "time" && c.type !== "where").map((c) => c.type === "source" ? { ...c, value: source } : c);
|
|
3126
|
-
if (source === "metrics") {
|
|
3127
|
-
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3128
|
-
const head = [];
|
|
3129
|
-
if (!next.some((c) => c.type === "agg")) head.push(restoreClause("c-agg", "agg", "sum"));
|
|
3130
|
-
if (!next.some((c) => c.type === "metric")) head.push(restoreClause("c-metric", "metric", ""));
|
|
3131
|
-
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, ...head);
|
|
3132
|
-
}
|
|
3133
|
-
return next;
|
|
3134
|
-
}
|
|
3135
|
-
function defaultClauses() {
|
|
3136
|
-
return [
|
|
3137
|
-
{ id: "c-source", type: "source", ...CLAUSE_META.source, value: "notes" },
|
|
3138
|
-
{ id: "c-time", type: "time", ...CLAUSE_META.time, value: "last 2w" }
|
|
3139
|
-
];
|
|
3140
|
-
}
|
|
3141
|
-
function defaultMetricsClauses() {
|
|
3142
|
-
return [
|
|
3143
|
-
{ id: "c-source", type: "source", ...CLAUSE_META.source, value: "metrics" },
|
|
3144
|
-
{ id: "c-agg", type: "agg", ...CLAUSE_META.agg, value: "sum" },
|
|
3145
|
-
{ id: "c-metric", type: "metric", ...CLAUSE_META.metric, value: "" }
|
|
3146
|
-
];
|
|
3147
|
-
}
|
|
3148
|
-
function clauseValue(clauses, type, fallback = "") {
|
|
3149
|
-
return clauses.find((c) => c.type === type)?.value?.trim() || fallback;
|
|
3150
|
-
}
|
|
3151
2898
|
function TokenSlotPill({
|
|
3152
2899
|
clause,
|
|
3153
2900
|
isActive,
|
|
@@ -3599,6 +3346,247 @@ function AddCalcDropdown({
|
|
|
3599
3346
|
)
|
|
3600
3347
|
] });
|
|
3601
3348
|
}
|
|
3349
|
+
var PILL_KEY = {
|
|
3350
|
+
text: "text",
|
|
3351
|
+
catalog: "catalog",
|
|
3352
|
+
tag: "tags",
|
|
3353
|
+
effort: "effort",
|
|
3354
|
+
discipline: "discipline",
|
|
3355
|
+
intensity: "intensity",
|
|
3356
|
+
origin: "origin",
|
|
3357
|
+
type: "type",
|
|
3358
|
+
has: "has",
|
|
3359
|
+
result: "result",
|
|
3360
|
+
block: "block",
|
|
3361
|
+
note: "note"
|
|
3362
|
+
};
|
|
3363
|
+
var KEY_PILL = Object.fromEntries(
|
|
3364
|
+
Object.entries(PILL_KEY).map(([pill, key]) => [key, pill])
|
|
3365
|
+
);
|
|
3366
|
+
function pillValueToFilter(type, rawValue) {
|
|
3367
|
+
const key = PILL_KEY[type];
|
|
3368
|
+
if (!key) return null;
|
|
3369
|
+
const values2 = rawValue.split("|").map((v) => v.trim()).filter(Boolean).map((v) => ({
|
|
3370
|
+
value: v.endsWith("*") ? v.slice(0, -1) : v,
|
|
3371
|
+
wildcard: v.endsWith("*")
|
|
3372
|
+
}));
|
|
3373
|
+
if (values2.length === 0) return null;
|
|
3374
|
+
return { key, negate: false, values: values2 };
|
|
3375
|
+
}
|
|
3376
|
+
function filterFragment(f) {
|
|
3377
|
+
const render = (v) => {
|
|
3378
|
+
const bare = /^[a-zA-Z0-9_-]+(:[a-zA-Z0-9_-]+)?$/.test(v.value);
|
|
3379
|
+
return `${bare ? v.value : `"${v.value}"`}${v.wildcard ? "*" : ""}`;
|
|
3380
|
+
};
|
|
3381
|
+
return `${f.negate ? "!" : ""}${f.key}:${f.values.map(render).join("|")}`;
|
|
3382
|
+
}
|
|
3383
|
+
function filterToPill(f, index) {
|
|
3384
|
+
if (f.negate) return null;
|
|
3385
|
+
const pillValue2 = f.values.map((v) => `${v.value}${v.wildcard ? "*" : ""}`).join("|");
|
|
3386
|
+
if (!pillValue2.trim()) return null;
|
|
3387
|
+
if (f.key === "source") {
|
|
3388
|
+
return restorePill(`c-source-${index}`, "source", pillValue2);
|
|
3389
|
+
}
|
|
3390
|
+
const builtin = KEY_PILL[f.key];
|
|
3391
|
+
if (builtin) {
|
|
3392
|
+
const value = builtin === "text" && f.values.length === 1 && /\s/.test(f.values[0].value) ? f.values[0].value : pillValue2;
|
|
3393
|
+
return restorePill(`c-${builtin}-${index}`, builtin, value);
|
|
3394
|
+
}
|
|
3395
|
+
const fragment = filterFragment(f);
|
|
3396
|
+
for (const slot of composerRegistry.getAllSlots()) {
|
|
3397
|
+
if (!slot.parseValue || !slot.formatValue) continue;
|
|
3398
|
+
const typed = slot.parseValue(pillValue2);
|
|
3399
|
+
if (typed !== void 0 && slot.wqlGenerator(typed) === fragment) {
|
|
3400
|
+
return restorePill(`c-${slot.type}-${index}`, slot.type, slot.formatValue(typed));
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
return null;
|
|
3404
|
+
}
|
|
3405
|
+
function restorePill(id, type, value) {
|
|
3406
|
+
return { id, type, ...getClauseMeta(type), value };
|
|
3407
|
+
}
|
|
3408
|
+
function astToPills(ast) {
|
|
3409
|
+
if (ast.error) return null;
|
|
3410
|
+
if (wodWikiWql.isRowsQuery(ast)) {
|
|
3411
|
+
if (ast.window?.kind === "range") return null;
|
|
3412
|
+
if (ast.filters.some((f) => f.key === "source")) return null;
|
|
3413
|
+
const filterPills = [];
|
|
3414
|
+
for (let i = 0; i < ast.filters.length; i++) {
|
|
3415
|
+
const p = filterToPill(ast.filters[i], i);
|
|
3416
|
+
if (!p) return null;
|
|
3417
|
+
filterPills.push(p);
|
|
3418
|
+
}
|
|
3419
|
+
return [
|
|
3420
|
+
restorePill("c-source", "source", "rows"),
|
|
3421
|
+
restorePill("c-time", "time", windowPillValue(ast.window)),
|
|
3422
|
+
restorePill("c-output", "output", ast.outputType ?? "all"),
|
|
3423
|
+
...filterPills
|
|
3424
|
+
];
|
|
3425
|
+
}
|
|
3426
|
+
if (wodWikiWql.isFindQuery(ast)) {
|
|
3427
|
+
if (ast.window?.kind === "range") return null;
|
|
3428
|
+
const sourceFilter = ast.filters.find((f) => f.key === "source" && !f.negate);
|
|
3429
|
+
const nonIdentitySource = sourceFilter && !sourceFilter.values.every((v) => v.value === "all");
|
|
3430
|
+
if ((ast.target === "block" || ast.target === "effort") && nonIdentitySource) return null;
|
|
3431
|
+
const sourceValue = ast.target === "block" ? "blocks" : ast.target === "effort" ? "efforts" : !sourceFilter || sourceFilter.values.every((v) => v.value === "all") ? "notes" : sourceFilter.values.map((v) => v.value).join("|");
|
|
3432
|
+
const rest = ast.filters.filter((f) => f !== sourceFilter);
|
|
3433
|
+
const filterPills = [];
|
|
3434
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3435
|
+
const p = filterToPill(rest[i], i);
|
|
3436
|
+
if (!p) return null;
|
|
3437
|
+
filterPills.push(p);
|
|
3438
|
+
}
|
|
3439
|
+
const pills = [
|
|
3440
|
+
restorePill("c-source", "source", sourceValue),
|
|
3441
|
+
restorePill("c-time", "time", windowPillValue(ast.window)),
|
|
3442
|
+
...filterPills
|
|
3443
|
+
];
|
|
3444
|
+
if (ast.join) {
|
|
3445
|
+
const j = ast.join;
|
|
3446
|
+
const head = wodWikiWql.serialize({
|
|
3447
|
+
family: "aggregate",
|
|
3448
|
+
raw: "",
|
|
3449
|
+
agg: j.agg,
|
|
3450
|
+
metric: j.metric,
|
|
3451
|
+
filters: j.filters,
|
|
3452
|
+
groupBy: []
|
|
3453
|
+
});
|
|
3454
|
+
pills.push(restorePill("c-where", "where", `${head} ${j.operator} ${j.threshold}`));
|
|
3455
|
+
}
|
|
3456
|
+
return pills;
|
|
3457
|
+
}
|
|
3458
|
+
if (wodWikiWql.isAggregateQuery(ast)) {
|
|
3459
|
+
if (ast.window?.kind === "range") return null;
|
|
3460
|
+
const filterPills = [];
|
|
3461
|
+
for (let i = 0; i < ast.filters.length; i++) {
|
|
3462
|
+
const p = filterToPill(ast.filters[i], i);
|
|
3463
|
+
if (!p) return null;
|
|
3464
|
+
filterPills.push(p);
|
|
3465
|
+
}
|
|
3466
|
+
const pills = [
|
|
3467
|
+
restorePill("c-source", "source", "metrics"),
|
|
3468
|
+
restorePill("c-agg", "agg", ast.agg),
|
|
3469
|
+
restorePill("c-metric", "metric", ast.metric),
|
|
3470
|
+
...filterPills,
|
|
3471
|
+
...ast.groupBy.map((d, i) => restorePill(`c-groupby-${i}`, "groupby", d))
|
|
3472
|
+
];
|
|
3473
|
+
if (ast.rollup) pills.push(restorePill("c-rollup", "rollup", `${ast.rollup.size}${ast.rollup.unit}`));
|
|
3474
|
+
if (ast.displayUnit) pills.push(restorePill("c-unit", "unit", ast.displayUnit));
|
|
3475
|
+
if (ast.window) pills.push(restorePill("c-time", "time", windowPillValue(ast.window)));
|
|
3476
|
+
if (ast.join) {
|
|
3477
|
+
pills.push(
|
|
3478
|
+
restorePill(
|
|
3479
|
+
"c-where",
|
|
3480
|
+
"where",
|
|
3481
|
+
wodWikiWql.serialize({
|
|
3482
|
+
family: "find",
|
|
3483
|
+
raw: "",
|
|
3484
|
+
target: ast.join.target,
|
|
3485
|
+
filters: ast.join.filters,
|
|
3486
|
+
window: ast.join.last ? { kind: "relative", size: ast.join.last.size, unit: ast.join.last.unit } : void 0
|
|
3487
|
+
})
|
|
3488
|
+
)
|
|
3489
|
+
);
|
|
3490
|
+
}
|
|
3491
|
+
return pills;
|
|
3492
|
+
}
|
|
3493
|
+
return null;
|
|
3494
|
+
}
|
|
3495
|
+
function windowPillValue(w) {
|
|
3496
|
+
if (!w) return "all";
|
|
3497
|
+
return `last ${w.size}${w.unit}`;
|
|
3498
|
+
}
|
|
3499
|
+
function wqlToPills(wql2) {
|
|
3500
|
+
const direct = astToPills(wodWikiWql.parseQuery(wql2.trim()));
|
|
3501
|
+
if (direct) return direct;
|
|
3502
|
+
const probed = wql2.trim().replace(/^([a-zA-Z_]\w*):(\s*\{)/, "$1:__placeholder__$2");
|
|
3503
|
+
if (probed === wql2.trim()) return null;
|
|
3504
|
+
const salvaged = astToPills(wodWikiWql.parseQuery(probed));
|
|
3505
|
+
if (!salvaged) return null;
|
|
3506
|
+
return salvaged.map((p) => p.type === "metric" ? { ...p, value: "" } : p);
|
|
3507
|
+
}
|
|
3508
|
+
function timePillText(raw) {
|
|
3509
|
+
const v = raw.trim();
|
|
3510
|
+
if (!v || v === "all") return "";
|
|
3511
|
+
if (v.startsWith("last") || v.startsWith("from")) return v;
|
|
3512
|
+
return `last ${v}`;
|
|
3513
|
+
}
|
|
3514
|
+
function buildCandidateText(pills) {
|
|
3515
|
+
const source = pillValue(pills, "source", "notes");
|
|
3516
|
+
const filterParts = [];
|
|
3517
|
+
for (const pill of pills) {
|
|
3518
|
+
const v = pill.value.trim();
|
|
3519
|
+
if (!v) continue;
|
|
3520
|
+
const builtin = pillValueToFilter(pill.type, v);
|
|
3521
|
+
if (builtin) {
|
|
3522
|
+
filterParts.push(filterFragment(builtin));
|
|
3523
|
+
continue;
|
|
3524
|
+
}
|
|
3525
|
+
const def = composerRegistry.getSlot(pill.type);
|
|
3526
|
+
if (!def?.parseValue) continue;
|
|
3527
|
+
const typed = def.parseValue(v);
|
|
3528
|
+
if (typed === void 0) continue;
|
|
3529
|
+
const fragment = def.wqlGenerator(typed).trim();
|
|
3530
|
+
if (fragment) filterParts.push(fragment);
|
|
3531
|
+
}
|
|
3532
|
+
const timePill = pills.find((c) => c.type === "time");
|
|
3533
|
+
const timeText = timePill ? timePillText(timePill.value) : "";
|
|
3534
|
+
const wherePill = pills.find((c) => c.type === "where");
|
|
3535
|
+
const whereText = wherePill?.value.trim() ? `where ${wherePill.value.trim()}` : "";
|
|
3536
|
+
if (source === "rows") {
|
|
3537
|
+
const output = pillValue(pills, "output", "all") || "all";
|
|
3538
|
+
return [`rows:${output}{${filterParts.join(",")}}`, timeText].filter(Boolean).join(" ").trim();
|
|
3539
|
+
}
|
|
3540
|
+
if (sourcePlane(source) === "metrics") {
|
|
3541
|
+
const agg = pillValue(pills, "agg", "sum");
|
|
3542
|
+
const metric = pillValue(pills, "metric", "");
|
|
3543
|
+
const dims = pills.filter((c) => c.type === "groupby" && c.value.trim()).map((c) => c.value.trim());
|
|
3544
|
+
const rollup = pillValue(pills, "rollup", "");
|
|
3545
|
+
const unit = pillValue(pills, "unit", "");
|
|
3546
|
+
return [
|
|
3547
|
+
`${agg}:${metric}{${filterParts.join(",")}}`,
|
|
3548
|
+
dims.length ? `by {${dims.join(", ")}}` : "",
|
|
3549
|
+
rollup ? `.rollup(${rollup})` : "",
|
|
3550
|
+
unit ? `in ${unit}` : "",
|
|
3551
|
+
timeText,
|
|
3552
|
+
whereText
|
|
3553
|
+
].filter(Boolean).join(" ").trim();
|
|
3554
|
+
}
|
|
3555
|
+
const target = source === "blocks" ? "block" : source === "efforts" ? "effort" : "note";
|
|
3556
|
+
const withSource = source === "notes" || source === "blocks" || source === "efforts" ? filterParts : [`source:${source}`, ...filterParts];
|
|
3557
|
+
const braces = withSource.length ? `{${withSource.join(",")}}` : "";
|
|
3558
|
+
return [`find:${target}${braces}`, timeText, whereText].filter(Boolean).join(" ").trim();
|
|
3559
|
+
}
|
|
3560
|
+
function pillsToAst(pills) {
|
|
3561
|
+
return wodWikiWql.parseQuery(buildCandidateText(pills));
|
|
3562
|
+
}
|
|
3563
|
+
var METRICS_HEAD_TYPES = /* @__PURE__ */ new Set(["agg", "metric", "groupby", "rollup", "unit"]);
|
|
3564
|
+
function pivotPills(pills, source) {
|
|
3565
|
+
const next = pills.filter((c) => !METRICS_HEAD_TYPES.has(c.type) && c.type !== "where").map((c) => c.type === "source" ? { ...c, value: source } : c);
|
|
3566
|
+
if (source === "metrics") {
|
|
3567
|
+
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3568
|
+
const head = [];
|
|
3569
|
+
if (!next.some((c) => c.type === "agg")) head.push(restorePill("c-agg", "agg", "sum"));
|
|
3570
|
+
if (!next.some((c) => c.type === "metric")) head.push(restorePill("c-metric", "metric", ""));
|
|
3571
|
+
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, ...head);
|
|
3572
|
+
}
|
|
3573
|
+
if (source === "rows" && !next.some((c) => c.type === "output")) {
|
|
3574
|
+
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3575
|
+
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, restorePill("c-output", "output", "all"));
|
|
3576
|
+
}
|
|
3577
|
+
return next;
|
|
3578
|
+
}
|
|
3579
|
+
function defaultPills() {
|
|
3580
|
+
return [
|
|
3581
|
+
restorePill("c-source", "source", "notes"),
|
|
3582
|
+
restorePill("c-time", "time", "last 2w")
|
|
3583
|
+
];
|
|
3584
|
+
}
|
|
3585
|
+
function pillValue(pills, type, fallback = "") {
|
|
3586
|
+
return pills.find((c) => c.type === type)?.value?.trim() || fallback;
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
// src/composer/diagnostics.ts
|
|
3602
3590
|
function summarizeFind(ast) {
|
|
3603
3591
|
const sourceFilter = ast.filters.find((f) => f.key === "source");
|
|
3604
3592
|
const scope = sourceFilter ? sourceFilter.values.map((v) => v.value).join(",") : "all";
|
|
@@ -3630,21 +3618,21 @@ function customSlotError(clause) {
|
|
|
3630
3618
|
if (parsed === void 0) return `Invalid ${custom.label} value`;
|
|
3631
3619
|
return custom.validate ? custom.validate(parsed) : null;
|
|
3632
3620
|
}
|
|
3633
|
-
function
|
|
3634
|
-
for (const
|
|
3635
|
-
const error = customSlotError(
|
|
3621
|
+
function diagnosePills(pills) {
|
|
3622
|
+
for (const pill of pills) {
|
|
3623
|
+
const error = customSlotError(pill);
|
|
3636
3624
|
if (error) {
|
|
3637
3625
|
return {
|
|
3638
3626
|
valid: false,
|
|
3639
|
-
wql:
|
|
3627
|
+
wql: wodWikiWql.serialize(pillsToAst(pills)),
|
|
3640
3628
|
ast: { family: "aggregate", raw: "", agg: "sum", metric: "", filters: [], groupBy: [], error },
|
|
3641
3629
|
error,
|
|
3642
|
-
offendingClauseId:
|
|
3630
|
+
offendingClauseId: pill.id
|
|
3643
3631
|
};
|
|
3644
3632
|
}
|
|
3645
3633
|
}
|
|
3646
|
-
const
|
|
3647
|
-
const
|
|
3634
|
+
const ast = pillsToAst(pills);
|
|
3635
|
+
const wql2 = wodWikiWql.serialize(ast);
|
|
3648
3636
|
if (ast.error) {
|
|
3649
3637
|
return {
|
|
3650
3638
|
valid: false,
|
|
@@ -3772,10 +3760,9 @@ function useWqlStageCounts(ast, valid, execute, debounceMs = DEFAULT_DIAGNOSTICS
|
|
|
3772
3760
|
return runnable ? stages : void 0;
|
|
3773
3761
|
}
|
|
3774
3762
|
function WqlComposer({
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
onWqlChange,
|
|
3763
|
+
initialQuery,
|
|
3764
|
+
query: controlledQuery,
|
|
3765
|
+
onQueryChange,
|
|
3779
3766
|
onValidationChange,
|
|
3780
3767
|
onAstChange,
|
|
3781
3768
|
onSubmit,
|
|
@@ -3789,52 +3776,91 @@ function WqlComposer({
|
|
|
3789
3776
|
placeholder = "Type search term and press Enter...",
|
|
3790
3777
|
className
|
|
3791
3778
|
}) {
|
|
3792
|
-
const [
|
|
3793
|
-
() =>
|
|
3779
|
+
const [internalPills, setInternalPills] = React.useState(
|
|
3780
|
+
() => wqlToPills(initialQuery ?? "") ?? defaultPills()
|
|
3781
|
+
);
|
|
3782
|
+
const controlledPills = React.useMemo(
|
|
3783
|
+
() => controlledQuery === void 0 ? void 0 : wqlToPills(controlledQuery),
|
|
3784
|
+
[controlledQuery]
|
|
3794
3785
|
);
|
|
3795
|
-
const
|
|
3786
|
+
const pills = controlledPills ?? internalPills;
|
|
3796
3787
|
const hiddenTypes = React.useMemo(() => new Set(hiddenClauseTypes), [hiddenClauseTypes]);
|
|
3797
3788
|
const [activeSlotIdx, setActiveSlotIdx] = React.useState(null);
|
|
3798
3789
|
const [freeText, setFreeText] = React.useState("");
|
|
3799
3790
|
const inputRef = React.useRef(null);
|
|
3791
|
+
const freeTextInitRef = React.useRef(null);
|
|
3792
|
+
React.useEffect(() => {
|
|
3793
|
+
if (controlledQuery === void 0) return;
|
|
3794
|
+
if (controlledPills === null) {
|
|
3795
|
+
if (freeTextInitRef.current !== controlledQuery) {
|
|
3796
|
+
freeTextInitRef.current = controlledQuery;
|
|
3797
|
+
setFreeText(controlledQuery);
|
|
3798
|
+
}
|
|
3799
|
+
} else if (freeTextInitRef.current !== null) {
|
|
3800
|
+
freeTextInitRef.current = null;
|
|
3801
|
+
setFreeText("");
|
|
3802
|
+
}
|
|
3803
|
+
}, [controlledQuery, controlledPills]);
|
|
3800
3804
|
React.useEffect(() => {
|
|
3801
3805
|
if (!autoFocus) return;
|
|
3802
3806
|
const t = setTimeout(() => inputRef.current?.focus(), 0);
|
|
3803
3807
|
return () => clearTimeout(t);
|
|
3804
3808
|
}, [autoFocus]);
|
|
3805
|
-
const
|
|
3809
|
+
const setPills = React.useCallback(
|
|
3806
3810
|
(next) => {
|
|
3807
|
-
if (
|
|
3808
|
-
onClausesChange?.(next);
|
|
3811
|
+
if (controlledQuery === void 0) setInternalPills(next);
|
|
3809
3812
|
},
|
|
3810
|
-
[
|
|
3813
|
+
[controlledQuery]
|
|
3811
3814
|
);
|
|
3812
3815
|
const registeredSlots = useComposerSlots();
|
|
3813
|
-
const
|
|
3814
|
-
const
|
|
3815
|
-
|
|
3816
|
+
const rawEscape = controlledQuery !== void 0 && controlledPills === null;
|
|
3817
|
+
const diagnostics = React.useMemo(() => {
|
|
3818
|
+
if (rawEscape) {
|
|
3819
|
+
const ast = wodWikiWql.parseQuery(controlledQuery);
|
|
3820
|
+
return {
|
|
3821
|
+
valid: !ast.error,
|
|
3822
|
+
wql: controlledQuery,
|
|
3823
|
+
ast,
|
|
3824
|
+
error: ast.error
|
|
3825
|
+
};
|
|
3826
|
+
}
|
|
3827
|
+
return diagnosePills(pills);
|
|
3828
|
+
}, [rawEscape, controlledQuery, pills, registeredSlots]);
|
|
3829
|
+
const callbacksRef = React.useRef({ onQueryChange, onValidationChange, onAstChange });
|
|
3830
|
+
callbacksRef.current = { onQueryChange, onValidationChange, onAstChange };
|
|
3816
3831
|
React.useEffect(() => {
|
|
3817
3832
|
const validation = diagnostics.valid ? { valid: true } : { valid: false, error: diagnostics.error };
|
|
3818
|
-
callbacksRef.current.
|
|
3833
|
+
if (controlledQuery === void 0) callbacksRef.current.onQueryChange?.(diagnostics.wql);
|
|
3819
3834
|
callbacksRef.current.onAstChange?.(diagnostics.ast);
|
|
3820
3835
|
callbacksRef.current.onValidationChange?.(validation);
|
|
3821
3836
|
}, [diagnostics]);
|
|
3822
3837
|
const stages = useWqlStageCounts(diagnostics.ast, diagnostics.valid, execute, debounceMs);
|
|
3823
|
-
const
|
|
3824
|
-
const offendingLabel =
|
|
3825
|
-
const
|
|
3826
|
-
const updated =
|
|
3827
|
-
const edited =
|
|
3838
|
+
const offendingPill = diagnostics.offendingClauseId ? pills.find((c) => c.id === diagnostics.offendingClauseId) : void 0;
|
|
3839
|
+
const offendingLabel = offendingPill ? getClauseMeta(offendingPill.type).label : void 0;
|
|
3840
|
+
const updatePill = (idx, patch) => {
|
|
3841
|
+
const updated = pills.map((c, i) => i === idx ? { ...c, ...patch } : c);
|
|
3842
|
+
const edited = pills[idx];
|
|
3828
3843
|
if (edited?.type === "source" && patch.value !== void 0 && patch.value !== edited.value) {
|
|
3829
|
-
|
|
3844
|
+
setPills(pivotPills(updated, patch.value));
|
|
3845
|
+
emitIfControlled(pivotPills(updated, patch.value));
|
|
3830
3846
|
return;
|
|
3831
3847
|
}
|
|
3832
|
-
|
|
3848
|
+
setPills(updated);
|
|
3849
|
+
emitIfControlled(updated);
|
|
3833
3850
|
};
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3851
|
+
const removePill = (idx) => {
|
|
3852
|
+
const next = pills.filter((_, i) => i !== idx);
|
|
3853
|
+
setPills(next);
|
|
3854
|
+
emitIfControlled(next);
|
|
3836
3855
|
};
|
|
3837
|
-
const
|
|
3856
|
+
const emitIfControlled = (next) => {
|
|
3857
|
+
if (controlledQuery !== void 0) {
|
|
3858
|
+
const nextAst = pillsToAst(next);
|
|
3859
|
+
const nextWql = wodWikiWql.serialize(nextAst);
|
|
3860
|
+
if (nextWql !== controlledQuery) callbacksRef.current.onQueryChange?.(nextWql);
|
|
3861
|
+
}
|
|
3862
|
+
};
|
|
3863
|
+
const makePill = (type, value) => {
|
|
3838
3864
|
const meta = getClauseMeta(type);
|
|
3839
3865
|
return {
|
|
3840
3866
|
id: `c-${Date.now()}-${Math.random()}`,
|
|
@@ -3848,10 +3874,13 @@ function WqlComposer({
|
|
|
3848
3874
|
const pending = React.useMemo(() => {
|
|
3849
3875
|
const raw = freeText.trim();
|
|
3850
3876
|
if (!raw) return null;
|
|
3851
|
-
const restored =
|
|
3852
|
-
if (restored) {
|
|
3853
|
-
|
|
3854
|
-
|
|
3877
|
+
const restored = wqlToPills(raw);
|
|
3878
|
+
if (restored) return { kind: "query", pills: restored };
|
|
3879
|
+
const parsed = wodWikiWql.parseQuery(raw);
|
|
3880
|
+
if (!parsed.error && /[:{]/.test(raw)) {
|
|
3881
|
+
return { kind: "raw", value: raw };
|
|
3882
|
+
}
|
|
3883
|
+
if (parsed.error && /[:{]/.test(raw)) {
|
|
3855
3884
|
return { kind: "invalid", reason: String(parsed.error) };
|
|
3856
3885
|
}
|
|
3857
3886
|
const words = raw.match(/[a-zA-Z0-9_-]+/g);
|
|
@@ -3863,42 +3892,48 @@ function WqlComposer({
|
|
|
3863
3892
|
}
|
|
3864
3893
|
return { kind: "text", value: words[0] };
|
|
3865
3894
|
}, [freeText]);
|
|
3866
|
-
const
|
|
3867
|
-
const type =
|
|
3868
|
-
const seed = type === "time" ? "last 2w" : type === "where" ? "sum:totalVolume{} > 5000" : type === "agg" ? "sum" : type === "groupby" ? "week" : type === "rollup" ? "1w" :
|
|
3869
|
-
|
|
3895
|
+
const addPill = (pill) => {
|
|
3896
|
+
const type = pill.type;
|
|
3897
|
+
const seed = type === "time" ? "last 2w" : type === "where" ? "sum:totalVolume{} > 5000" : type === "agg" ? "sum" : type === "groupby" ? "week" : type === "rollup" ? "1w" : pill.value;
|
|
3898
|
+
const next = [...pills, { ...pill, value: seed }];
|
|
3899
|
+
setPills(next);
|
|
3900
|
+
emitIfControlled(next);
|
|
3870
3901
|
};
|
|
3871
|
-
const addCalc = (
|
|
3872
|
-
const { type, value } =
|
|
3902
|
+
const addCalc = (pill) => {
|
|
3903
|
+
const { type, value } = pill;
|
|
3873
3904
|
if (type === "metric") {
|
|
3874
|
-
if (
|
|
3905
|
+
if (pills.some((c) => c.type === "metric")) return;
|
|
3875
3906
|
const afterIdx = Math.max(
|
|
3876
|
-
|
|
3877
|
-
|
|
3907
|
+
pills.findIndex((c) => c.type === "agg"),
|
|
3908
|
+
pills.findIndex((c) => c.type === "source")
|
|
3878
3909
|
);
|
|
3879
|
-
const next2 = [...
|
|
3880
|
-
next2.splice(afterIdx + 1, 0,
|
|
3881
|
-
|
|
3910
|
+
const next2 = [...pills];
|
|
3911
|
+
next2.splice(afterIdx + 1, 0, makePill("metric", value));
|
|
3912
|
+
setPills(next2);
|
|
3913
|
+
emitIfControlled(next2);
|
|
3882
3914
|
return;
|
|
3883
3915
|
}
|
|
3884
3916
|
if (type !== "agg") {
|
|
3885
|
-
|
|
3917
|
+
addPill(pill);
|
|
3886
3918
|
return;
|
|
3887
3919
|
}
|
|
3888
3920
|
const agg = value || "sum";
|
|
3889
|
-
const existingIdx =
|
|
3921
|
+
const existingIdx = pills.findIndex((c) => c.type === "agg");
|
|
3890
3922
|
if (existingIdx >= 0) {
|
|
3891
|
-
|
|
3923
|
+
updatePill(existingIdx, { value: agg });
|
|
3892
3924
|
return;
|
|
3893
3925
|
}
|
|
3894
|
-
if (sourcePlane(
|
|
3895
|
-
|
|
3926
|
+
if (sourcePlane(pillValue(pills, "source", "notes")) !== "metrics") {
|
|
3927
|
+
const next2 = pivotPills(pills, "metrics").map((c) => c.type === "agg" ? { ...c, value: agg } : c);
|
|
3928
|
+
setPills(next2);
|
|
3929
|
+
emitIfControlled(next2);
|
|
3896
3930
|
return;
|
|
3897
3931
|
}
|
|
3898
|
-
const sourceIdx =
|
|
3899
|
-
const next = [...
|
|
3900
|
-
next.splice(sourceIdx + 1, 0,
|
|
3901
|
-
|
|
3932
|
+
const sourceIdx = pills.findIndex((c) => c.type === "source");
|
|
3933
|
+
const next = [...pills];
|
|
3934
|
+
next.splice(sourceIdx + 1, 0, makePill("agg", agg));
|
|
3935
|
+
setPills(next);
|
|
3936
|
+
emitIfControlled(next);
|
|
3902
3937
|
};
|
|
3903
3938
|
const handleKeyDown = (e) => {
|
|
3904
3939
|
if (e.key !== "Enter") return;
|
|
@@ -3906,10 +3941,15 @@ function WqlComposer({
|
|
|
3906
3941
|
e.preventDefault();
|
|
3907
3942
|
e.stopPropagation();
|
|
3908
3943
|
if (pending?.kind === "query") {
|
|
3909
|
-
|
|
3944
|
+
setPills(pending.pills);
|
|
3945
|
+
emitIfControlled(pending.pills);
|
|
3910
3946
|
setFreeText("");
|
|
3947
|
+
} else if (pending?.kind === "raw") {
|
|
3948
|
+
onSubmit?.(pending.value);
|
|
3911
3949
|
} else if (pending?.kind === "text") {
|
|
3912
|
-
|
|
3950
|
+
const next = [...pills, makePill("text", pending.value)];
|
|
3951
|
+
setPills(next);
|
|
3952
|
+
emitIfControlled(next);
|
|
3913
3953
|
setFreeText("");
|
|
3914
3954
|
}
|
|
3915
3955
|
return;
|
|
@@ -3932,20 +3972,20 @@ function WqlComposer({
|
|
|
3932
3972
|
"data-testid": "wql-composer",
|
|
3933
3973
|
children: [
|
|
3934
3974
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Command, { className: "size-4 text-amber-500 shrink-0 mr-0.5" }),
|
|
3935
|
-
|
|
3936
|
-
(
|
|
3975
|
+
pills.map(
|
|
3976
|
+
(pill, idx) => hiddenTypes.has(pill.type) ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3937
3977
|
TokenSlotPill,
|
|
3938
3978
|
{
|
|
3939
|
-
clause,
|
|
3979
|
+
clause: pill,
|
|
3940
3980
|
isActive: activeSlotIdx === idx,
|
|
3941
|
-
invalid: diagnostics.offendingClauseId ===
|
|
3942
|
-
invalidReason: diagnostics.offendingClauseId ===
|
|
3981
|
+
invalid: diagnostics.offendingClauseId === pill.id,
|
|
3982
|
+
invalidReason: diagnostics.offendingClauseId === pill.id ? diagnostics.error : void 0,
|
|
3943
3983
|
onClick: () => setActiveSlotIdx(idx),
|
|
3944
|
-
onChange: (patch) =>
|
|
3945
|
-
onRemove: () =>
|
|
3984
|
+
onChange: (patch) => updatePill(idx, patch),
|
|
3985
|
+
onRemove: () => removePill(idx),
|
|
3946
3986
|
compact: true
|
|
3947
3987
|
},
|
|
3948
|
-
|
|
3988
|
+
pill.id
|
|
3949
3989
|
)
|
|
3950
3990
|
),
|
|
3951
3991
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3976,6 +4016,7 @@ function WqlComposer({
|
|
|
3976
4016
|
role: pending.kind === "invalid" ? "alert" : void 0,
|
|
3977
4017
|
children: [
|
|
3978
4018
|
pending.kind === "query" && "\u21B5 Use as query",
|
|
4019
|
+
pending.kind === "raw" && "\u21B5 Run query",
|
|
3979
4020
|
pending.kind === "text" && `\u21B5 Search text: ${pending.value}`,
|
|
3980
4021
|
pending.kind === "invalid" && pending.reason
|
|
3981
4022
|
]
|
|
@@ -3988,14 +4029,14 @@ function WqlComposer({
|
|
|
3988
4029
|
offendingLabel,
|
|
3989
4030
|
stages,
|
|
3990
4031
|
actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3991
|
-
/* @__PURE__ */ jsxRuntime.jsx(AddCalcDropdown, { clauses, onAdd: addCalc }),
|
|
3992
|
-
/* @__PURE__ */ jsxRuntime.jsx(AddFilterDropdown, { clauses, onAdd:
|
|
4032
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddCalcDropdown, { clauses: pills, onAdd: addCalc }),
|
|
4033
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddFilterDropdown, { clauses: pills, onAdd: addPill }),
|
|
3993
4034
|
diagnosticsActions
|
|
3994
4035
|
] })
|
|
3995
4036
|
}
|
|
3996
4037
|
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1.5 px-1.5", "data-testid": "wql-add-row", children: [
|
|
3997
|
-
/* @__PURE__ */ jsxRuntime.jsx(AddCalcDropdown, { clauses, onAdd: addCalc }),
|
|
3998
|
-
/* @__PURE__ */ jsxRuntime.jsx(AddFilterDropdown, { clauses, onAdd:
|
|
4038
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddCalcDropdown, { clauses: pills, onAdd: addCalc }),
|
|
4039
|
+
/* @__PURE__ */ jsxRuntime.jsx(AddFilterDropdown, { clauses: pills, onAdd: addPill }),
|
|
3999
4040
|
diagnosticsActions
|
|
4000
4041
|
] })
|
|
4001
4042
|
] });
|
|
@@ -4009,13 +4050,10 @@ function WqlQueryInspectorModal({
|
|
|
4009
4050
|
subtitle = "Use the Omni-Composer to edit this block query.",
|
|
4010
4051
|
executor
|
|
4011
4052
|
}) {
|
|
4012
|
-
const [
|
|
4053
|
+
const [wql2, setWql] = React.useState(initialQuery);
|
|
4013
4054
|
const [isValid, setIsValid] = React.useState(true);
|
|
4014
4055
|
React.useEffect(() => {
|
|
4015
|
-
if (isOpen)
|
|
4016
|
-
const restored = wqlToClauses(initialQuery);
|
|
4017
|
-
setClauses(restored ?? defaultMetricsClauses());
|
|
4018
|
-
}
|
|
4056
|
+
if (isOpen) setWql(initialQuery);
|
|
4019
4057
|
}, [isOpen, initialQuery]);
|
|
4020
4058
|
const diagnosticsExecutor = React.useCallback(
|
|
4021
4059
|
(ast) => {
|
|
@@ -4033,8 +4071,7 @@ function WqlQueryInspectorModal({
|
|
|
4033
4071
|
);
|
|
4034
4072
|
const handleApply = () => {
|
|
4035
4073
|
if (!isValid) return;
|
|
4036
|
-
|
|
4037
|
-
onApply(nextWql);
|
|
4074
|
+
onApply(wql2);
|
|
4038
4075
|
onClose();
|
|
4039
4076
|
};
|
|
4040
4077
|
if (!isOpen) return null;
|
|
@@ -4065,15 +4102,15 @@ function WqlQueryInspectorModal({
|
|
|
4065
4102
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 overflow-y-auto space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4066
4103
|
WqlComposer,
|
|
4067
4104
|
{
|
|
4068
|
-
|
|
4069
|
-
|
|
4105
|
+
query: wql2,
|
|
4106
|
+
onQueryChange: setWql,
|
|
4070
4107
|
onValidationChange: (v) => setIsValid(v.valid),
|
|
4071
4108
|
execute: diagnosticsExecutor,
|
|
4072
4109
|
showDiagnostics: true
|
|
4073
4110
|
}
|
|
4074
4111
|
) }),
|
|
4075
4112
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-3 border-t border-border/80 bg-muted/10", children: [
|
|
4076
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground font-mono", children:
|
|
4113
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground font-mono", children: wql2 }),
|
|
4077
4114
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4078
4115
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4079
4116
|
"button",
|