wiki-formant 0.89.1 → 0.89.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1011,8 +1011,8 @@ total, a rate's mean), every non-empty
1011
1011
  ranked list, and `?days=` links between windows, all time among them, pinned in
1012
1012
  the chart's frame as its own buttons are (`TimeChart`'s `controls`). Each
1013
1013
  figure is a button that charts it: visitors, page views, bounce rate,
1014
- visit length and clicks to X. Visitors count agents; `?agents=apart`, a pair
1015
- of links beside the windows, splits them into people and agents, and every
1014
+ visit length and clicks to X. Visitors count agents; `?agents=apart`, a switch
1015
+ beside the windows, splits them into people and agents, and every
1016
1016
  other link on the page keeps the choice. `digest`'s `by_day` carries every
1017
1017
  one of those per UTC day, a visit on the day it began, so the days add up to the
1018
1018
  totals. A day with no visits has no bounce rate or visit length and is left out
package/dist/base.css CHANGED
@@ -294,7 +294,7 @@ html[data-sidebar='closed'] .wiki-sidebar { width: 0; }
294
294
  .chart-controls { font-size: 0.625rem; }
295
295
  .chart-controls :is(button, a) { border: 1px solid var(--ui-border); border-radius: calc(var(--ui-radius) / 2); opacity: 1; transition: background-color 150ms, color 150ms; }
296
296
  .chart-controls :is(button, a):hover { background: var(--ui-surface-2); }
297
- .chart-controls :is(button, a):is([aria-pressed='true'], [aria-current]) { background: var(--ui-accent); border-color: var(--ui-accent); color: var(--ui-on-accent); }
297
+ .chart-controls :is(button, a):is([aria-pressed='true'], [aria-checked='true'], [aria-current]) { background: var(--ui-accent); border-color: var(--ui-accent); color: var(--ui-on-accent); }
298
298
 
299
299
  .code-tabs { margin-block: 1rem; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); }
300
300
  .code-tabs-list { overflow-x: auto; border-bottom: 1px solid var(--ui-border); background: var(--ui-surface); }
@@ -618,8 +618,8 @@ pre:hover > .code-copy-btn,
618
618
  .chart-controls :is(button, a) { padding: 0.125rem 0.375rem; font: inherit; color: inherit; text-decoration: none; background: none; cursor: pointer; opacity: 0.6; }
619
619
  /* An icon sits on the text's line, so an icon button is as tall as a labelled one. */
620
620
  .chart-icon { display: inline-block; width: 1em; height: 1em; vertical-align: -0.125em; }
621
- /* A button is pressed; a link (a stats page's window) is the page shown. */
622
- .chart-controls :is(button, a):is([aria-pressed='true'], [aria-current]) { opacity: 1; }
621
+ /* A button is pressed, a switch on, and a link (a stats page's window) the page shown. */
622
+ .chart-controls :is(button, a):is([aria-pressed='true'], [aria-checked='true'], [aria-current]) { opacity: 1; }
623
623
  /* A step the range cannot make stays in its place, and dims. */
624
624
  .chart-controls button:disabled { opacity: 0.3; cursor: default; pointer-events: none; }
625
625
 
package/dist/mcp.d.ts CHANGED
@@ -51,7 +51,8 @@ export type JsonSchema = {
51
51
  [keyword: string]: unknown;
52
52
  };
53
53
  export type ToolParam = {
54
- type: 'string' | 'number' | 'boolean' | 'array' | 'object';
54
+ /** Unset for a union (`oneOf`, `anyOf`), which the handler checks itself. */
55
+ type?: 'string' | 'number' | 'boolean' | 'array' | 'object';
55
56
  description: string;
56
57
  enum?: string[];
57
58
  items?: JsonSchema;
package/dist/mcp.js CHANGED
@@ -242,6 +242,10 @@ function validateArgs(tool, args) {
242
242
  problems.push(`Parameter "${key}" must contain only ${itemType}s.`);
243
243
  }
244
244
  }
245
+ else if (!spec.type) {
246
+ // No one type (a `oneOf` of shapes, as a zod union emits) is the tool's own
247
+ // to check, as with array items: compared to undefined, every call was refused.
248
+ }
245
249
  else if (actual !== spec.type) {
246
250
  problems.push(`Parameter "${key}" must be a ${spec.type}; received ${actual} (${JSON.stringify(value)}).`);
247
251
  }
package/dist/react.js CHANGED
@@ -96,10 +96,16 @@ function useCollapsibleSidebar(options = {}) {
96
96
  }, [storageKey]);
97
97
  const toggle = useCallback(() => setOpen(!open), [open, setOpen]);
98
98
  // One source of truth either side of hydration: the boot script writes this
