* [LIQ] Add more tests for builtin aggregates
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* [LIQ] Add `first`, `last`, `mode` and `median` aggregates and `group_concat` alias
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* [LIQ] Add new aggregate functions, aliases, and queryable aggregate registry
* Extend with 13 new built-in aggregates: `product`, `string_agg`,
`yaml_agg`, `json_agg`, `bit_and`, `bit_or`, `bit_xor`, `bool_and`,
`bool_or`, `stddev_pop`, `stddev_samp`, `var_pop` and `var_samp`.
* Introduce `aggregate.alias` API allowing users to define custom
aliases for any aggregate. Standard aliases (`every`, `std`, `stddev`
and `variance`) are now defined via this API rather than hardcoded.
* Add `index.aggregates` queryable collection so users can discover
all available aggregates directly from LIQ queries.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix config pass through query path so custom aggregates work
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Preserve `LuaTable`/`LuaFunction` values in aggregate config storage
`config.set` uses `LuaNativeJSFunction` which calls `luaValueToJS` on
all arguments. This converted the aggregate `LuaTable` to a plain JS
object and wrapped `LuaFunction` callbacks in JS functions that also
converted their returned values via `luaValueToJS`. The result was that
state returned by initialize (a `LuaTable`) got converted to a plain JS
object before being passed to `iterate`. Therefor Lua operations like
`table.insert` on that were failing because they expected a `LuaTable`
and not a plain JS array.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix formatting
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Improve aggregate functions descriptions, fix `sum` divergence
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Align `product` with `sum`
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: extract `alias` from `LuaTable` via `rawGet` in `aggregates()` registry
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Rename `alias` in `aggregates()` to `target` for clarity
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Add a null guard at the top of `jsToLuaValue`
This preserves `null`/`undefined` as-is (both map to Lua nil) and
prevents them from falling through to the `typeof` "object" branch.
For this PR it means that null `target` in our `aggregates` entries will
correctly show as empty/`nil` in query results rather than `{}`.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Documentation reflects recent changes
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: make `sum`/`product` return null on empty input; stop `LIQ_NULL` leaks
* `sum(`) and` product(`) now return null when no rows match (matching
Postgres semantics) instead of returning 0 and 1 respectively.
* Query result columns that hold null are internally preserved using
a `LIQ_NULL` sentinel so that column keys survive in `LuaTable`
storage. This sentinel was leaking into Lua code as "userdata"
through three read paths:
* `luaIndexValue`: `rawGet` returned the sentinel directly to Lua when
accessing table fields,
* `rawget` (stdlib): the builtin `rawget` function exposed the
sentinel without converting it back to `nil`,
* `createAugmentedEnv`: string interpolation unpacked table values via
`rawGet` into local variables, making the sentinel visible in
template expressions like `${var}`.
All three now convert `LIQ_NULL` to `nil` at the read boundary,
keeping the sentinel internal to table storage where it belongs.
* Update affected test expectations accordingly.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Remove duplicated LIQ_NULL hazard, add guard for all builtin aggregate `iterate`s
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: `array_agg` preserves NULL positions
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Add symbol guard to `json_agg`
`JSON.stringify(Symbol(...))` in an array produces null by accident.
That is a JS implementation detail we **MUST NOT** rely on. Explicit
null push makes intent clear and avoids surprises if the `Symbol`
representation ever changes.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Add symbol guard to `yaml_agg` (ditto)
`js-yaml` has no knowledge of the `LIQ_NULL` symbol. Passing null makes
it emit YAML null (or `~`), which is the correct YAML representation of
a missing value and matches standard `json_agg`/`yaml_agg`
NULL-inclusion semantics.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Add intra-aggregate ordering null guards
Without this, `LIQ_NULL` sort keys would fall through to `valA < valB`
which is always false for `Symbol`s which is breaking the `nulls
first`/`nulls last` contract...
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Ditto, but for `order by` null comparisons
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Guard `luaTypeName`, `luaTypeOf` and `luaToString` against `LIQ_NULL` sentinel
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Guard presentation layer against `LIQ_NULL` sentinel leaking as visible text
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix: Evaluate extra args per-item in `executeAggregate`; add new aggregates
Extra arguments (2nd, 3rd, etc.) to aggregate functions were evaluated
against the outer query environment where the object variable is not
bound. This caused multi-argument aggregates like `covar_samp(data.y,
data.x)` to fail with nil reference errors. This commit addresses this
by evaluating extra args per-item inside the iterate loop using the item
environment so all arguments resolve correctly.
We also add few common aggregates:
- `covar_pop`, `covar_samp`, `corr`: population/sample covariance and
correlation coefficient using online co-moment algorithm.
- `quantile(value, q, method)`: general quantile with interpolation
methods: lower, higher, nearest, midpoint and default linear.
- `percentile_cont(value, q)`: continuous percentile (linear)
- `percentile_disc(value, q)`: discrete percentile (lower)
Note: `percentile_cont` and `percentile_disc` share the `quantile`
implementation through `ctx.name` at initialize time.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Update docs
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Make the ordering for quantile aggregates explicit
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Update docs
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Improve docs
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix extended task state widget click behavior in widgets
* Add mouse click handler on extended task state span to prevent
CodeMirror from moving selection to widget source on normal click
* Let `Alt+Click` pass to parent div for the "cursor to source" behavior
matching simple checkbox `Alt+Click` behavior
* Skip state cycling on `Alt+Click`
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Prevent task state corruption from concurrent cycling on mobile
On mobile, rapid taps on an enhanced task state (e.g. `[PLANNED]`) could
fire multiple events before the first `taskCycleAtPos` completed. Add
a reentrancy guard to `taskCycleAtPos` so concurrent calls are dropped.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix extended task rendering in widgets
This patch addresses several issues.
1. Extended tasks like `[PLANNED]` or `[TODO]` were not rendering
correctly when generated via widgets. The HTML renderer was producing
plain text without brackets or proper styling, while simple `[x]`
tasks worked fine. The fix produces the same DOM structure as
CodeMirror producing coherent styling everywhere.
2. Widget checkboxes are not interactive now. Since widget tasks have no
backing state handler clicking them only caused a misleading visual
toggle with no effect. The `disabled` attribute prevents this
confusion and the dimmed appearanceit signals to the user that the
checkbox is read-only in this context which is the truth.
To see it in action try the following Markdown snippet in SilverBullet:
```
- [x] #foo [att: i]
- [PLANNED] bar #bar [att: i]
${'- [x] #foo [att: i]\n- [PLANNED] bar #bar [att: i]'}
```
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Revert and just prevent widget checkboxes from toggling visually
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Support custom task states in widgets and indexer
Custom task states like `[PLANNED]` or `[IN PROGRESS]` now render
correctly in *widgets*, matching the same DOM structure as CodeMirror
uses in the editor. Widget-rendered tasks that reference a real task
(via `page@pos`) are fully interactive — clicking an extended state
cycles it through the configured states and updates the source task.
Tasks without a reference are disabled to prevent misleading visual
feedback.
The task indexer now respects the `done` flag on custom states defined
via `taskState.define`, so queries like `where not t.done` correctly
exclude tasks in states marked as done. Previously only the built-in
simple tasks (`- [x]` or `- [X]`) states were treated as completed. The
`Task: Remove Completed` command already handled custom done states and
continues to work unchanged.
State cycling now preserves the definition order from `taskState.define`
calls instead of sorting alphabetically. A new `order` field allows
explicit control over the cycle sequence. Without it, states cycle in
definition order. With it, states are sorted by the `order` field value.
This gives users full control over the progression, for example:
PLANNED --> IN PROGRESS --> FINISHED
The `taskState.define` API now validates its input against a schema:
`name` (string, required), `done` (boolean, optional), and `order`
(number, optional).
The `expandMarkdown` no longer injects "fake" `page@pos` references into
tasks rendered via `widget.markdownBlock` or `widget.new` which was
causing broken links and unwanted interactivity on static task content.
The `task:stateChange` event is dispatched when toggling tasks from
widgets, ensuring Lua listeners (such as some custom completed timestamp
management) fire correctly regardless of where the toggle originates.
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
Based on https://www.markdownlang.com/extended/footnotes.html
* Both "official" [^1] and [^1]: def
* And inline ^[My foot note]
Features:
* Properly parsed in markdown parser
* Syntax highlighting
* Live Preview to ... when on-hover preview of content, on-click moves
cursor to definition
* Reference completion
* Invalid references are marked
When `standalone` is set to true, this action button will only appear in
"standalone PWA" mode (e.g. home-screen mobile apps, or installed PWA on
desktop). Useful to e.g. only show forward and backward buttons. These
forward and backward buttons are now also added to the default action
button config.
* Fix table row parser
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Remove all `{[command button]}` remnants
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Render `Attribute` nodes in HTML like CodeMirror does
This patch
- ensures always render `Attribute`,
- produces `sb-attribute` span with sub-spans matching CodeMirror DOM,
- removee `preserveAttributes` from Markdown render options and callers.
Finally the attributes looks the same everywhere (table, text, widget).
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix table row parser
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Remove all `{[command button]}` remnants
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Add tests
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
* Fix extended task rendering in case of following EoL
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
---------
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>