@bitcobblers/wod-wiki-ui 0.10.41 → 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
|
@@ -4,7 +4,7 @@ import { tags, styleTags, Tag } from '@lezer/highlight';
|
|
|
4
4
|
import { RangeSetBuilder, Facet, StateEffect, StateField, Prec, EditorState, EditorSelection, countColumn } from '@codemirror/state';
|
|
5
5
|
import { linter, forEachDiagnostic } from '@codemirror/lint';
|
|
6
6
|
import { whiteboardScriptLanguage, createParser, getAnalyticsFromLogs, whiteboardScript } from '@bitcobblers/wod-wiki-lang';
|
|
7
|
-
import { WQL_SOURCES, WQL_AGGREGATORS, WQL_ROLLUP_PERIODS, WQL_VIRTUAL_DIMS, WQL_TAG_KEYS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_CALC_TARGETS, WQL_DISPLAY_UNITS, WQL_INTENSITY_TIERS, parseQueryWidgetSuffix, WQL_COMPARISON_OPS, parseQuery, isRowsQuery, isDashboardWidgetType, unknownWidgetTypeMessage, resolveWidgetType, PLANNED_WIDGET_TYPES,
|
|
7
|
+
import { WQL_SOURCES, WQL_AGGREGATORS, WQL_ROLLUP_PERIODS, WQL_VIRTUAL_DIMS, WQL_TAG_KEYS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_CALC_TARGETS, WQL_DISPLAY_UNITS, WQL_INTENSITY_TIERS, parseQueryWidgetSuffix, WQL_COMPARISON_OPS, parseQuery, isRowsQuery, isDashboardWidgetType, unknownWidgetTypeMessage, resolveWidgetType, PLANNED_WIDGET_TYPES, isFindQuery, isAggregateQuery, splitWidgetBody, substituteTokens, unknownTokensMessage, wql, serialize, wqlLanguage } from '@bitcobblers/wod-wiki-wql';
|
|
8
8
|
import { clsx } from 'clsx';
|
|
9
9
|
import { twMerge } from 'tailwind-merge';
|
|
10
10
|
import { snippet, autocompletion, closeBrackets, closeBracketsKeymap, CompletionContext, ifNotIn, completeFromList, snippetCompletion } from '@codemirror/autocomplete';
|
|
@@ -2872,7 +2872,6 @@ var CONTENT_SOURCES = WQL_SOURCES.filter((s) => s !== "metrics");
|
|
|
2872
2872
|
function sourcePlane(source) {
|
|
2873
2873
|
return source === "metrics" ? "metrics" : "content";
|
|
2874
2874
|
}
|
|
2875
|
-
var METRICS_ONLY_TYPES = /* @__PURE__ */ new Set(["agg", "metric", "groupby", "rollup", "unit"]);
|
|
2876
2875
|
var SOURCE_OPTIONS = [
|
|
2877
2876
|
{ value: "journal", label: "Journal", description: "Find notes in the personal journal" },
|
|
2878
2877
|
{ value: "collections", label: "Collections", description: "Find notes in workout catalogs" },
|
|
@@ -2950,263 +2949,6 @@ function getClauseMeta(type) {
|
|
|
2950
2949
|
description: ""
|
|
2951
2950
|
};
|
|
2952
2951
|
}
|
|
2953
|
-
function clauseToWql(clause) {
|
|
2954
|
-
if (!clause.value.trim()) return {};
|
|
2955
|
-
const val = clause.value.trim();
|
|
2956
|
-
switch (clause.type) {
|
|
2957
|
-
case "text": {
|
|
2958
|
-
const needsQuotes = /\s/.test(val) && !(val.startsWith('"') && val.endsWith('"'));
|
|
2959
|
-
return { filterStr: `text:${needsQuotes ? `"${val}"` : val}` };
|
|
2960
|
-
}
|
|
2961
|
-
case "catalog":
|
|
2962
|
-
return { filterStr: `catalog:${val}` };
|
|
2963
|
-
case "tag":
|
|
2964
|
-
return { filterStr: `tags:${val}` };
|
|
2965
|
-
case "effort":
|
|
2966
|
-
return { filterStr: `effort:${val}` };
|
|
2967
|
-
case "discipline":
|
|
2968
|
-
return { filterStr: `discipline:${val}` };
|
|
2969
|
-
case "intensity":
|
|
2970
|
-
return { filterStr: `intensity:${val}` };
|
|
2971
|
-
case "origin":
|
|
2972
|
-
return { filterStr: `origin:${val}` };
|
|
2973
|
-
case "type":
|
|
2974
|
-
return { filterStr: `type:${val}` };
|
|
2975
|
-
case "has":
|
|
2976
|
-
return { filterStr: `has:${val}` };
|
|
2977
|
-
case "result":
|
|
2978
|
-
return { filterStr: `result:${val}` };
|
|
2979
|
-
case "block":
|
|
2980
|
-
return { filterStr: `block:${val}` };
|
|
2981
|
-
case "note":
|
|
2982
|
-
return { filterStr: `note:${val}` };
|
|
2983
|
-
default: {
|
|
2984
|
-
const def = composerRegistry.getSlot(clause.type);
|
|
2985
|
-
if (!def) return {};
|
|
2986
|
-
if (!def.parseValue) return {};
|
|
2987
|
-
const value = def.parseValue(val);
|
|
2988
|
-
if (value === void 0) return {};
|
|
2989
|
-
const fragment = def.wqlGenerator(value);
|
|
2990
|
-
return fragment.trim() ? { filterStr: fragment } : {};
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
function clausesToWql(clauses) {
|
|
2995
|
-
const whereClause = clauses.find((c) => c.type === "where");
|
|
2996
|
-
const whereStr = whereClause?.value?.trim() ? `where ${whereClause.value.trim()}` : "";
|
|
2997
|
-
const filterParts = [];
|
|
2998
|
-
for (const c of clauses) {
|
|
2999
|
-
const res = clauseToWql(c);
|
|
3000
|
-
if (res.filterStr) {
|
|
3001
|
-
filterParts.push(res.filterStr);
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
const filterBraces = filterParts.length > 0 ? `{${filterParts.join(", ")}}` : "";
|
|
3005
|
-
const source = clauseValue(clauses, "source", "notes");
|
|
3006
|
-
if (source === "rows") {
|
|
3007
|
-
const output = clauseValue(clauses, "output", "") || "all";
|
|
3008
|
-
const timeClause2 = clauses.find((c) => c.type === "time");
|
|
3009
|
-
let timeStr2 = "";
|
|
3010
|
-
if (timeClause2 && timeClause2.value && timeClause2.value !== "all") {
|
|
3011
|
-
const rawVal = timeClause2.value.trim();
|
|
3012
|
-
timeStr2 = rawVal.startsWith("last") ? rawVal : `last ${rawVal}`;
|
|
3013
|
-
}
|
|
3014
|
-
return [
|
|
3015
|
-
`rows:${output}${filterBraces}`,
|
|
3016
|
-
timeStr2
|
|
3017
|
-
].filter(Boolean).join(" ").trim();
|
|
3018
|
-
}
|
|
3019
|
-
if (source === "metrics") {
|
|
3020
|
-
const agg = clauseValue(clauses, "agg", "sum");
|
|
3021
|
-
const metric = clauseValue(clauses, "metric", "");
|
|
3022
|
-
const dims = clauses.filter((c) => c.type === "groupby" && c.value.trim()).map((c) => c.value.trim());
|
|
3023
|
-
const rollup = clauseValue(clauses, "rollup", "");
|
|
3024
|
-
const unit = clauseValue(clauses, "unit", "");
|
|
3025
|
-
const byStr = dims.length ? `by {${dims.join(", ")}}` : "";
|
|
3026
|
-
const rollupStr = rollup ? `.rollup(${rollup})` : "";
|
|
3027
|
-
const head = byStr ? `${agg}:${metric}${filterBraces}` : `${agg}:${metric}${filterBraces}${rollupStr}`;
|
|
3028
|
-
return [
|
|
3029
|
-
head,
|
|
3030
|
-
`${byStr}${byStr ? rollupStr : ""}`,
|
|
3031
|
-
unit ? `in ${unit}` : "",
|
|
3032
|
-
whereStr
|
|
3033
|
-
].filter(Boolean).join(" ").trim();
|
|
3034
|
-
}
|
|
3035
|
-
const target = source === "blocks" ? "block" : source === "efforts" ? "effort" : "note";
|
|
3036
|
-
const scope = source === "notes" || source === "blocks" || source === "efforts" ? "all" : source;
|
|
3037
|
-
const timeClause = clauses.find((c) => c.type === "time");
|
|
3038
|
-
let timeStr = "";
|
|
3039
|
-
if (timeClause && timeClause.value && timeClause.value !== "all") {
|
|
3040
|
-
const rawVal = timeClause.value.trim();
|
|
3041
|
-
timeStr = rawVal.startsWith("last") ? rawVal : `last ${rawVal}`;
|
|
3042
|
-
}
|
|
3043
|
-
return [
|
|
3044
|
-
`find:${target}${filterBraces}`,
|
|
3045
|
-
`in ${scope}`,
|
|
3046
|
-
timeStr,
|
|
3047
|
-
whereStr
|
|
3048
|
-
].filter(Boolean).join(" ").trim();
|
|
3049
|
-
}
|
|
3050
|
-
var POSITIONAL_CLAUSE_TYPES = /* @__PURE__ */ new Set(["source", "time", "where", "agg", "metric", "groupby", "rollup", "unit", "output"]);
|
|
3051
|
-
var FILTER_KEY_TO_CLAUSE_TYPE = Object.fromEntries(
|
|
3052
|
-
Object.entries(CLAUSE_META).filter(([type]) => !POSITIONAL_CLAUSE_TYPES.has(type)).map(([type, meta]) => [meta.prefix.replace(/:$/, ""), type])
|
|
3053
|
-
);
|
|
3054
|
-
function splitTopLevel(body) {
|
|
3055
|
-
const parts = [];
|
|
3056
|
-
let depth = 0;
|
|
3057
|
-
let inQuote = false;
|
|
3058
|
-
let start = 0;
|
|
3059
|
-
for (let i = 0; i < body.length; i++) {
|
|
3060
|
-
const c = body[i];
|
|
3061
|
-
if (c === '"') inQuote = !inQuote;
|
|
3062
|
-
else if (!inQuote && c === "{") depth++;
|
|
3063
|
-
else if (!inQuote && c === "}") depth = Math.max(0, depth - 1);
|
|
3064
|
-
else if (!inQuote && c === "," && depth === 0) {
|
|
3065
|
-
parts.push(body.slice(start, i));
|
|
3066
|
-
start = i + 1;
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
parts.push(body.slice(start));
|
|
3070
|
-
return parts;
|
|
3071
|
-
}
|
|
3072
|
-
var RESTORE_FIND_HEAD_RE = /^find:([a-zA-Z0-9_-]*)/;
|
|
3073
|
-
var RESTORE_AGG_HEAD_RE = /^([a-zA-Z0-9_-]+):([a-zA-Z0-9_.-]*)/;
|
|
3074
|
-
function restoreClause(id, type, value) {
|
|
3075
|
-
return { id, type, ...getClauseMeta(type), value };
|
|
3076
|
-
}
|
|
3077
|
-
function filterFragmentToClause(fragment, index) {
|
|
3078
|
-
if (fragment.startsWith("!")) return null;
|
|
3079
|
-
const colon2 = fragment.indexOf(":");
|
|
3080
|
-
if (colon2 <= 0) return null;
|
|
3081
|
-
const key = fragment.slice(0, colon2);
|
|
3082
|
-
const value = fragment.slice(colon2 + 1);
|
|
3083
|
-
if (!value.trim()) return null;
|
|
3084
|
-
if (key === "source") {
|
|
3085
|
-
return restoreClause(`c-source-${index}`, "source", value);
|
|
3086
|
-
}
|
|
3087
|
-
const builtin = FILTER_KEY_TO_CLAUSE_TYPE[key];
|
|
3088
|
-
if (builtin) {
|
|
3089
|
-
const restoredValue = builtin === "text" && value.length >= 2 && value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
|
|
3090
|
-
return restoreClause(`c-${builtin}-${index}`, builtin, restoredValue);
|
|
3091
|
-
}
|
|
3092
|
-
for (const slot of composerRegistry.getAllSlots()) {
|
|
3093
|
-
if (!slot.parseValue || !slot.formatValue) continue;
|
|
3094
|
-
const typed = slot.parseValue(value);
|
|
3095
|
-
if (typed !== void 0 && slot.wqlGenerator(typed) === fragment) {
|
|
3096
|
-
return restoreClause(`c-${slot.type}-${index}`, slot.type, slot.formatValue(typed));
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
return null;
|
|
3100
|
-
}
|
|
3101
|
-
function restoreFilters(rest) {
|
|
3102
|
-
if (!rest) return [];
|
|
3103
|
-
if (!rest.startsWith("{") || !rest.endsWith("}")) return null;
|
|
3104
|
-
const body = rest.slice(1, -1);
|
|
3105
|
-
const fragments = splitTopLevel(body).map((f) => f.trim()).filter(Boolean);
|
|
3106
|
-
const clauses = [];
|
|
3107
|
-
for (let i = 0; i < fragments.length; i++) {
|
|
3108
|
-
const clause = filterFragmentToClause(fragments[i], i);
|
|
3109
|
-
if (!clause) return null;
|
|
3110
|
-
clauses.push(clause);
|
|
3111
|
-
}
|
|
3112
|
-
return clauses;
|
|
3113
|
-
}
|
|
3114
|
-
function wqlToClauses(wql2) {
|
|
3115
|
-
const suffixes = parseWqlSuffixes(wql2.trim());
|
|
3116
|
-
if (suffixes.conflicts?.length) return null;
|
|
3117
|
-
const { where, displayUnit, groupBy, rollup, window: window2, legacyScope, primaryText } = suffixes;
|
|
3118
|
-
if (window2?.kind === "range") return null;
|
|
3119
|
-
const rowsMatch = /^rows(?:\:([a-zA-Z0-9_-]+))?\:?/.exec(primaryText);
|
|
3120
|
-
if (rowsMatch) {
|
|
3121
|
-
if (where || groupBy || rollup) return null;
|
|
3122
|
-
const rest2 = primaryText.slice(rowsMatch[0].length).trim();
|
|
3123
|
-
const filterClauses2 = restoreFilters(rest2);
|
|
3124
|
-
if (!filterClauses2) return null;
|
|
3125
|
-
const clauses2 = [
|
|
3126
|
-
restoreClause("c-source", "source", "rows"),
|
|
3127
|
-
restoreClause("c-time", "time", window2?.kind === "relative" ? `last ${window2.size}${window2.unit}` : "all"),
|
|
3128
|
-
...rowsMatch[1] ? [restoreClause("c-output", "output", rowsMatch[1])] : [],
|
|
3129
|
-
...filterClauses2
|
|
3130
|
-
];
|
|
3131
|
-
return clauses2;
|
|
3132
|
-
}
|
|
3133
|
-
if (primaryText.startsWith("find:")) {
|
|
3134
|
-
const headMatch2 = RESTORE_FIND_HEAD_RE.exec(primaryText);
|
|
3135
|
-
if (!headMatch2) return null;
|
|
3136
|
-
const targetValue = headMatch2[1];
|
|
3137
|
-
if (!WQL_FIND_TARGETS.includes(targetValue)) return null;
|
|
3138
|
-
const rest2 = primaryText.slice(headMatch2[0].length).trim();
|
|
3139
|
-
const filterClauses2 = restoreFilters(rest2);
|
|
3140
|
-
if (!filterClauses2) return null;
|
|
3141
|
-
const timeValue = window2?.kind === "relative" ? `last ${window2.size}${window2.unit}` : "all";
|
|
3142
|
-
const sourceClauseIdx = filterClauses2.findIndex((c) => c.type === "source");
|
|
3143
|
-
const sourceFromFilter = sourceClauseIdx !== -1 ? filterClauses2[sourceClauseIdx].value : void 0;
|
|
3144
|
-
const scopeValue = legacyScope ?? sourceFromFilter;
|
|
3145
|
-
const remainingFilterClauses = sourceClauseIdx !== -1 ? filterClauses2.filter((_, idx) => idx !== sourceClauseIdx) : filterClauses2;
|
|
3146
|
-
const sourceValue = targetValue === "block" ? "blocks" : targetValue === "effort" ? "efforts" : !scopeValue || scopeValue === "all" ? "notes" : scopeValue;
|
|
3147
|
-
const clauses2 = [
|
|
3148
|
-
restoreClause("c-source", "source", sourceValue),
|
|
3149
|
-
restoreClause("c-time", "time", timeValue),
|
|
3150
|
-
...remainingFilterClauses
|
|
3151
|
-
];
|
|
3152
|
-
if (where) clauses2.push(restoreClause("c-where", "where", where));
|
|
3153
|
-
return clauses2;
|
|
3154
|
-
}
|
|
3155
|
-
if (window2) return null;
|
|
3156
|
-
const headMatch = RESTORE_AGG_HEAD_RE.exec(primaryText);
|
|
3157
|
-
if (!headMatch) return null;
|
|
3158
|
-
const aggValue = headMatch[1];
|
|
3159
|
-
const metricValue = headMatch[2] ?? "";
|
|
3160
|
-
const rest = primaryText.slice(headMatch[0].length).trim();
|
|
3161
|
-
const filterClauses = restoreFilters(rest);
|
|
3162
|
-
if (!filterClauses) return null;
|
|
3163
|
-
const dimValues = groupBy ?? [];
|
|
3164
|
-
const rollupValue = rollup?.raw ?? "";
|
|
3165
|
-
const unitValue = displayUnit ?? "";
|
|
3166
|
-
const clauses = [
|
|
3167
|
-
restoreClause("c-source", "source", "metrics"),
|
|
3168
|
-
restoreClause("c-agg", "agg", aggValue),
|
|
3169
|
-
restoreClause("c-metric", "metric", metricValue),
|
|
3170
|
-
...filterClauses,
|
|
3171
|
-
...dimValues.map((d, i) => restoreClause(`c-groupby-${i}`, "groupby", d))
|
|
3172
|
-
];
|
|
3173
|
-
if (rollupValue) clauses.push(restoreClause("c-rollup", "rollup", rollupValue));
|
|
3174
|
-
if (unitValue) clauses.push(restoreClause("c-unit", "unit", unitValue));
|
|
3175
|
-
if (where) clauses.push(restoreClause("c-where", "where", where));
|
|
3176
|
-
return clauses;
|
|
3177
|
-
}
|
|
3178
|
-
function setMetricClause(clauses, metric) {
|
|
3179
|
-
const withSource = clauses.some((c) => c.type === "source") ? clauses : [restoreClause("c-source", "source", "metrics"), ...clauses];
|
|
3180
|
-
const pivoted = clauseValue(withSource, "source") === "metrics" ? withSource : pivotClauses(withSource, "metrics");
|
|
3181
|
-
return pivoted.some((c) => c.type === "metric") ? pivoted.map((c) => c.type === "metric" ? { ...c, value: metric } : c) : [...pivoted, restoreClause("c-metric", "metric", metric)];
|
|
3182
|
-
}
|
|
3183
|
-
function pivotClauses(clauses, source) {
|
|
3184
|
-
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);
|
|
3185
|
-
if (source === "metrics") {
|
|
3186
|
-
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3187
|
-
const head = [];
|
|
3188
|
-
if (!next.some((c) => c.type === "agg")) head.push(restoreClause("c-agg", "agg", "sum"));
|
|
3189
|
-
if (!next.some((c) => c.type === "metric")) head.push(restoreClause("c-metric", "metric", ""));
|
|
3190
|
-
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, ...head);
|
|
3191
|
-
}
|
|
3192
|
-
return next;
|
|
3193
|
-
}
|
|
3194
|
-
function defaultClauses() {
|
|
3195
|
-
return [
|
|
3196
|
-
{ id: "c-source", type: "source", ...CLAUSE_META.source, value: "notes" },
|
|
3197
|
-
{ id: "c-time", type: "time", ...CLAUSE_META.time, value: "last 2w" }
|
|
3198
|
-
];
|
|
3199
|
-
}
|
|
3200
|
-
function defaultMetricsClauses() {
|
|
3201
|
-
return [
|
|
3202
|
-
{ id: "c-source", type: "source", ...CLAUSE_META.source, value: "metrics" },
|
|
3203
|
-
{ id: "c-agg", type: "agg", ...CLAUSE_META.agg, value: "sum" },
|
|
3204
|
-
{ id: "c-metric", type: "metric", ...CLAUSE_META.metric, value: "" }
|
|
3205
|
-
];
|
|
3206
|
-
}
|
|
3207
|
-
function clauseValue(clauses, type, fallback = "") {
|
|
3208
|
-
return clauses.find((c) => c.type === type)?.value?.trim() || fallback;
|
|
3209
|
-
}
|
|
3210
2952
|
function TokenSlotPill({
|
|
3211
2953
|
clause,
|
|
3212
2954
|
isActive,
|
|
@@ -3658,6 +3400,247 @@ function AddCalcDropdown({
|
|
|
3658
3400
|
)
|
|
3659
3401
|
] });
|
|
3660
3402
|
}
|
|
3403
|
+
var PILL_KEY = {
|
|
3404
|
+
text: "text",
|
|
3405
|
+
catalog: "catalog",
|
|
3406
|
+
tag: "tags",
|
|
3407
|
+
effort: "effort",
|
|
3408
|
+
discipline: "discipline",
|
|
3409
|
+
intensity: "intensity",
|
|
3410
|
+
origin: "origin",
|
|
3411
|
+
type: "type",
|
|
3412
|
+
has: "has",
|
|
3413
|
+
result: "result",
|
|
3414
|
+
block: "block",
|
|
3415
|
+
note: "note"
|
|
3416
|
+
};
|
|
3417
|
+
var KEY_PILL = Object.fromEntries(
|
|
3418
|
+
Object.entries(PILL_KEY).map(([pill, key]) => [key, pill])
|
|
3419
|
+
);
|
|
3420
|
+
function pillValueToFilter(type, rawValue) {
|
|
3421
|
+
const key = PILL_KEY[type];
|
|
3422
|
+
if (!key) return null;
|
|
3423
|
+
const values2 = rawValue.split("|").map((v) => v.trim()).filter(Boolean).map((v) => ({
|
|
3424
|
+
value: v.endsWith("*") ? v.slice(0, -1) : v,
|
|
3425
|
+
wildcard: v.endsWith("*")
|
|
3426
|
+
}));
|
|
3427
|
+
if (values2.length === 0) return null;
|
|
3428
|
+
return { key, negate: false, values: values2 };
|
|
3429
|
+
}
|
|
3430
|
+
function filterFragment(f) {
|
|
3431
|
+
const render = (v) => {
|
|
3432
|
+
const bare = /^[a-zA-Z0-9_-]+(:[a-zA-Z0-9_-]+)?$/.test(v.value);
|
|
3433
|
+
return `${bare ? v.value : `"${v.value}"`}${v.wildcard ? "*" : ""}`;
|
|
3434
|
+
};
|
|
3435
|
+
return `${f.negate ? "!" : ""}${f.key}:${f.values.map(render).join("|")}`;
|
|
3436
|
+
}
|
|
3437
|
+
function filterToPill(f, index) {
|
|
3438
|
+
if (f.negate) return null;
|
|
3439
|
+
const pillValue2 = f.values.map((v) => `${v.value}${v.wildcard ? "*" : ""}`).join("|");
|
|
3440
|
+
if (!pillValue2.trim()) return null;
|
|
3441
|
+
if (f.key === "source") {
|
|
3442
|
+
return restorePill(`c-source-${index}`, "source", pillValue2);
|
|
3443
|
+
}
|
|
3444
|
+
const builtin = KEY_PILL[f.key];
|
|
3445
|
+
if (builtin) {
|
|
3446
|
+
const value = builtin === "text" && f.values.length === 1 && /\s/.test(f.values[0].value) ? f.values[0].value : pillValue2;
|
|
3447
|
+
return restorePill(`c-${builtin}-${index}`, builtin, value);
|
|
3448
|
+
}
|
|
3449
|
+
const fragment = filterFragment(f);
|
|
3450
|
+
for (const slot of composerRegistry.getAllSlots()) {
|
|
3451
|
+
if (!slot.parseValue || !slot.formatValue) continue;
|
|
3452
|
+
const typed = slot.parseValue(pillValue2);
|
|
3453
|
+
if (typed !== void 0 && slot.wqlGenerator(typed) === fragment) {
|
|
3454
|
+
return restorePill(`c-${slot.type}-${index}`, slot.type, slot.formatValue(typed));
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3457
|
+
return null;
|
|
3458
|
+
}
|
|
3459
|
+
function restorePill(id, type, value) {
|
|
3460
|
+
return { id, type, ...getClauseMeta(type), value };
|
|
3461
|
+
}
|
|
3462
|
+
function astToPills(ast) {
|
|
3463
|
+
if (ast.error) return null;
|
|
3464
|
+
if (isRowsQuery(ast)) {
|
|
3465
|
+
if (ast.window?.kind === "range") return null;
|
|
3466
|
+
if (ast.filters.some((f) => f.key === "source")) return null;
|
|
3467
|
+
const filterPills = [];
|
|
3468
|
+
for (let i = 0; i < ast.filters.length; i++) {
|
|
3469
|
+
const p = filterToPill(ast.filters[i], i);
|
|
3470
|
+
if (!p) return null;
|
|
3471
|
+
filterPills.push(p);
|
|
3472
|
+
}
|
|
3473
|
+
return [
|
|
3474
|
+
restorePill("c-source", "source", "rows"),
|
|
3475
|
+
restorePill("c-time", "time", windowPillValue(ast.window)),
|
|
3476
|
+
restorePill("c-output", "output", ast.outputType ?? "all"),
|
|
3477
|
+
...filterPills
|
|
3478
|
+
];
|
|
3479
|
+
}
|
|
3480
|
+
if (isFindQuery(ast)) {
|
|
3481
|
+
if (ast.window?.kind === "range") return null;
|
|
3482
|
+
const sourceFilter = ast.filters.find((f) => f.key === "source" && !f.negate);
|
|
3483
|
+
const nonIdentitySource = sourceFilter && !sourceFilter.values.every((v) => v.value === "all");
|
|
3484
|
+
if ((ast.target === "block" || ast.target === "effort") && nonIdentitySource) return null;
|
|
3485
|
+
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("|");
|
|
3486
|
+
const rest = ast.filters.filter((f) => f !== sourceFilter);
|
|
3487
|
+
const filterPills = [];
|
|
3488
|
+
for (let i = 0; i < rest.length; i++) {
|
|
3489
|
+
const p = filterToPill(rest[i], i);
|
|
3490
|
+
if (!p) return null;
|
|
3491
|
+
filterPills.push(p);
|
|
3492
|
+
}
|
|
3493
|
+
const pills = [
|
|
3494
|
+
restorePill("c-source", "source", sourceValue),
|
|
3495
|
+
restorePill("c-time", "time", windowPillValue(ast.window)),
|
|
3496
|
+
...filterPills
|
|
3497
|
+
];
|
|
3498
|
+
if (ast.join) {
|
|
3499
|
+
const j = ast.join;
|
|
3500
|
+
const head = serialize({
|
|
3501
|
+
family: "aggregate",
|
|
3502
|
+
raw: "",
|
|
3503
|
+
agg: j.agg,
|
|
3504
|
+
metric: j.metric,
|
|
3505
|
+
filters: j.filters,
|
|
3506
|
+
groupBy: []
|
|
3507
|
+
});
|
|
3508
|
+
pills.push(restorePill("c-where", "where", `${head} ${j.operator} ${j.threshold}`));
|
|
3509
|
+
}
|
|
3510
|
+
return pills;
|
|
3511
|
+
}
|
|
3512
|
+
if (isAggregateQuery(ast)) {
|
|
3513
|
+
if (ast.window?.kind === "range") return null;
|
|
3514
|
+
const filterPills = [];
|
|
3515
|
+
for (let i = 0; i < ast.filters.length; i++) {
|
|
3516
|
+
const p = filterToPill(ast.filters[i], i);
|
|
3517
|
+
if (!p) return null;
|
|
3518
|
+
filterPills.push(p);
|
|
3519
|
+
}
|
|
3520
|
+
const pills = [
|
|
3521
|
+
restorePill("c-source", "source", "metrics"),
|
|
3522
|
+
restorePill("c-agg", "agg", ast.agg),
|
|
3523
|
+
restorePill("c-metric", "metric", ast.metric),
|
|
3524
|
+
...filterPills,
|
|
3525
|
+
...ast.groupBy.map((d, i) => restorePill(`c-groupby-${i}`, "groupby", d))
|
|
3526
|
+
];
|
|
3527
|
+
if (ast.rollup) pills.push(restorePill("c-rollup", "rollup", `${ast.rollup.size}${ast.rollup.unit}`));
|
|
3528
|
+
if (ast.displayUnit) pills.push(restorePill("c-unit", "unit", ast.displayUnit));
|
|
3529
|
+
if (ast.window) pills.push(restorePill("c-time", "time", windowPillValue(ast.window)));
|
|
3530
|
+
if (ast.join) {
|
|
3531
|
+
pills.push(
|
|
3532
|
+
restorePill(
|
|
3533
|
+
"c-where",
|
|
3534
|
+
"where",
|
|
3535
|
+
serialize({
|
|
3536
|
+
family: "find",
|
|
3537
|
+
raw: "",
|
|
3538
|
+
target: ast.join.target,
|
|
3539
|
+
filters: ast.join.filters,
|
|
3540
|
+
window: ast.join.last ? { kind: "relative", size: ast.join.last.size, unit: ast.join.last.unit } : void 0
|
|
3541
|
+
})
|
|
3542
|
+
)
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3545
|
+
return pills;
|
|
3546
|
+
}
|
|
3547
|
+
return null;
|
|
3548
|
+
}
|
|
3549
|
+
function windowPillValue(w) {
|
|
3550
|
+
if (!w) return "all";
|
|
3551
|
+
return `last ${w.size}${w.unit}`;
|
|
3552
|
+
}
|
|
3553
|
+
function wqlToPills(wql2) {
|
|
3554
|
+
const direct = astToPills(parseQuery(wql2.trim()));
|
|
3555
|
+
if (direct) return direct;
|
|
3556
|
+
const probed = wql2.trim().replace(/^([a-zA-Z_]\w*):(\s*\{)/, "$1:__placeholder__$2");
|
|
3557
|
+
if (probed === wql2.trim()) return null;
|
|
3558
|
+
const salvaged = astToPills(parseQuery(probed));
|
|
3559
|
+
if (!salvaged) return null;
|
|
3560
|
+
return salvaged.map((p) => p.type === "metric" ? { ...p, value: "" } : p);
|
|
3561
|
+
}
|
|
3562
|
+
function timePillText(raw) {
|
|
3563
|
+
const v = raw.trim();
|
|
3564
|
+
if (!v || v === "all") return "";
|
|
3565
|
+
if (v.startsWith("last") || v.startsWith("from")) return v;
|
|
3566
|
+
return `last ${v}`;
|
|
3567
|
+
}
|
|
3568
|
+
function buildCandidateText(pills) {
|
|
3569
|
+
const source = pillValue(pills, "source", "notes");
|
|
3570
|
+
const filterParts = [];
|
|
3571
|
+
for (const pill of pills) {
|
|
3572
|
+
const v = pill.value.trim();
|
|
3573
|
+
if (!v) continue;
|
|
3574
|
+
const builtin = pillValueToFilter(pill.type, v);
|
|
3575
|
+
if (builtin) {
|
|
3576
|
+
filterParts.push(filterFragment(builtin));
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
const def = composerRegistry.getSlot(pill.type);
|
|
3580
|
+
if (!def?.parseValue) continue;
|
|
3581
|
+
const typed = def.parseValue(v);
|
|
3582
|
+
if (typed === void 0) continue;
|
|
3583
|
+
const fragment = def.wqlGenerator(typed).trim();
|
|
3584
|
+
if (fragment) filterParts.push(fragment);
|
|
3585
|
+
}
|
|
3586
|
+
const timePill = pills.find((c) => c.type === "time");
|
|
3587
|
+
const timeText = timePill ? timePillText(timePill.value) : "";
|
|
3588
|
+
const wherePill = pills.find((c) => c.type === "where");
|
|
3589
|
+
const whereText = wherePill?.value.trim() ? `where ${wherePill.value.trim()}` : "";
|
|
3590
|
+
if (source === "rows") {
|
|
3591
|
+
const output = pillValue(pills, "output", "all") || "all";
|
|
3592
|
+
return [`rows:${output}{${filterParts.join(",")}}`, timeText].filter(Boolean).join(" ").trim();
|
|
3593
|
+
}
|
|
3594
|
+
if (sourcePlane(source) === "metrics") {
|
|
3595
|
+
const agg = pillValue(pills, "agg", "sum");
|
|
3596
|
+
const metric = pillValue(pills, "metric", "");
|
|
3597
|
+
const dims = pills.filter((c) => c.type === "groupby" && c.value.trim()).map((c) => c.value.trim());
|
|
3598
|
+
const rollup = pillValue(pills, "rollup", "");
|
|
3599
|
+
const unit = pillValue(pills, "unit", "");
|
|
3600
|
+
return [
|
|
3601
|
+
`${agg}:${metric}{${filterParts.join(",")}}`,
|
|
3602
|
+
dims.length ? `by {${dims.join(", ")}}` : "",
|
|
3603
|
+
rollup ? `.rollup(${rollup})` : "",
|
|
3604
|
+
unit ? `in ${unit}` : "",
|
|
3605
|
+
timeText,
|
|
3606
|
+
whereText
|
|
3607
|
+
].filter(Boolean).join(" ").trim();
|
|
3608
|
+
}
|
|
3609
|
+
const target = source === "blocks" ? "block" : source === "efforts" ? "effort" : "note";
|
|
3610
|
+
const withSource = source === "notes" || source === "blocks" || source === "efforts" ? filterParts : [`source:${source}`, ...filterParts];
|
|
3611
|
+
const braces = withSource.length ? `{${withSource.join(",")}}` : "";
|
|
3612
|
+
return [`find:${target}${braces}`, timeText, whereText].filter(Boolean).join(" ").trim();
|
|
3613
|
+
}
|
|
3614
|
+
function pillsToAst(pills) {
|
|
3615
|
+
return parseQuery(buildCandidateText(pills));
|
|
3616
|
+
}
|
|
3617
|
+
var METRICS_HEAD_TYPES = /* @__PURE__ */ new Set(["agg", "metric", "groupby", "rollup", "unit"]);
|
|
3618
|
+
function pivotPills(pills, source) {
|
|
3619
|
+
const next = pills.filter((c) => !METRICS_HEAD_TYPES.has(c.type) && c.type !== "where").map((c) => c.type === "source" ? { ...c, value: source } : c);
|
|
3620
|
+
if (source === "metrics") {
|
|
3621
|
+
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3622
|
+
const head = [];
|
|
3623
|
+
if (!next.some((c) => c.type === "agg")) head.push(restorePill("c-agg", "agg", "sum"));
|
|
3624
|
+
if (!next.some((c) => c.type === "metric")) head.push(restorePill("c-metric", "metric", ""));
|
|
3625
|
+
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, ...head);
|
|
3626
|
+
}
|
|
3627
|
+
if (source === "rows" && !next.some((c) => c.type === "output")) {
|
|
3628
|
+
const sourceIdx = next.findIndex((c) => c.type === "source");
|
|
3629
|
+
next.splice(sourceIdx >= 0 ? sourceIdx + 1 : next.length, 0, restorePill("c-output", "output", "all"));
|
|
3630
|
+
}
|
|
3631
|
+
return next;
|
|
3632
|
+
}
|
|
3633
|
+
function defaultPills() {
|
|
3634
|
+
return [
|
|
3635
|
+
restorePill("c-source", "source", "notes"),
|
|
3636
|
+
restorePill("c-time", "time", "last 2w")
|
|
3637
|
+
];
|
|
3638
|
+
}
|
|
3639
|
+
function pillValue(pills, type, fallback = "") {
|
|
3640
|
+
return pills.find((c) => c.type === type)?.value?.trim() || fallback;
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
// src/composer/diagnostics.ts
|
|
3661
3644
|
function summarizeFind(ast) {
|
|
3662
3645
|
const sourceFilter = ast.filters.find((f) => f.key === "source");
|
|
3663
3646
|
const scope = sourceFilter ? sourceFilter.values.map((v) => v.value).join(",") : "all";
|
|
@@ -3689,21 +3672,21 @@ function customSlotError(clause) {
|
|
|
3689
3672
|
if (parsed === void 0) return `Invalid ${custom.label} value`;
|
|
3690
3673
|
return custom.validate ? custom.validate(parsed) : null;
|
|
3691
3674
|
}
|
|
3692
|
-
function
|
|
3693
|
-
for (const
|
|
3694
|
-
const error = customSlotError(
|
|
3675
|
+
function diagnosePills(pills) {
|
|
3676
|
+
for (const pill of pills) {
|
|
3677
|
+
const error = customSlotError(pill);
|
|
3695
3678
|
if (error) {
|
|
3696
3679
|
return {
|
|
3697
3680
|
valid: false,
|
|
3698
|
-
wql:
|
|
3681
|
+
wql: serialize(pillsToAst(pills)),
|
|
3699
3682
|
ast: { family: "aggregate", raw: "", agg: "sum", metric: "", filters: [], groupBy: [], error },
|
|
3700
3683
|
error,
|
|
3701
|
-
offendingClauseId:
|
|
3684
|
+
offendingClauseId: pill.id
|
|
3702
3685
|
};
|
|
3703
3686
|
}
|
|
3704
3687
|
}
|
|
3705
|
-
const
|
|
3706
|
-
const
|
|
3688
|
+
const ast = pillsToAst(pills);
|
|
3689
|
+
const wql2 = serialize(ast);
|
|
3707
3690
|
if (ast.error) {
|
|
3708
3691
|
return {
|
|
3709
3692
|
valid: false,
|
|
@@ -3831,10 +3814,9 @@ function useWqlStageCounts(ast, valid, execute, debounceMs = DEFAULT_DIAGNOSTICS
|
|
|
3831
3814
|
return runnable ? stages : void 0;
|
|
3832
3815
|
}
|
|
3833
3816
|
function WqlComposer({
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
onWqlChange,
|
|
3817
|
+
initialQuery,
|
|
3818
|
+
query: controlledQuery,
|
|
3819
|
+
onQueryChange,
|
|
3838
3820
|
onValidationChange,
|
|
3839
3821
|
onAstChange,
|
|
3840
3822
|
onSubmit,
|
|
@@ -3848,52 +3830,91 @@ function WqlComposer({
|
|
|
3848
3830
|
placeholder = "Type search term and press Enter...",
|
|
3849
3831
|
className
|
|
3850
3832
|
}) {
|
|
3851
|
-
const [
|
|
3852
|
-
() =>
|
|
3833
|
+
const [internalPills, setInternalPills] = useState(
|
|
3834
|
+
() => wqlToPills(initialQuery ?? "") ?? defaultPills()
|
|
3835
|
+
);
|
|
3836
|
+
const controlledPills = useMemo(
|
|
3837
|
+
() => controlledQuery === void 0 ? void 0 : wqlToPills(controlledQuery),
|
|
3838
|
+
[controlledQuery]
|
|
3853
3839
|
);
|
|
3854
|
-
const
|
|
3840
|
+
const pills = controlledPills ?? internalPills;
|
|
3855
3841
|
const hiddenTypes = useMemo(() => new Set(hiddenClauseTypes), [hiddenClauseTypes]);
|
|
3856
3842
|
const [activeSlotIdx, setActiveSlotIdx] = useState(null);
|
|
3857
3843
|
const [freeText, setFreeText] = useState("");
|
|
3858
3844
|
const inputRef = useRef(null);
|
|
3845
|
+
const freeTextInitRef = useRef(null);
|
|
3846
|
+
useEffect(() => {
|
|
3847
|
+
if (controlledQuery === void 0) return;
|
|
3848
|
+
if (controlledPills === null) {
|
|
3849
|
+
if (freeTextInitRef.current !== controlledQuery) {
|
|
3850
|
+
freeTextInitRef.current = controlledQuery;
|
|
3851
|
+
setFreeText(controlledQuery);
|
|
3852
|
+
}
|
|
3853
|
+
} else if (freeTextInitRef.current !== null) {
|
|
3854
|
+
freeTextInitRef.current = null;
|
|
3855
|
+
setFreeText("");
|
|
3856
|
+
}
|
|
3857
|
+
}, [controlledQuery, controlledPills]);
|
|
3859
3858
|
useEffect(() => {
|
|
3860
3859
|
if (!autoFocus) return;
|
|
3861
3860
|
const t = setTimeout(() => inputRef.current?.focus(), 0);
|
|
3862
3861
|
return () => clearTimeout(t);
|
|
3863
3862
|
}, [autoFocus]);
|
|
3864
|
-
const
|
|
3863
|
+
const setPills = useCallback(
|
|
3865
3864
|
(next) => {
|
|
3866
|
-
if (
|
|
3867
|
-
onClausesChange?.(next);
|
|
3865
|
+
if (controlledQuery === void 0) setInternalPills(next);
|
|
3868
3866
|
},
|
|
3869
|
-
[
|
|
3867
|
+
[controlledQuery]
|
|
3870
3868
|
);
|
|
3871
3869
|
const registeredSlots = useComposerSlots();
|
|
3872
|
-
const
|
|
3873
|
-
const
|
|
3874
|
-
|
|
3870
|
+
const rawEscape = controlledQuery !== void 0 && controlledPills === null;
|
|
3871
|
+
const diagnostics = useMemo(() => {
|
|
3872
|
+
if (rawEscape) {
|
|
3873
|
+
const ast = parseQuery(controlledQuery);
|
|
3874
|
+
return {
|
|
3875
|
+
valid: !ast.error,
|
|
3876
|
+
wql: controlledQuery,
|
|
3877
|
+
ast,
|
|
3878
|
+
error: ast.error
|
|
3879
|
+
};
|
|
3880
|
+
}
|
|
3881
|
+
return diagnosePills(pills);
|
|
3882
|
+
}, [rawEscape, controlledQuery, pills, registeredSlots]);
|
|
3883
|
+
const callbacksRef = useRef({ onQueryChange, onValidationChange, onAstChange });
|
|
3884
|
+
callbacksRef.current = { onQueryChange, onValidationChange, onAstChange };
|
|
3875
3885
|
useEffect(() => {
|
|
3876
3886
|
const validation = diagnostics.valid ? { valid: true } : { valid: false, error: diagnostics.error };
|
|
3877
|
-
callbacksRef.current.
|
|
3887
|
+
if (controlledQuery === void 0) callbacksRef.current.onQueryChange?.(diagnostics.wql);
|
|
3878
3888
|
callbacksRef.current.onAstChange?.(diagnostics.ast);
|
|
3879
3889
|
callbacksRef.current.onValidationChange?.(validation);
|
|
3880
3890
|
}, [diagnostics]);
|
|
3881
3891
|
const stages = useWqlStageCounts(diagnostics.ast, diagnostics.valid, execute, debounceMs);
|
|
3882
|
-
const
|
|
3883
|
-
const offendingLabel =
|
|
3884
|
-
const
|
|
3885
|
-
const updated =
|
|
3886
|
-
const edited =
|
|
3892
|
+
const offendingPill = diagnostics.offendingClauseId ? pills.find((c) => c.id === diagnostics.offendingClauseId) : void 0;
|
|
3893
|
+
const offendingLabel = offendingPill ? getClauseMeta(offendingPill.type).label : void 0;
|
|
3894
|
+
const updatePill = (idx, patch) => {
|
|
3895
|
+
const updated = pills.map((c, i) => i === idx ? { ...c, ...patch } : c);
|
|
3896
|
+
const edited = pills[idx];
|
|
3887
3897
|
if (edited?.type === "source" && patch.value !== void 0 && patch.value !== edited.value) {
|
|
3888
|
-
|
|
3898
|
+
setPills(pivotPills(updated, patch.value));
|
|
3899
|
+
emitIfControlled(pivotPills(updated, patch.value));
|
|
3889
3900
|
return;
|
|
3890
3901
|
}
|
|
3891
|
-
|
|
3902
|
+
setPills(updated);
|
|
3903
|
+
emitIfControlled(updated);
|
|
3892
3904
|
};
|
|
3893
|
-
const
|
|
3894
|
-
|
|
3905
|
+
const removePill = (idx) => {
|
|
3906
|
+
const next = pills.filter((_, i) => i !== idx);
|
|
3907
|
+
setPills(next);
|
|
3908
|
+
emitIfControlled(next);
|
|
3895
3909
|
};
|
|
3896
|
-
const
|
|
3910
|
+
const emitIfControlled = (next) => {
|
|
3911
|
+
if (controlledQuery !== void 0) {
|
|
3912
|
+
const nextAst = pillsToAst(next);
|
|
3913
|
+
const nextWql = serialize(nextAst);
|
|
3914
|
+
if (nextWql !== controlledQuery) callbacksRef.current.onQueryChange?.(nextWql);
|
|
3915
|
+
}
|
|
3916
|
+
};
|
|
3917
|
+
const makePill = (type, value) => {
|
|
3897
3918
|
const meta = getClauseMeta(type);
|
|
3898
3919
|
return {
|
|
3899
3920
|
id: `c-${Date.now()}-${Math.random()}`,
|
|
@@ -3907,10 +3928,13 @@ function WqlComposer({
|
|
|
3907
3928
|
const pending = useMemo(() => {
|
|
3908
3929
|
const raw = freeText.trim();
|
|
3909
3930
|
if (!raw) return null;
|
|
3910
|
-
const restored =
|
|
3911
|
-
if (restored) {
|
|
3912
|
-
|
|
3913
|
-
|
|
3931
|
+
const restored = wqlToPills(raw);
|
|
3932
|
+
if (restored) return { kind: "query", pills: restored };
|
|
3933
|
+
const parsed = parseQuery(raw);
|
|
3934
|
+
if (!parsed.error && /[:{]/.test(raw)) {
|
|
3935
|
+
return { kind: "raw", value: raw };
|
|
3936
|
+
}
|
|
3937
|
+
if (parsed.error && /[:{]/.test(raw)) {
|
|
3914
3938
|
return { kind: "invalid", reason: String(parsed.error) };
|
|
3915
3939
|
}
|
|
3916
3940
|
const words = raw.match(/[a-zA-Z0-9_-]+/g);
|
|
@@ -3922,42 +3946,48 @@ function WqlComposer({
|
|
|
3922
3946
|
}
|
|
3923
3947
|
return { kind: "text", value: words[0] };
|
|
3924
3948
|
}, [freeText]);
|
|
3925
|
-
const
|
|
3926
|
-
const type =
|
|
3927
|
-
const seed = type === "time" ? "last 2w" : type === "where" ? "sum:totalVolume{} > 5000" : type === "agg" ? "sum" : type === "groupby" ? "week" : type === "rollup" ? "1w" :
|
|
3928
|
-
|
|
3949
|
+
const addPill = (pill) => {
|
|
3950
|
+
const type = pill.type;
|
|
3951
|
+
const seed = type === "time" ? "last 2w" : type === "where" ? "sum:totalVolume{} > 5000" : type === "agg" ? "sum" : type === "groupby" ? "week" : type === "rollup" ? "1w" : pill.value;
|
|
3952
|
+
const next = [...pills, { ...pill, value: seed }];
|
|
3953
|
+
setPills(next);
|
|
3954
|
+
emitIfControlled(next);
|
|
3929
3955
|
};
|
|
3930
|
-
const addCalc = (
|
|
3931
|
-
const { type, value } =
|
|
3956
|
+
const addCalc = (pill) => {
|
|
3957
|
+
const { type, value } = pill;
|
|
3932
3958
|
if (type === "metric") {
|
|
3933
|
-
if (
|
|
3959
|
+
if (pills.some((c) => c.type === "metric")) return;
|
|
3934
3960
|
const afterIdx = Math.max(
|
|
3935
|
-
|
|
3936
|
-
|
|
3961
|
+
pills.findIndex((c) => c.type === "agg"),
|
|
3962
|
+
pills.findIndex((c) => c.type === "source")
|
|
3937
3963
|
);
|
|
3938
|
-
const next2 = [...
|
|
3939
|
-
next2.splice(afterIdx + 1, 0,
|
|
3940
|
-
|
|
3964
|
+
const next2 = [...pills];
|
|
3965
|
+
next2.splice(afterIdx + 1, 0, makePill("metric", value));
|
|
3966
|
+
setPills(next2);
|
|
3967
|
+
emitIfControlled(next2);
|
|
3941
3968
|
return;
|
|
3942
3969
|
}
|
|
3943
3970
|
if (type !== "agg") {
|
|
3944
|
-
|
|
3971
|
+
addPill(pill);
|
|
3945
3972
|
return;
|
|
3946
3973
|
}
|
|
3947
3974
|
const agg = value || "sum";
|
|
3948
|
-
const existingIdx =
|
|
3975
|
+
const existingIdx = pills.findIndex((c) => c.type === "agg");
|
|
3949
3976
|
if (existingIdx >= 0) {
|
|
3950
|
-
|
|
3977
|
+
updatePill(existingIdx, { value: agg });
|
|
3951
3978
|
return;
|
|
3952
3979
|
}
|
|
3953
|
-
if (sourcePlane(
|
|
3954
|
-
|
|
3980
|
+
if (sourcePlane(pillValue(pills, "source", "notes")) !== "metrics") {
|
|
3981
|
+
const next2 = pivotPills(pills, "metrics").map((c) => c.type === "agg" ? { ...c, value: agg } : c);
|
|
3982
|
+
setPills(next2);
|
|
3983
|
+
emitIfControlled(next2);
|
|
3955
3984
|
return;
|
|
3956
3985
|
}
|
|
3957
|
-
const sourceIdx =
|
|
3958
|
-
const next = [...
|
|
3959
|
-
next.splice(sourceIdx + 1, 0,
|
|
3960
|
-
|
|
3986
|
+
const sourceIdx = pills.findIndex((c) => c.type === "source");
|
|
3987
|
+
const next = [...pills];
|
|
3988
|
+
next.splice(sourceIdx + 1, 0, makePill("agg", agg));
|
|
3989
|
+
setPills(next);
|
|
3990
|
+
emitIfControlled(next);
|
|
3961
3991
|
};
|
|
3962
3992
|
const handleKeyDown = (e) => {
|
|
3963
3993
|
if (e.key !== "Enter") return;
|
|
@@ -3965,10 +3995,15 @@ function WqlComposer({
|
|
|
3965
3995
|
e.preventDefault();
|
|
3966
3996
|
e.stopPropagation();
|
|
3967
3997
|
if (pending?.kind === "query") {
|
|
3968
|
-
|
|
3998
|
+
setPills(pending.pills);
|
|
3999
|
+
emitIfControlled(pending.pills);
|
|
3969
4000
|
setFreeText("");
|
|
4001
|
+
} else if (pending?.kind === "raw") {
|
|
4002
|
+
onSubmit?.(pending.value);
|
|
3970
4003
|
} else if (pending?.kind === "text") {
|
|
3971
|
-
|
|
4004
|
+
const next = [...pills, makePill("text", pending.value)];
|
|
4005
|
+
setPills(next);
|
|
4006
|
+
emitIfControlled(next);
|
|
3972
4007
|
setFreeText("");
|
|
3973
4008
|
}
|
|
3974
4009
|
return;
|
|
@@ -3991,20 +4026,20 @@ function WqlComposer({
|
|
|
3991
4026
|
"data-testid": "wql-composer",
|
|
3992
4027
|
children: [
|
|
3993
4028
|
/* @__PURE__ */ jsx(Command, { className: "size-4 text-amber-500 shrink-0 mr-0.5" }),
|
|
3994
|
-
|
|
3995
|
-
(
|
|
4029
|
+
pills.map(
|
|
4030
|
+
(pill, idx) => hiddenTypes.has(pill.type) ? null : /* @__PURE__ */ jsx(
|
|
3996
4031
|
TokenSlotPill,
|
|
3997
4032
|
{
|
|
3998
|
-
clause,
|
|
4033
|
+
clause: pill,
|
|
3999
4034
|
isActive: activeSlotIdx === idx,
|
|
4000
|
-
invalid: diagnostics.offendingClauseId ===
|
|
4001
|
-
invalidReason: diagnostics.offendingClauseId ===
|
|
4035
|
+
invalid: diagnostics.offendingClauseId === pill.id,
|
|
4036
|
+
invalidReason: diagnostics.offendingClauseId === pill.id ? diagnostics.error : void 0,
|
|
4002
4037
|
onClick: () => setActiveSlotIdx(idx),
|
|
4003
|
-
onChange: (patch) =>
|
|
4004
|
-
onRemove: () =>
|
|
4038
|
+
onChange: (patch) => updatePill(idx, patch),
|
|
4039
|
+
onRemove: () => removePill(idx),
|
|
4005
4040
|
compact: true
|
|
4006
4041
|
},
|
|
4007
|
-
|
|
4042
|
+
pill.id
|
|
4008
4043
|
)
|
|
4009
4044
|
),
|
|
4010
4045
|
/* @__PURE__ */ jsx(
|
|
@@ -4035,6 +4070,7 @@ function WqlComposer({
|
|
|
4035
4070
|
role: pending.kind === "invalid" ? "alert" : void 0,
|
|
4036
4071
|
children: [
|
|
4037
4072
|
pending.kind === "query" && "\u21B5 Use as query",
|
|
4073
|
+
pending.kind === "raw" && "\u21B5 Run query",
|
|
4038
4074
|
pending.kind === "text" && `\u21B5 Search text: ${pending.value}`,
|
|
4039
4075
|
pending.kind === "invalid" && pending.reason
|
|
4040
4076
|
]
|
|
@@ -4047,14 +4083,14 @@ function WqlComposer({
|
|
|
4047
4083
|
offendingLabel,
|
|
4048
4084
|
stages,
|
|
4049
4085
|
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4050
|
-
/* @__PURE__ */ jsx(AddCalcDropdown, { clauses, onAdd: addCalc }),
|
|
4051
|
-
/* @__PURE__ */ jsx(AddFilterDropdown, { clauses, onAdd:
|
|
4086
|
+
/* @__PURE__ */ jsx(AddCalcDropdown, { clauses: pills, onAdd: addCalc }),
|
|
4087
|
+
/* @__PURE__ */ jsx(AddFilterDropdown, { clauses: pills, onAdd: addPill }),
|
|
4052
4088
|
diagnosticsActions
|
|
4053
4089
|
] })
|
|
4054
4090
|
}
|
|
4055
4091
|
) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5 px-1.5", "data-testid": "wql-add-row", children: [
|
|
4056
|
-
/* @__PURE__ */ jsx(AddCalcDropdown, { clauses, onAdd: addCalc }),
|
|
4057
|
-
/* @__PURE__ */ jsx(AddFilterDropdown, { clauses, onAdd:
|
|
4092
|
+
/* @__PURE__ */ jsx(AddCalcDropdown, { clauses: pills, onAdd: addCalc }),
|
|
4093
|
+
/* @__PURE__ */ jsx(AddFilterDropdown, { clauses: pills, onAdd: addPill }),
|
|
4058
4094
|
diagnosticsActions
|
|
4059
4095
|
] })
|
|
4060
4096
|
] });
|
|
@@ -4145,13 +4181,10 @@ function WqlQueryInspectorModal({
|
|
|
4145
4181
|
subtitle = "Use the Omni-Composer to edit this block query.",
|
|
4146
4182
|
executor
|
|
4147
4183
|
}) {
|
|
4148
|
-
const [
|
|
4184
|
+
const [wql2, setWql] = useState(initialQuery);
|
|
4149
4185
|
const [isValid, setIsValid] = useState(true);
|
|
4150
4186
|
useEffect(() => {
|
|
4151
|
-
if (isOpen)
|
|
4152
|
-
const restored = wqlToClauses(initialQuery);
|
|
4153
|
-
setClauses(restored ?? defaultMetricsClauses());
|
|
4154
|
-
}
|
|
4187
|
+
if (isOpen) setWql(initialQuery);
|
|
4155
4188
|
}, [isOpen, initialQuery]);
|
|
4156
4189
|
const diagnosticsExecutor = useCallback(
|
|
4157
4190
|
(ast) => {
|
|
@@ -4169,8 +4202,7 @@ function WqlQueryInspectorModal({
|
|
|
4169
4202
|
);
|
|
4170
4203
|
const handleApply = () => {
|
|
4171
4204
|
if (!isValid) return;
|
|
4172
|
-
|
|
4173
|
-
onApply(nextWql);
|
|
4205
|
+
onApply(wql2);
|
|
4174
4206
|
onClose();
|
|
4175
4207
|
};
|
|
4176
4208
|
if (!isOpen) return null;
|
|
@@ -4201,15 +4233,15 @@ function WqlQueryInspectorModal({
|
|
|
4201
4233
|
/* @__PURE__ */ jsx("div", { className: "p-6 overflow-y-auto space-y-4", children: /* @__PURE__ */ jsx(
|
|
4202
4234
|
WqlComposer,
|
|
4203
4235
|
{
|
|
4204
|
-
|
|
4205
|
-
|
|
4236
|
+
query: wql2,
|
|
4237
|
+
onQueryChange: setWql,
|
|
4206
4238
|
onValidationChange: (v) => setIsValid(v.valid),
|
|
4207
4239
|
execute: diagnosticsExecutor,
|
|
4208
4240
|
showDiagnostics: true
|
|
4209
4241
|
}
|
|
4210
4242
|
) }),
|
|
4211
4243
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-3 border-t border-border/80 bg-muted/10", children: [
|
|
4212
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground font-mono", children:
|
|
4244
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground font-mono", children: wql2 }),
|
|
4213
4245
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4214
4246
|
/* @__PURE__ */ jsx(
|
|
4215
4247
|
"button",
|
|
@@ -10379,6 +10411,6 @@ function editorPreset(optionsOrDialect = "markdown") {
|
|
|
10379
10411
|
return extensions;
|
|
10380
10412
|
}
|
|
10381
10413
|
|
|
10382
|
-
export { AGG_OPTIONS, AddCalcDropdown, AddFilterDropdown,
|
|
10383
|
-
//# sourceMappingURL=chunk-
|
|
10384
|
-
//# sourceMappingURL=chunk-
|
|
10414
|
+
export { AGG_OPTIONS, AddCalcDropdown, AddFilterDropdown, CONTENT_SOURCES, ClausePopover, ComposerRegistry, CustomSlotPopover, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DefaultFrontmatterWidget, GROUPBY_OPTIONS, GoalRings, METRIC_OPTIONS, MULTI_VALUE_TYPES, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TIME_OPTIONS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WHERE_OPERATORS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, blockContentId, blockTypesFromBlocks, catalogIdsFromBlocks, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, dateRangeSlot, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatTimestamp, frontmatterPreview, frontmatterPreviewField, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, mergeSeries, mergeTagSuggestions, navigationFacet, patchBlockQuery, previewDecorations, queryBlockPreview, readSessionRpe, removeRuntimePanel, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sessionQueryInsert, sessionQueryWql, setGutterHighlights, setSuggestionBinding, smartIncrement, sourcePlane, summarizeAggregate, summarizeFind, tagsFromStaticBlocks, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wrapInTimeFence };
|
|
10415
|
+
//# sourceMappingURL=chunk-PZXX6GBK.mjs.map
|
|
10416
|
+
//# sourceMappingURL=chunk-PZXX6GBK.mjs.map
|