blixify-ui-web-mcp 0.1.56 → 0.1.58

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.
@@ -729,6 +729,28 @@ Use `organise.additionalQuery` to add filter controls above the list. Each entry
729
729
 
730
730
  ---
731
731
 
732
+ ### Formula column
733
+
734
+ A `type: "formula"` column is a monday.com-style formula. It is worked out from the row whenever the row is shown — table, kanban, grid and read view — and never stored on the record. It is a column type of its own: `math` columns and their `mathExpression` are untouched by it.
735
+
736
+ ```tsx
737
+ {
738
+ id: "spendStatus",
739
+ name: "Spend Status",
740
+ type: "formula",
741
+ formulaExpression: 'IF({spent} > {budget}, "Over budget", "Under budget")',
742
+ }
743
+ ```
744
+
745
+ - **Syntax** follows [monday's formula column](https://support.monday.com/hc/en-us/articles/360001235445-The-Formula-Column): `{Column}` references a column (the builder shows names, the schema stores ids, so renaming a column never breaks a formula), text in quotes, `=` / `<>` compare, `&` joins text (below `+`/`-`, above comparisons, as in monday), `+ - * / ^ %` and `( )`.
746
+ - **Functions** — monday's library, in `src/components/data/formula/functions.ts`: text (`CONCATENATE`, `LEFT`, `LEN`, `REPLACE`, `SEARCH`, `SUBSTITUTE`, `TEXT`, `TRIM` …), logical (`IF`, `SWITCH`, `AND`, `OR`, `XOR`, `NOT`, `EXACT`), numeric (`SUM`, `AVERAGE`, `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `MOD`, `POWER` …), date (`TODAY`, `NOW`, `DAYS`, `WORKDAYS`, `ADD_DAYS`, `FORMAT_DATE`, `WEEKNUM` …) and `PI` / `TRUE` / `FALSE`. Text results are capped at 10,000 characters.
747
+ - **Values:** an empty cell is blank text, 0 in arithmetic and equal to 0 in a comparison with a number. Reference and role columns read their label; a math column reads the number it shows. A formula can read another formula; a circular reference shows `Error`. Files, images, addresses, objects, passwords, signatures, canvas and gantt columns cannot be used.
748
+ - **Adding:** with `bareAddColumns`, pick **Formula** as the type in the header + panel. The builder lists columns and functions with an example of each, validates as you type and previews the first row.
749
+ - **Sort and filter** use the computed value, as monday does: header / view settings sort, and the flex filters. Either makes the list read the matching rows — at most `FORMULA_VIEW_MAX_ROWS` (5,000), in a fixed order, with a notice when there are more — and filter, order and page them locally. The cap also applies to the calendar, map, timeline and legacy grouped views, which otherwise read every row. On Firebase and union lists formula columns are not offered for sorting or filtering. CSV export sends the rows a formula filter kept, by id; "Select all pages" is off in a formula view, and the selection callbacks' query does not carry formula conditions.
750
+ - **Earlier library versions** have no case for `formula`, so they show its cell blank. Apps that build schemas with `parseSpaceToDataStructure` need this version to receive `formulaExpression`; a consumer with its own `DataType` list (blixify-space-next `models/Data.ts`) adds `"formula"` to it.
751
+
752
+ ---
753
+
732
754
  ### With activity log (comments + audit trail)
733
755
 
734
756
  Enable `edit.activityLog` to open the activity panel from list and read views. It uses the new feeds and pagination UI, an inline comment composer, editable comments, confirmed deletion, and before/after audit values. It follows `organise.darkMode`, including when opened from a legacy table.
@@ -1084,7 +1106,7 @@ Which field types can be edited from the list table, and how. Types not listed u
1084
1106
 
1085
1107
  | Edited **in the cell** | Edited **in a modal** (cell click opens it) | Read-only in list | Never rendered as a list column |
1086
1108
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------- |
1087
- | `string`, `contact`, `email`, `textArea`, `number`, `boolean` (checkbox and radio commit on select), `date`, `list`, `multipleList`, `person`, `listPerson`, `roles`, `reference`, `listReference`, `referenceSelection` | `image`, `multipleImage`, `address`, `object`, `listObject`, `markdown`, `signature`, `file`, `multipleFile`, `listAddress` | `math` (computed), `password` | `canvas`, `ganttchart` |
1109
+ | `string`, `contact`, `email`, `textArea`, `number`, `boolean` (checkbox and radio commit on select), `date`, `list`, `multipleList`, `person`, `listPerson`, `roles`, `reference`, `listReference`, `referenceSelection` | `image`, `multipleImage`, `address`, `object`, `listObject`, `markdown`, `signature`, `file`, `multipleFile`, `listAddress` | `math` (computed), `formula` (computed), `password` | `canvas`, `ganttchart` |
1088
1110
 
1089
1111
  - **Why some types edit in a modal:** their editor is bigger than one line. An
1090
1112
  `address` is a Places autocomplete plus a map with a draggable pin plus
@@ -538,6 +538,97 @@ export const DTWInlineEditMatrix = () => {
538
538
  );
539
539
  };
