blixify-ui-web-mcp 0.1.23 → 0.1.24

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.
@@ -42,17 +42,23 @@ import { DetailList } from "blixify-ui-web/components/display/detailList";
42
42
 
43
43
  ##### Optional props
44
44
 
45
- | Name | Type | Description |
46
- | --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------- |
47
- | `addTitleElement` | `ReactElement` | Additional react elemnt to be displayed next to title |
48
- | `headerRightElement` | `ReactElement` | Element anchored to the top-right of the header, opposite the title/description |
49
- | `description` | `string` | |
50
- | `className` | `tailwind class` | |
51
- | `model` | `Model Data Structure` | Only used in Data Template |
52
- | `hideModal` | `boolean` | Only used in Data Template |
53
- | `referenceModel` | `Model Data Structure` | Only used in Data Template |
54
- | `splitView` | `boolean` | Able to split into two |
55
- | `handleReferenceLink` | `(dataId: string, collectionId: string) => void` | Only used in Data Template, when reference being clicked |
45
+ | Name | Type | Description |
46
+ | --------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `addTitleElement` | `ReactElement` | Additional react elemnt to be displayed next to title |
48
+ | `headerRightElement` | `ReactElement` | Element anchored to the top-right of the header, opposite the title/description |
49
+ | `description` | `string` | |
50
+ | `className` | `tailwind class` | |
51
+ | `textClassName` | `tailwind class` | Applied to both the label (`dt`) and value (`dd`) text; overrides the default `text-sm` |
52
+ | `darkMode` | `boolean` | Renders with dark-mode colors |
53
+ | `darkModeBg` | `tailwind class` | Overrides the dark-mode background (defaults to `bg-gray-900`); only used when `darkMode` is set |
54
+ | `model` | `Model Data Structure` | Only used in Data Template |
55
+ | `hideModal` | `boolean` | Only used in Data Template |
56
+ | `referenceModel` | `Model Data Structure` | Only used in Data Template |
57
+ | `splitView` | `boolean` | Able to split into two |
58
+ | `size` | `"default" \| "compact"` | `compact` renders label:value on the same row, much denser than the default stacked layout |
59
+ | `valueTextAlign` | `"left" \| "right"` | Compact only. Forces the value alignment at all widths. When omitted, compact keeps its responsive default (right on mobile, left from `sm` up) |
60
+ | `maxHeight` | `string` | When set, the body scrolls with this max height (e.g. `"60vh"`, `"500px"`) |
61
+ | `handleReferenceLink` | `(dataId: string, collectionId: string) => void` | Only used in Data Template, when reference being clicked |
56
62
 
57
63
  ##### Detail List class
58
64
 