99
- // attribute before first paint, and from here on the hook owns it.
99
+ // attribute before first paint, and once storage has been read the hook owns
100
+ // it. Not before: this effect runs in the same commit as the read above, with
101
+ // `open` still the server's `true`, and writing "open" there over a stamped
102
+ // "closed" widened a remembered-closed rail for one frame and then slid it
103
+ // shut — the very slide the boot script exists to prevent.
100
104
  useEffect(() => {
105
+ if (!ready)
106
+ return;
101
107
  document.documentElement.setAttribute(SIDEBAR_ATTRIBUTE, open ? 'open' : 'closed');
102
- }, [open]);
108
+ }, [open, ready]);
103
109
  return { open, setOpen, toggle, isMobile, ready };
104
110
  }
105
111
  // ---- sharing that state across the layout -----------------------------------
package/dist/stats.js CHANGED
@@ -72,8 +72,8 @@ export function Stats({ digest: d, days, filter = {}, apart = false, ranges = ST
72
72
  const at = (f, n = days, a = apart) => href(n, f, a);
73
73
  const to = (k) => (v) => at({ ...filter, [k]: v });
74
74
  // Each window is another digest, so a link rather than a button, pinned
75
- // with the chart's own controls, and so is whether agents count apart.
76
- const controls = (_jsxs(_Fragment, { children: [_jsx("div", { role: "group", "aria-label": "Window", children: ranges.map(n => (_jsx("a", { href: at(filter, n), "aria-current": n === days ? 'page' : undefined, children: windowLabel(n) }, n))) }), _jsxs("div", { role: "group", "aria-label": "Agents", children: [_jsx("a", { href: at(filter, days, false), "aria-current": apart ? undefined : 'page', title: "Visitors count agents", children: "With agents" }), _jsx("a", { href: at(filter, days, true), "aria-current": apart ? 'page' : undefined, title: "People and agents counted apart", children: "Apart" })] })] }));
75
+ // with the chart's own controls, and so is the switch that counts agents apart.
76
+ const controls = (_jsxs(_Fragment, { children: [_jsx("div", { role: "group", "aria-label": "Window", children: ranges.map(n => (_jsx("a", { href: at(filter, n), "aria-current": n === days ? 'page' : undefined, children: windowLabel(n) }, n))) }), _jsx("div", { children: _jsx("a", { href: at(filter, days, !apart), role: "switch", "aria-checked": apart, title: "People and agents counted apart", children: "Agents apart" }) })] }));
77
77
  return (_jsxs("div", { className: "stats", children: [narrowed.length > 0 && (_jsxs("div", { className: "stats-filters", children: [_jsx("ul", { "aria-label": "Narrowed to", children: narrowed.map(k => (_jsx("li", { children: _jsxs("a", { href: at({ ...filter, [k]: undefined }), title: "Remove", children: [NAMES[k], ": ", k === 'country' ? country(filter[k]) : filter[k], " \u00D7"] }) }, k))) }), start < counted && d.counted_from && (_jsxs("p", { children: ["Narrowed figures start on ", d.counted_from, ", the first day this site counted its own visits."] }))] })), _jsx(StatsFigures, { digest: d, first: first, apart: apart, controls: controls }), _jsxs("div", { className: "stats-lists", children: [_jsx(StatsList, { title: "Pages", rows: rows(d.top_pages, 'page', 'visitors'), to: to('page') }), _jsx(StatsList, { title: "Entry pages", rows: rows(d.entry_pages, 'page', 'visitors'), to: to('entry') }), _jsx(StatsList, { title: "Sources", rows: rows(d.top_sources, 'source', 'visitors'), to: to('source') }), _jsx(StatsList, { title: "From X posts", rows: rows(d.from_posts, 'page', 'visitors'), to: v => at({ ...filter, source: 'x', entry: v }) }), _jsx(StatsList, { title: "Sibling sites", rows: rows(d.sibling_referrals, 'source', 'visitors'), to: to('source') }), _jsx(StatsList, { title: "Countries", rows: rows(d.countries, 'country', 'visitors'), label: country, to: to('country') }), _jsx(StatsList, { title: "Devices", rows: rows(d.devices, 'device', 'visitors'), to: to('device') }), _jsx(StatsList, { title: "Agents", rows: rows(d.top_agents, 'agent', 'visitors') }), _jsx(StatsList, { title: "Agent tool calls", rows: rows(d.agent_tool_calls, 'tool', 'events') }), _jsx(StatsList, { title: "Searches that found nothing", rows: rows(d.gaps, 'query', 'events') })] })] }));
78
78
  }
79
79
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-formant",
3
- "version": "0.89.1",
3
+ "version": "0.89.4",
4
4
  "description": "The portable half of a wiki: derived taxonomy and facet controls, a version-negotiating MCP transport, markdown twins, block rendering, a rich-text editor engine, conditional-GET plumbing, and the headers and CSP every origin sends.",
5
5
  "type": "module",
6
6
  "license": "MIT",