540
540
 
541
+ // INFO: a formula column the monday.com way — added from the header +, written
542
+ // with {Column} references, sorted and filtered on its computed value. Formula
543
+ // rows are read in full (stopLimit) so a sort orders every row, not one page.
544
+ export const DTWFormulaColumn = () => {
545
+ // 1. State
546
+ const [mount, setMount] = useState<boolean>(false);
547
+ const [schema, setSchema] = useState<any[]>(() => [
548
+ ...InlineEditMatrixSchema.filter((each) =>
549
+ ["name", "qty", "status", "due", "owner", "site"].includes(each.id),
550
+ ),
551
+ {
552
+ id: "spend",
553
+ name: "Spend Status",
554
+ type: "formula" as const,
555
+ formulaExpression: 'IF({qty} > 20, "Over budget", "Under budget")',
556
+ optional: true,
557
+ },
558
+ ]);
559
+ const [, setTick] = useState<number>(0);
560
+
561
+ // 2. Refs
562
+ const storeRef = useRef<any[]>(handleBuildMatrixRecords());
563
+ const ownersRef = useRef<any[]>(handleBuildMatrixOwners());
564
+ const countersRef = useRef({ list: 0, update: 0, ownerList: 0 });
565
+ const mockRef = useRef<any>(null);
566
+
567
+ // 4. Effects
568
+ useEffect(() => {
569
+ mockRef.current = handleInstallMatrixMock(
570
+ storeRef.current,
571
+ ownersRef.current,
572
+ countersRef.current,
573
+ () => setTick((previous) => previous + 1),
574
+ );
575
+ setMount(true);
576
+ return () => {
577
+ mockRef.current?.restore();
578
+ mockRef.current = null;
579
+ };
580
+ }, []);
581
+
582
+ // 7. Return
583
+ if (!mount) return <Loading />;
584
+
585
+ return (
586
+ <div className="w-full">
587
+ <div className="font-semibold mb-1 text-gray-900">DTW Formula Column</div>
588
+ <div className="text-sm font-light mb-4 text-gray-500">
589
+ Spend Status is IF({"{Qty}"} &gt; 20, &quot;Over budget&quot;,
590
+ &quot;Under budget&quot;). Add another formula from the header +, pick
591
+ Formula as the type and reference columns in braces. Sort a formula from
592
+ its header or filter it from the flex filters — both run on the computed
593
+ value.
594
+ </div>
595
+ <div className="mb-3 text-xs text-gray-600 font-mono">
596
+ list calls:{" "}
597
+ <span data-testid="formula-list-count">{countersRef.current.list}</span>
598
+ </div>
599
+ <DataTemplateWrapper
600
+ title="Budget"
601
+ type="list"
602
+ collectionId={MATRIX_COLLECTION}
603
+ schema={schema}
604
+ workspace={{
605
+ apiEndpointType: "mongoDB",
606
+ apiEndpoint: MATRIX_ENDPOINT,
607
+ apiEndpointToken: "",
608
+ apiUserToken: "",
609
+ assetEndpoint: "",
610
+ assetUploadEndpoint: "",
611
+ }}
612
+ edit={{ allEditable: true, listEditable: true, deleteBtn: false }}
613
+ bareSettings={{
614
+ bareNewDTW: true,
615
+ bareCompactMode: true,
616
+ bareAddColumns: true,
617
+ bareFlexFilters: true,
618
+ }}
619
+ organise={{
620
+ // INFO: holds the sort the header sets, as an app's saved view would
621
+ persistViewState: "session",
622
+ breadcrumbType: "button",
623
+ onAddColumns: (model) => setSchema(model),
624
+ }}
625
+ handleNavigate={() => {}}
626
+ limit={5}
627
+ />
628
+ </div>
629
+ );
630
+ };
631
+
541
632
  /**
542
633
  * INFO: the shape blixify-space-next actually runs — a live database embedded in
543
634
  * a document, with columns addable from the table and rows addable inline.
@@ -118,10 +118,20 @@ On error the editor draws a red outline (`border border-red-500 rounded`) — th
118
118
 
119
119
  > **`heightClassName` in borderless mode must be a `min-h-*` class**, not a fixed `h-*`. Borderless has no inner `overflow-y-auto`, so a fixed height clips content instead of scrolling it. Leave it unset unless you need a taller empty state.
120
120
 
121
- Selecting text in borderless mode shows a floating toolbar above the selection — Bold, Italic, Underline, Strikethrough, Inline code, Link and Clear formatting. It works inside table cells, and does not show in read-only mode, in a code block, or when an image or embed is selected. The boxed editor keeps its static toolbar instead.
121
+ Selecting text in borderless mode shows a floating toolbar above the selection — Bold, Italic, Underline, Strikethrough, Inline code, Colour, Link and Clear formatting. It works inside table cells, and does not show in read-only mode, in a code block, or when an image or embed is selected. The boxed editor keeps its static toolbar instead.
122
122
 
123
123
  > **Note:** hiding the toolbar also hides its media-upload button. In borderless mode insert images and videos by **dragging or pasting** into the editor, or supply your own control via `customElement`.
124
124
 
125
+ ##### Text colour and highlight
126
+
127
+ The **A** button — in the boxed editor's toolbar and in the selection toolbar — opens a palette of nine text colours and nine highlights (gray, brown, orange, yellow, green, blue, purple, pink, red), each with a Default that takes it off. Markdown has no colour, so coloured text is stored as a small inline tag, the way underline is stored as `<u>`:
128
+
129
+ ```markdown
130
+ <span data-text-color="red" style="color: #dc2626">late</span> and <mark data-highlight="yellow" style="background-color: rgba(250, 204, 21, 0.4); color: inherit">due</mark>
131
+ ```
132
+
133
+ The editor reads back only the palette's names and always rewrites the style from the palette, so a note saved through the editor carries no other style through these tags, and an unknown colour name is saved as plain text. `MarkdownStatic` renders stored HTML as it is, so a note written by another route (an API, an import) is not cleaned by this. Colour pasted from Notion or Google Docs is not kept — only colour copied from another of these editors, which carries the palette names. Highlights are see-through, so they read on light and dark pages; text colours are picked for a white page, and blue, purple and brown are faint on the dark editor. Anything reading a note as plain markdown (an AI, a search snippet) sees the tags.
134
+
125
135
  ##### Read-Only Mode (Preview)
126
136
 
127
137
  ```tsx
@@ -350,6 +360,29 @@ moving away closes it, but opening is gated on the document actually changing
350
360
  otherwise clicking anywhere after a slash already sitting in the prose would pop
351
361
  the menu open unasked.
352
362
 
363
+ ##### Tables
364
+
365
+ How a table is stored depends on what is in it (`tableMarkdown.ts`):
366
+
367
+ - **Simple**: a header row of `th`, one line of inline text per cell, and no
368
+ merged cells. These stay a GFM pipe table. Inline marks are kept and `|` is
369
+ escaped.
370
+ - **Rich**: lists, several paragraphs, line breaks, merged cells, or no header
371
+ row. These are stored as one line of `<table>` html. markdown-it runs with
372
+ `html: true`, so it reads back exactly as written. Tables pasted from Notion
373
+ usually land here.
374
+
375
+ In borderless mode, `TableControls` (`tableControls.tsx`) adds controls to the
376
+ hovered table, or to the table holding the caret on touch and keyboard:
377
+
378
+ - a `+` bar on the right edge adds a column
379
+ - a `+` bar on the bottom edge adds a row
380
+ - a row grip and a column grip each open a menu (insert, duplicate, clear,
381
+ delete, header row)
382
+
383
+ Embedded databases are left alone. While the caret is in a table, the slash menu
384
+ also lists add and delete commands for rows and columns.
385
+
353
386
  ##### Spacing
354
387
 
355
388
  Tailwind Typography is tuned for articles, which leaves 20px between paragraphs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blixify-ui-web-mcp",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "license": "MIT",
5
5
  "main": "lib/mcp/server.js",
6
6
  "bin": {