@@ -107,6 +107,28 @@ export const DetailList = () => (
107
107
  />
108
108
  </StorySection>
109
109
 
110
+ <StorySection
111
+ title='Order Summary — valueTextAlign="right"'
112
+ description="Compact list with values pinned to the right at all widths. The final row uses a styled node as its text to render the total bigger and bold."
113
+ >
114
+ <DetailListImpl
115
+ title="Order Summary"
116
+ size="compact"
117
+ valueTextAlign="right"
118
+ list={[
119
+ { title: "Subtotal", text: "RM 1,200.00" },
120
+ { title: "Shipping", text: "RM 15.00" },
121
+ { title: "Tax (6%)", text: "RM 72.90" },
122
+ { title: "Discount", text: "- RM 27.90" },
123
+ {
124
+ title: "Total",
125
+ customTitle: <span className="text-lg font-bold">Total</span>,
126
+ text: <span className="text-lg font-bold">RM 1,260.00</span>,
127
+ },
128
+ ]}
129
+ />
130
+ </StorySection>
131
+
110
132
  <StorySection
111
133
  title="splitView (legacy) — Two-Column Grid"
112
134
  description='Two-up grid layout. Kept for backward compat; new code should prefer size="compact" or groups instead.'
@@ -53,3 +53,17 @@ const passwordRef = useRef(null);
53
53
  | `handleComplete` | `(isDraft?: boolean) => void` | — | Called when all inputs pass validation. `isDraft=true` when triggered via `handleSave()` |
54
54
  | `handleOnFailValidation` | `(ids: string[]) => void` | — | Called when one or more inputs fail. Receives an array of the failed input `id` values |
55
55
  | `disabledOnEnter` | `boolean` | `false` | When `true`, pressing Enter does not trigger form submission |
56
+
57
+ ### Enter to submit
58
+
59
+ Pressing Enter calls `handleSubmit()` when the key event comes from inside this `<form>` — including inputs a child renders through a portal. Enter pressed anywhere else on the page, or inside another `Form`, does not submit this one.
60
+
61
+ Enter does not submit when:
62
+
63
+ | Condition | |
64
+ | --- | --- |
65
+ | `disabledOnEnter` is `true` | set it for forms with a textarea or rich text field that needs Enter for a newline |
66
+ | Shift is held | |
67
+ | an IME composition is in progress (`isComposing`, or `keyCode` 229) | |
68
+ | the key is auto-repeating (`repeat`) | |
69
+ | the focused element is a `button` | |
@@ -6,19 +6,21 @@ import { RadioList } from "../../action/radioList";
6
6
  import { StorySection } from "../../storyUtils";
7
7
  import { TextInput } from "../textInput";
8
8
  import { UploadInput } from "../uploadInput";
9
+ import { TextArea } from "../textArea";
9
10
 
10
11
  const meta = { title: "Input/Form" };
11
12
  export default meta;
12
13
 
13
-
14
14
  export const Form = () => {
15
15
  const formRef = useRef<any>(null);
16
16
  const input1 = useRef<any>(null);
17
17
  const input2 = useRef<any>(null);
18
+ const input3 = useRef<any>(null);
18
19
  const uploadRef = useRef<any>(null);
19
20
 
20
21
  const [radioValue, setRadioValue] = useState("");
21
22
  const [textValue, setTextValue] = useState("");
23
+ const [textAreaValue, setTextAreaValue] = useState("");
22
24
  const [file, setFile] = useState<any>(undefined);
23
25
  const [result, setResult] = useState<string | null>(null);
24
26
 
@@ -38,7 +40,7 @@ export const Form = () => {
38
40
  <div className="w-full max-w-sm">
39
41
  <FormImpl
40
42
  ref={formRef}
41
- refList={[input1, input2, uploadRef]}
43
+ refList={[input1, input2, input3, uploadRef]}
42
44
  handleComplete={() => setResult("All valid — form submitted!")}
43
45
  >
44
46
  <RadioList
@@ -61,6 +63,16 @@ export const Form = () => {
61
63
  containerClassName="mt-3"
62
64
  onChange={(e) => setTextValue(e.target.value)}
63
65
  />
66
+ <TextArea
67
+ ref={input3}
68
+ id="remarks"
69
+ value={textAreaValue}
70
+ rows={3}
71
+ label="Remarks"
72
+ placeholder="Remarks"
73
+ containerClassName="mt-3"
74
+ onChange={(e) => setTextAreaValue(e.target.value)}
75
+ />
64
76
  <UploadInput
65
77
  ref={uploadRef}
66
78
  id="file-upload"
@@ -134,4 +146,4 @@ export const Form = () => {
134
146
  </StorySection>
135
147
  </>
136
148
  );
137
- }
149
+ };
@@ -160,7 +160,7 @@ On error the editor draws a red outline (`border border-red-500 rounded`) — th
160
160
  | `enableImageUpload` | `boolean` | `false` | Enable image upload functionality (drag-and-drop, paste, click to upload) |
161
161
  | `enableVideoUpload` | `boolean` | `false` | Enable video upload functionality (supports MP4, MOV, WebM, AVI, MKV, etc.) |
162
162
  | `compress` | `{ maxFileSize?: number, fileQuality?: number}` | `null` | Image compression settings. `maxFileSize` in MB (default: 30), `fileQuality` 0.0-1.0 (default: 0.8) |
163
- | `lib` | `{ axios: any }` | `null` | External libraries (e.g., axios for API calls) |
163
+ | `lib` | `{ axios?: any, mermaid?: () => Promise<any> }` | `null` | External libraries — axios for API calls; `mermaid` loader (e.g. `() => import("mermaid")`) to draw diagrams |
164
164
  | `uploadEndpoint` | `string` | `null` | API endpoint for uploading media files |
165
165
 
166
166
  ##### Ref Methods
@@ -209,6 +209,57 @@ commands. `onSelect` receives `insertEmbed`, and may await first — the host
209
209
  usually has to create the space before it has an id. Embeds survive the round
210
210
  trip back to markdown as the same `div`, the way an inline `<video>` does.
211
211
 
212
+ ##### Embed view — grid or list
213
+
214
+ Each embed carries a `view` of `grid` (default) or `list`. It is stored on the
215
+ marker only when it is `list`:
216
+
217
+ ```html
218
+ <div data-space-id="guides"></div> <!-- grid -->
219
+ <div data-space-id="guides" data-view="list"></div> <!-- list -->
220
+ ```
221
+
222
+ Grid writes no attribute at all, so documents saved before views existed
223
+ serialise byte-for-byte as they did. Anything other than exactly `list` reads as
224
+ grid. A host that parses these markers itself must accept the optional
225
+ `data-view`.
226
+
227
+ `renderEmbed` receives `view` and `onChangeView(view)` alongside `onRemove`, so
228
+ the host decides what a list row looks like and where its toggle sits. The node
229
+ view wrapper also carries `data-view` for styling. A new embed copies the view
230
+ of the embed directly above it, so a list stays a list as items are added.
231
+
232
+ ##### Mermaid diagrams
233
+
234
+ Pass a loader and fenced ```` ```mermaid ```` blocks draw as diagrams — in the
235
+ editor as a live preview under the source, and in `MarkdownStatic` in place of
236
+ the block. The `/` menu gains **Diagram**, which inserts a starter flowchart.
237
+
238
+ ```tsx
239
+ <MarkdownInput lib={{ axios, mermaid: () => import("mermaid") }} />
240
+ <MarkdownStatic label="" value={body} lib={{ mermaid: () => import("mermaid") }} />
241
+ ```
242
+
243
+ - **The host supplies mermaid.** This library compiles to CommonJS, so importing
244
+ mermaid here would bundle it into every app with a markdown field. Passed in,
245
+ the host's bundler splits it into a chunk fetched only by pages with a diagram.
246
+ Without a loader the block shows as ordinary code and Diagram is not offered.
247
+ - **`securityLevel: "strict"` is always set.** Documents can be published to
248
+ anyone with a link; strict keeps mermaid sanitising labels and refusing click
249
+ handlers.
250
+ - **Html labels keep inline formatting; `<img>` is stripped.** `<b>`, `<i>`,
251
+ `<u>`, `<sup>` and `<br>` draw as formatting. Strict still keeps sanitised
252
+ tags, so `dompurifyConfig.FORBID_TAGS: ["img"]` removes images, which would
253
+ otherwise load a remote file for every reader.
254
+ - **Entity codes such as `#quot;` render garbled** in mermaid 11.17.2 with
255
+ either api (`render` or `run`). Write the character directly instead.
256
+ - **`suppressErrorRendering: true`.** Without it mermaid appends its own error
257
+ graphic to `document.body` on a syntax error, left behind below the page.
258
+ - **A broken diagram never breaks the page.** It shows a message; in
259
+ `MarkdownStatic` the source stays visible under it.
260
+ - **The source is what is stored**, as a normal fenced block, so search and
261
+ anything reading the raw markdown still see the diagram's text.
262
+
212
263
  ##### The slash menu
213
264
 
214
265
  Only a real edit opens it. The detection also runs on selection changes so that
@@ -1,16 +1,34 @@
1
1
  import React, { useState } from "react";
2
- import { MarkdownInput as MarkdownInputImpl } from ".";
2
+ import { MarkdownInput as MarkdownInputImpl, MarkdownStatic } from ".";
3
3
  import { StorySection } from "../../storyUtils";
4
4
 
5
5
  const meta = { title: "Input/Markdown Input" };
6
6
  export default meta;
7
7
 
8
+ // INFO: the host passes mermaid in; the library never imports it
9
+ const handleLoadMermaid = () => import("mermaid");
10
+
11
+ const DIAGRAM_SAMPLE = [
12
+ "Pick Diagram from the / menu to add another.",
13
+ "",
14
+ "```mermaid",
15
+ "flowchart LR",
16
+ " A[<b>Draft</b>] --> B[Review<br/>by <i>owner</i>]",
17
+ " B --> C[Published]",
18
+ "```",
19
+ ].join("\n");
20
+
8
21
  export const MarkdownInput = () => {
9
22
  const [value, setValue] = useState<string | undefined>("");
10
23
  const [noteValue, setNoteValue] = useState<string | undefined>(
11
24
  'A page can hold a live database, not just a link to one.\n\n<div data-space-id="orders"></div>\n\nType / on any line and pick Database to add another.',
12
25
  );
26
+ const [diagramValue, setDiagramValue] = useState<string | undefined>(
27
+ DIAGRAM_SAMPLE,
28
+ );
13
29
  const handleOnChange = (id: string, value?: string) => setValue(value);
30
+ const handleOnDiagramChange = (id: string, value?: string) =>
31
+ setDiagramValue(value);
14
32
  const handleOnNoteChange = (id: string, value?: string) =>
15
33
  setNoteValue(value);
16
34
 
@@ -88,6 +106,26 @@ export const MarkdownInput = () => {
88
106
  {noteValue}
89
107
  </pre>
90
108
  </StorySection>
109
+
110
+ <StorySection
111
+ title="Mermaid diagrams"
112
+ description="lib.mermaid given, so ```mermaid blocks draw as diagrams — under the source while editing, in place of it in MarkdownStatic. Inline tags such as <b>, <i> and <br> format the label; an <img> is stripped."
113
+ >
114
+ <div className="grid w-full gap-6 md:grid-cols-2">
115
+ <MarkdownInputImpl
116
+ id="diagram"
117
+ value={diagramValue ?? ""}
118
+ onChange={handleOnDiagramChange}
119
+ borderless={true}
120
+ lib={{ mermaid: handleLoadMermaid }}
121
+ />
122
+ <MarkdownStatic
123
+ label="MarkdownStatic"
124
+ value={diagramValue ?? ""}
125
+ lib={{ mermaid: handleLoadMermaid }}
126
+ />
127
+ </div>
128
+ </StorySection>
91
129
  </>
92
130
  );
93
131
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blixify-ui-web-mcp",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "license": "MIT",
5
5
  "main": "lib/mcp/server.js",
6
6
  "bin": {