@meistrari/chat-ui-lang 0.3.0 → 0.4.0

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.
Files changed (122) hide show
  1. package/README.md +105 -13
  2. package/dist/actions.d.ts +9 -0
  3. package/dist/answers.d.ts +16 -0
  4. package/dist/components/actions.d.ts +47 -0
  5. package/dist/components/chart.d.ts +23 -0
  6. package/dist/components/chat.d.ts +55 -0
  7. package/dist/components/data.d.ts +31 -0
  8. package/dist/components/form.d.ts +42 -0
  9. package/dist/components/layout.d.ts +1179 -0
  10. package/dist/components/text.d.ts +14 -0
  11. package/dist/define.d.ts +8 -0
  12. package/dist/index.d.ts +25 -0
  13. package/dist/index.js +935 -0
  14. package/dist/library.d.ts +6 -0
  15. package/dist/nuxt/module.d.mts +9 -0
  16. package/dist/nuxt/module.json +9 -0
  17. package/dist/nuxt/module.mjs +31 -0
  18. package/dist/nuxt/runtime/actions.d.ts +15 -0
  19. package/dist/nuxt/runtime/actions.js +40 -0
  20. package/dist/nuxt/runtime/components/openui-widget.d.vue.ts +21 -0
  21. package/dist/nuxt/runtime/components/openui-widget.vue +72 -0
  22. package/dist/nuxt/runtime/components/openui-widget.vue.d.ts +21 -0
  23. package/dist/nuxt/runtime/components/plain-prose.d.vue.ts +7 -0
  24. package/dist/nuxt/runtime/components/plain-prose.vue +12 -0
  25. package/dist/nuxt/runtime/components/plain-prose.vue.d.ts +7 -0
  26. package/dist/nuxt/runtime/components/widgets/bar-chart.d.vue.ts +10 -0
  27. package/dist/nuxt/runtime/components/widgets/bar-chart.vue +39 -0
  28. package/dist/nuxt/runtime/components/widgets/bar-chart.vue.d.ts +10 -0
  29. package/dist/nuxt/runtime/components/widgets/bar.d.vue.ts +10 -0
  30. package/dist/nuxt/runtime/components/widgets/bar.vue +12 -0
  31. package/dist/nuxt/runtime/components/widgets/bar.vue.d.ts +10 -0
  32. package/dist/nuxt/runtime/components/widgets/button.d.vue.ts +11 -0
  33. package/dist/nuxt/runtime/components/widgets/button.vue +30 -0
  34. package/dist/nuxt/runtime/components/widgets/button.vue.d.ts +11 -0
  35. package/dist/nuxt/runtime/components/widgets/buttons.d.vue.ts +8 -0
  36. package/dist/nuxt/runtime/components/widgets/buttons.vue +14 -0
  37. package/dist/nuxt/runtime/components/widgets/buttons.vue.d.ts +8 -0
  38. package/dist/nuxt/runtime/components/widgets/callout.d.vue.ts +11 -0
  39. package/dist/nuxt/runtime/components/widgets/callout.vue +27 -0
  40. package/dist/nuxt/runtime/components/widgets/callout.vue.d.ts +11 -0
  41. package/dist/nuxt/runtime/components/widgets/card.d.vue.ts +9 -0
  42. package/dist/nuxt/runtime/components/widgets/card.vue +19 -0
  43. package/dist/nuxt/runtime/components/widgets/card.vue.d.ts +9 -0
  44. package/dist/nuxt/runtime/components/widgets/checkbox.d.vue.ts +9 -0
  45. package/dist/nuxt/runtime/components/widgets/checkbox.vue +26 -0
  46. package/dist/nuxt/runtime/components/widgets/checkbox.vue.d.ts +9 -0
  47. package/dist/nuxt/runtime/components/widgets/citation.d.vue.ts +9 -0
  48. package/dist/nuxt/runtime/components/widgets/citation.vue +9 -0
  49. package/dist/nuxt/runtime/components/widgets/citation.vue.d.ts +9 -0
  50. package/dist/nuxt/runtime/components/widgets/col.d.vue.ts +10 -0
  51. package/dist/nuxt/runtime/components/widgets/col.vue +16 -0
  52. package/dist/nuxt/runtime/components/widgets/col.vue.d.ts +10 -0
  53. package/dist/nuxt/runtime/components/widgets/file-card.d.vue.ts +9 -0
  54. package/dist/nuxt/runtime/components/widgets/file-card.vue +9 -0
  55. package/dist/nuxt/runtime/components/widgets/file-card.vue.d.ts +9 -0
  56. package/dist/nuxt/runtime/components/widgets/form.d.vue.ts +10 -0
  57. package/dist/nuxt/runtime/components/widgets/form.vue +60 -0
  58. package/dist/nuxt/runtime/components/widgets/form.vue.d.ts +10 -0
  59. package/dist/nuxt/runtime/components/widgets/image.d.vue.ts +10 -0
  60. package/dist/nuxt/runtime/components/widgets/image.vue +34 -0
  61. package/dist/nuxt/runtime/components/widgets/image.vue.d.ts +10 -0
  62. package/dist/nuxt/runtime/components/widgets/input.d.vue.ts +10 -0
  63. package/dist/nuxt/runtime/components/widgets/input.vue +23 -0
  64. package/dist/nuxt/runtime/components/widgets/input.vue.d.ts +10 -0
  65. package/dist/nuxt/runtime/components/widgets/markdown.d.vue.ts +9 -0
  66. package/dist/nuxt/runtime/components/widgets/markdown.vue +14 -0
  67. package/dist/nuxt/runtime/components/widgets/markdown.vue.d.ts +9 -0
  68. package/dist/nuxt/runtime/components/widgets/metric.d.vue.ts +11 -0
  69. package/dist/nuxt/runtime/components/widgets/metric.vue +20 -0
  70. package/dist/nuxt/runtime/components/widgets/metric.vue.d.ts +11 -0
  71. package/dist/nuxt/runtime/components/widgets/option.d.vue.ts +10 -0
  72. package/dist/nuxt/runtime/components/widgets/option.vue +12 -0
  73. package/dist/nuxt/runtime/components/widgets/option.vue.d.ts +10 -0
  74. package/dist/nuxt/runtime/components/widgets/question.d.vue.ts +11 -0
  75. package/dist/nuxt/runtime/components/widgets/question.vue +15 -0
  76. package/dist/nuxt/runtime/components/widgets/question.vue.d.ts +11 -0
  77. package/dist/nuxt/runtime/components/widgets/questions.d.vue.ts +9 -0
  78. package/dist/nuxt/runtime/components/widgets/questions.vue +91 -0
  79. package/dist/nuxt/runtime/components/widgets/questions.vue.d.ts +9 -0
  80. package/dist/nuxt/runtime/components/widgets/select.d.vue.ts +10 -0
  81. package/dist/nuxt/runtime/components/widgets/select.vue +27 -0
  82. package/dist/nuxt/runtime/components/widgets/select.vue.d.ts +10 -0
  83. package/dist/nuxt/runtime/components/widgets/stack.d.vue.ts +9 -0
  84. package/dist/nuxt/runtime/components/widgets/stack.vue +18 -0
  85. package/dist/nuxt/runtime/components/widgets/stack.vue.d.ts +9 -0
  86. package/dist/nuxt/runtime/components/widgets/table.d.vue.ts +9 -0
  87. package/dist/nuxt/runtime/components/widgets/table.vue +41 -0
  88. package/dist/nuxt/runtime/components/widgets/table.vue.d.ts +9 -0
  89. package/dist/nuxt/runtime/context.d.ts +30 -0
  90. package/dist/nuxt/runtime/context.js +4 -0
  91. package/dist/nuxt/runtime/form-fields.d.ts +10 -0
  92. package/dist/nuxt/runtime/form-fields.js +30 -0
  93. package/dist/nuxt/runtime/program.d.ts +15 -0
  94. package/dist/nuxt/runtime/program.js +74 -0
  95. package/dist/nuxt/runtime/registry.d.ts +4 -0
  96. package/dist/nuxt/runtime/registry.js +44 -0
  97. package/dist/nuxt/runtime/render.d.ts +21 -0
  98. package/dist/nuxt/runtime/render.js +48 -0
  99. package/dist/nuxt/runtime/use-openui-context.d.ts +2 -0
  100. package/dist/nuxt/runtime/use-openui-context.js +8 -0
  101. package/dist/nuxt/types.d.mts +9 -0
  102. package/dist/profiles.d.ts +10 -0
  103. package/dist/prompt.d.ts +5 -0
  104. package/dist/safe-url.d.ts +2 -0
  105. package/dist/segments.d.ts +16 -0
  106. package/dist/slack.d.ts +28 -0
  107. package/dist/version.d.ts +2 -0
  108. package/dist/widget-context.d.ts +13 -0
  109. package/package.json +72 -31
  110. package/src/actions.ts +0 -12
  111. package/src/components/actions.ts +0 -21
  112. package/src/components/chart.ts +0 -22
  113. package/src/components/chat.ts +0 -60
  114. package/src/components/data.ts +0 -32
  115. package/src/components/form.ts +0 -41
  116. package/src/components/layout.ts +0 -30
  117. package/src/components/text.ts +0 -20
  118. package/src/define.ts +0 -9
  119. package/src/index.ts +0 -14
  120. package/src/library.ts +0 -64
  121. package/src/prompt.ts +0 -54
  122. package/src/version.ts +0 -2
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ props: {
3
+ columns: unknown[];
4
+ caption?: string;
5
+ };
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,41 @@
1
+ <script setup>
2
+ const props = defineProps({
3
+ props: { type: Object, required: true }
4
+ });
5
+ function toColumn(node) {
6
+ const column = node;
7
+ if (column?.typeName !== "Col" || !column.props)
8
+ return null;
9
+ return {
10
+ label: typeof column.props.label === "string" ? column.props.label : "",
11
+ values: Array.isArray(column.props.values) ? column.props.values.filter((value) => typeof value === "string" || typeof value === "number") : [],
12
+ align: column.props.align === "right" ? "right" : "left"
13
+ };
14
+ }
15
+ const columns = computed(() => (Array.isArray(props.props.columns) ? props.props.columns : []).map(toColumn).filter((column) => column !== null));
16
+ const rowCount = computed(() => columns.value.reduce((max, column) => Math.max(max, column.values.length), 0));
17
+ </script>
18
+
19
+ <template>
20
+ <div data-openui="table">
21
+ <TelaTable>
22
+ <TelaTableCaption v-if="props.props.caption">
23
+ {{ props.props.caption }}
24
+ </TelaTableCaption>
25
+ <TelaTableHeader>
26
+ <TelaTableRow>
27
+ <TelaTableHead v-for="(column, index) in columns" :key="index" :class="column.align === 'right' ? 'text-right' : ''">
28
+ {{ column.label }}
29
+ </TelaTableHead>
30
+ </TelaTableRow>
31
+ </TelaTableHeader>
32
+ <TelaTableBody>
33
+ <TelaTableRow v-for="row in rowCount" :key="row">
34
+ <TelaTableCell v-for="(column, index) in columns" :key="index" :class="column.align === 'right' ? 'text-right' : ''">
35
+ {{ column.values[row - 1] ?? "" }}
36
+ </TelaTableCell>
37
+ </TelaTableRow>
38
+ </TelaTableBody>
39
+ </TelaTable>
40
+ </div>
41
+ </template>
@@ -0,0 +1,9 @@
1
+ type __VLS_Props = {
2
+ props: {
3
+ columns: unknown[];
4
+ caption?: string;
5
+ };
6
+ };
7
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
@@ -0,0 +1,30 @@
1
+ import type { componentNames } from '@meistrari/chat-ui-lang';
2
+ import type { ActionPlan, OpenUIError } from '@openuidev/lang-core';
3
+ import type { Component, InjectionKey, Ref, VNode } from 'vue';
4
+ export type OpenuiComponentName = (typeof componentNames)[number];
5
+ export type RenderedNode = VNode | string | null | Array<VNode | string | null>;
6
+ export interface OpenuiContext {
7
+ renderNode: (value: unknown) => RenderedNode;
8
+ /** Clicks are honoured only while the host allows it and the block is complete. */
9
+ interactive: Ref<boolean>;
10
+ /** Resolves once the action finished; returns false when it was rejected. A widget answers once: after an accepted send, `interactive` turns false. */
11
+ triggerAction: (label: string, plan?: ActionPlan) => Promise<boolean>;
12
+ }
13
+ export declare const OPENUI_CONTEXT_KEY: InjectionKey<OpenuiContext>;
14
+ export type OpenuiErrorReporter = (errors: OpenUIError[], code: string) => void;
15
+ /** Provided by OpenuiWidget, which forwards what widgets report while rendering (such as repeated form fields) as its `errors` event. */
16
+ export declare const OPENUI_ERROR_REPORTER_KEY: InjectionKey<OpenuiErrorReporter>;
17
+ export interface OpenuiHost {
18
+ /** Renders markdown prose for Markdown and Callout, with props `text` and `partial`; plain text when absent. */
19
+ prose?: Component;
20
+ /** Replaces the package widget of the same name, e.g. a FileCard bound to the host's file preview. */
21
+ components?: Partial<Record<OpenuiComponentName, Component>>;
22
+ }
23
+ /** A ref, so that a host an app replaces after mounting reaches the widgets that read it. */
24
+ export declare const OPENUI_HOST_KEY: InjectionKey<Ref<OpenuiHost>>;
25
+ export interface OpenuiFormState {
26
+ get: (name: string) => unknown;
27
+ set: (name: string, value: unknown) => void;
28
+ }
29
+ /** Provided by Form so its fields share one state bag. */
30
+ export declare const OPENUI_FORM_KEY: InjectionKey<OpenuiFormState>;
@@ -0,0 +1,4 @@
1
+ export const OPENUI_CONTEXT_KEY = Symbol("openui-context");
2
+ export const OPENUI_ERROR_REPORTER_KEY = Symbol("openui-error-reporter");
3
+ export const OPENUI_HOST_KEY = Symbol("openui-host");
4
+ export const OPENUI_FORM_KEY = Symbol("openui-form");
@@ -0,0 +1,10 @@
1
+ export type FormField = {
2
+ name: string;
3
+ label: string;
4
+ type: string;
5
+ };
6
+ /** Fields share state by name, so a repeated name keeps its first field and is reported. */
7
+ export declare function collectFormFields(value: unknown): {
8
+ fields: FormField[];
9
+ duplicates: string[];
10
+ };
@@ -0,0 +1,30 @@
1
+ const FIELD_TYPES = /* @__PURE__ */ new Set(["Input", "Select", "Checkbox"]);
2
+ function collect(value, into) {
3
+ if (Array.isArray(value)) {
4
+ for (const item of value)
5
+ collect(item, into);
6
+ return;
7
+ }
8
+ const node = value;
9
+ if (!node || typeof node !== "object" || !node.props)
10
+ return;
11
+ if (node.typeName && FIELD_TYPES.has(node.typeName) && typeof node.props.name === "string") {
12
+ into.push({ name: node.props.name, label: typeof node.props.label === "string" ? node.props.label : node.props.name, type: node.typeName });
13
+ return;
14
+ }
15
+ for (const child of Object.values(node.props))
16
+ collect(child, into);
17
+ }
18
+ export function collectFormFields(value) {
19
+ const found = [];
20
+ collect(value, found);
21
+ const fields = [];
22
+ const duplicates = /* @__PURE__ */ new Set();
23
+ for (const field of found) {
24
+ if (fields.some((existing) => existing.name === field.name))
25
+ duplicates.add(field.name);
26
+ else
27
+ fields.push(field);
28
+ }
29
+ return { fields, duplicates: [...duplicates] };
30
+ }
@@ -0,0 +1,15 @@
1
+ import { createStore } from '@openuidev/lang-core';
2
+ import type { ElementNode, OpenUIError } from '@openuidev/lang-core';
3
+ import type { ComputedRef, Ref } from 'vue';
4
+ export type OpenuiProgram = {
5
+ root: ComputedRef<ElementNode | null>;
6
+ errors: ComputedRef<OpenUIError[]>;
7
+ incomplete: ComputedRef<boolean>;
8
+ stateDefaults: ComputedRef<Record<string, unknown>>;
9
+ store: ReturnType<typeof createStore>;
10
+ };
11
+ /**
12
+ * Parses a streamed OpenUI Lang program. `code` may grow while `complete` is
13
+ * false; the parser diffs against its buffer so each chunk is parsed once.
14
+ */
15
+ export declare function useOpenuiProgram(code: Ref<string>, complete: Ref<boolean>): OpenuiProgram;
@@ -0,0 +1,74 @@
1
+ import { createStore, createStreamingParser, evaluateElementProps } from "@openuidev/lang-core";
2
+ import { library } from "@meistrari/chat-ui-lang";
3
+ import { computed, shallowRef, watch } from "vue";
4
+ const schema = library.toJSONSchema();
5
+ function parserErrors(result) {
6
+ return result.meta.errors.map((error) => ({
7
+ source: "parser",
8
+ code: error.code,
9
+ message: error.message,
10
+ component: error.component,
11
+ path: error.path || void 0,
12
+ statementId: error.statementId
13
+ }));
14
+ }
15
+ function describe(error) {
16
+ return error instanceof Error ? error.message : String(error);
17
+ }
18
+ export function useOpenuiProgram(code, complete) {
19
+ const parser = createStreamingParser(schema, library.root);
20
+ const store = createStore();
21
+ const storeRevision = shallowRef(0);
22
+ store.subscribe(() => {
23
+ storeRevision.value++;
24
+ });
25
+ const parsed = computed(() => {
26
+ if (!code.value.trim())
27
+ return { result: null, errors: [] };
28
+ try {
29
+ const result = parser.set(code.value, complete.value);
30
+ return { result, errors: parserErrors(result) };
31
+ } catch (error) {
32
+ return { result: null, errors: [{ source: "parser", code: "parse-exception", message: describe(error) }] };
33
+ }
34
+ });
35
+ const stateDefaults = computed(() => parsed.value.result?.stateDeclarations ?? {});
36
+ let seededDefaults = "";
37
+ watch(stateDefaults, (defaults) => {
38
+ const key = JSON.stringify(defaults);
39
+ if (key === seededDefaults)
40
+ return;
41
+ seededDefaults = key;
42
+ store.initialize(defaults, {});
43
+ }, { immediate: true });
44
+ const evaluated = computed(() => {
45
+ const result = parsed.value.result;
46
+ if (!result?.root)
47
+ return { root: null, errors: [] };
48
+ void storeRevision.value;
49
+ const errors = [];
50
+ const context = {
51
+ ctx: {
52
+ getState: (name) => store.get(name),
53
+ resolveRef: () => null,
54
+ // Without the library actions, @CopyText(...) evaluates to a plain node and its button sends the label instead.
55
+ actions: schema.actions
56
+ },
57
+ library,
58
+ store,
59
+ errors
60
+ };
61
+ try {
62
+ return { root: evaluateElementProps(result.root, context), errors };
63
+ } catch (error) {
64
+ return { root: result.root, errors: [...errors, { source: "runtime", code: "runtime-error", message: describe(error) }] };
65
+ }
66
+ });
67
+ return {
68
+ root: computed(() => evaluated.value.root),
69
+ errors: computed(() => [...parsed.value.errors, ...evaluated.value.errors]),
70
+ incomplete: computed(() => !complete.value || (parsed.value.result?.meta.incomplete ?? true)),
71
+ stateDefaults,
72
+ store
73
+ };
74
+ }
@@ -0,0 +1,4 @@
1
+ import type { Component } from 'vue';
2
+ import type { OpenuiComponentName } from './context.js';
3
+ /** Every component the library exposes to the model must have a renderer here; hosts may replace any of them through OpenuiHost. */
4
+ export declare const OPENUI_REGISTRY: Record<OpenuiComponentName, Component>;
@@ -0,0 +1,44 @@
1
+ import OpenuiBar from "./components/widgets/bar.vue";
2
+ import OpenuiBarChart from "./components/widgets/bar-chart.vue";
3
+ import OpenuiButton from "./components/widgets/button.vue";
4
+ import OpenuiButtons from "./components/widgets/buttons.vue";
5
+ import OpenuiCallout from "./components/widgets/callout.vue";
6
+ import OpenuiCard from "./components/widgets/card.vue";
7
+ import OpenuiCheckbox from "./components/widgets/checkbox.vue";
8
+ import OpenuiCitation from "./components/widgets/citation.vue";
9
+ import OpenuiCol from "./components/widgets/col.vue";
10
+ import OpenuiFileCard from "./components/widgets/file-card.vue";
11
+ import OpenuiForm from "./components/widgets/form.vue";
12
+ import OpenuiImage from "./components/widgets/image.vue";
13
+ import OpenuiInput from "./components/widgets/input.vue";
14
+ import OpenuiMarkdown from "./components/widgets/markdown.vue";
15
+ import OpenuiMetric from "./components/widgets/metric.vue";
16
+ import OpenuiOption from "./components/widgets/option.vue";
17
+ import OpenuiQuestion from "./components/widgets/question.vue";
18
+ import OpenuiQuestions from "./components/widgets/questions.vue";
19
+ import OpenuiSelect from "./components/widgets/select.vue";
20
+ import OpenuiStack from "./components/widgets/stack.vue";
21
+ import OpenuiTable from "./components/widgets/table.vue";
22
+ export const OPENUI_REGISTRY = {
23
+ Stack: OpenuiStack,
24
+ Card: OpenuiCard,
25
+ Markdown: OpenuiMarkdown,
26
+ Callout: OpenuiCallout,
27
+ Metric: OpenuiMetric,
28
+ Col: OpenuiCol,
29
+ Table: OpenuiTable,
30
+ Bar: OpenuiBar,
31
+ BarChart: OpenuiBarChart,
32
+ Button: OpenuiButton,
33
+ Buttons: OpenuiButtons,
34
+ Input: OpenuiInput,
35
+ Select: OpenuiSelect,
36
+ Checkbox: OpenuiCheckbox,
37
+ Form: OpenuiForm,
38
+ Option: OpenuiOption,
39
+ Question: OpenuiQuestion,
40
+ Questions: OpenuiQuestions,
41
+ FileCard: OpenuiFileCard,
42
+ Image: OpenuiImage,
43
+ Citation: OpenuiCitation
44
+ };
@@ -0,0 +1,21 @@
1
+ import type { ElementNode } from '@openuidev/lang-core';
2
+ import type { PropType } from 'vue';
3
+ import type { RenderedNode } from './context.js';
4
+ /**
5
+ * One parsed element. Unknown components render nothing: the parser already
6
+ * reported them, and dropping a node is safer than showing raw text.
7
+ */
8
+ export declare const OpenuiNode: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
9
+ node: {
10
+ type: PropType<ElementNode>;
11
+ required: true;
12
+ };
13
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
14
+ [key: string]: any;
15
+ }> | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
16
+ node: {
17
+ type: PropType<ElementNode>;
18
+ required: true;
19
+ };
20
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
21
+ export declare function renderNode(value: unknown): RenderedNode;
@@ -0,0 +1,48 @@
1
+ import { defineComponent, h, inject, onErrorCaptured, ref, watch } from "vue";
2
+ import { OPENUI_HOST_KEY } from "./context.js";
3
+ import { OPENUI_REGISTRY } from "./registry.js";
4
+ function isElementNode(value) {
5
+ return typeof value === "object" && value !== null && value.type === "element";
6
+ }
7
+ export const OpenuiNode = defineComponent({
8
+ name: "OpenuiNode",
9
+ props: {
10
+ node: { type: Object, required: true }
11
+ },
12
+ setup(props) {
13
+ const host = inject(OPENUI_HOST_KEY, ref({}));
14
+ const failed = ref(false);
15
+ onErrorCaptured((error) => {
16
+ console.error("[openui] widget render failed", { component: props.node.typeName, error });
17
+ failed.value = true;
18
+ return false;
19
+ });
20
+ watch(() => props.node, () => {
21
+ failed.value = false;
22
+ });
23
+ return () => {
24
+ const name = props.node.typeName;
25
+ const component = host.value.components?.[name] ?? OPENUI_REGISTRY[name];
26
+ if (!component || failed.value)
27
+ return null;
28
+ return h(component, { props: props.node.props, partial: props.node.partial });
29
+ };
30
+ }
31
+ });
32
+ export function renderNode(value) {
33
+ if (value == null)
34
+ return null;
35
+ if (typeof value === "string")
36
+ return value;
37
+ if (typeof value === "number" || typeof value === "boolean")
38
+ return String(value);
39
+ if (Array.isArray(value)) {
40
+ return value.flatMap((item, index) => {
41
+ const rendered = isElementNode(item) ? h(OpenuiNode, { node: item, key: item.statementId ?? `inline-${index}` }) : renderNode(item);
42
+ return Array.isArray(rendered) ? rendered : [rendered];
43
+ });
44
+ }
45
+ if (isElementNode(value))
46
+ return h(OpenuiNode, { node: value, key: value.statementId });
47
+ return null;
48
+ }
@@ -0,0 +1,2 @@
1
+ import type { OpenuiContext } from './context.js';
2
+ export declare function useOpenuiContext(): OpenuiContext;
@@ -0,0 +1,8 @@
1
+ import { inject } from "vue";
2
+ import { OPENUI_CONTEXT_KEY } from "./context.js";
3
+ export function useOpenuiContext() {
4
+ const context = inject(OPENUI_CONTEXT_KEY, null);
5
+ if (!context)
6
+ throw new Error("OpenUI widgets must render inside OpenuiWidget");
7
+ return context;
8
+ }
@@ -0,0 +1,9 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { type OpenuiHost } from '../../dist/nuxt/runtime/context.js'
8
+
9
+ export { default, type registerOpenuiWidget } from './module.mjs'
@@ -0,0 +1,10 @@
1
+ import type { UiLibrary } from './library';
2
+ export declare const UI_PROFILES: readonly ["web", "slack"];
3
+ export type UiProfile = (typeof UI_PROFILES)[number];
4
+ export type UiProfileDefinition = {
5
+ library: UiLibrary;
6
+ /** Rules only this surface needs, appended to the shared prompt. */
7
+ rules: string[];
8
+ examples: string[];
9
+ };
10
+ export declare const profiles: Record<UiProfile, UiProfileDefinition>;
@@ -0,0 +1,5 @@
1
+ import type { UiProfile } from './profiles';
2
+ export declare function prompt(options: {
3
+ profile: UiProfile;
4
+ additionalRules?: string[];
5
+ }): string;
@@ -0,0 +1,2 @@
1
+ /** Only absolute URLs with an explicit scheme; relative and protocol-relative values are rejected. */
2
+ export declare function isSafeUrl(url: string): boolean;
@@ -0,0 +1,16 @@
1
+ export type AnswerSegment = {
2
+ kind: 'markdown';
3
+ text: string;
4
+ } | {
5
+ kind: 'widget';
6
+ code: string;
7
+ complete: boolean;
8
+ };
9
+ export declare const WIDGET_PENDING_TEXT = "Montando um widget\u2026";
10
+ /** Markdown and widget blocks in order; a widget still open at the end is incomplete. Empty markdown between blocks is dropped. */
11
+ export declare function splitAnswer(content: string): AnswerSegment[];
12
+ /**
13
+ * The streamed answer with every widget block, finished or still open, replaced by one placeholder line. It only grows as
14
+ * the text grows: a last line that may still become a widget opener is held back, so widget code is never shown.
15
+ */
16
+ export declare function maskWidgets(content: string, placeholder?: string): string;
@@ -0,0 +1,28 @@
1
+ export type SlackBlock = Record<string, unknown>;
2
+ export type SlackAnswer = {
3
+ /** The answer as plain markdown with every widget written out; Slack uses it for notifications and screen readers. */
4
+ text: string;
5
+ /** Empty when even the answer written out as prose does not fit `maxBlocks`: post `text` alone. */
6
+ blocks: SlackBlock[];
7
+ };
8
+ export type SlackRenderOptions = {
9
+ /** Renders markdown prose as blocks, the way the connector posts answers without widgets. */
10
+ prose: (text: string) => SlackBlock[];
11
+ /** Thread of the published answer. Without it buttons render as text, since a click could not continue the conversation. */
12
+ threadTs?: string;
13
+ /** Blocks left for the answer in its message; above it the answer goes out as text. */
14
+ maxBlocks: number;
15
+ };
16
+ export type SlackWidgetAction = {
17
+ threadTs: string;
18
+ message: string;
19
+ };
20
+ export declare function isSlackWidgetSendAction(actionId: string): boolean;
21
+ export declare function isSlackWidgetLinkAction(actionId: string): boolean;
22
+ /** The thread and message a widget button carries; null for any value this renderer did not write. */
23
+ export declare function parseSlackWidgetAction(value: string): SlackWidgetAction | null;
24
+ /**
25
+ * Block Kit for an answer that has widget blocks; null when it has none, so the caller keeps its plain path. Any program
26
+ * renders: what Slack cannot show (charts, forms, questions, files) is written out as text.
27
+ */
28
+ export declare function renderSlackAnswer(content: string, options: SlackRenderOptions): SlackAnswer | null;
@@ -0,0 +1,2 @@
1
+ export declare const LIBRARY_VERSION = "0.4.0";
2
+ export declare const UI_LANG_FENCE = "openui-lang";
@@ -0,0 +1,13 @@
1
+ export type WidgetContextDrop = {
2
+ reason: 'not-serialisable';
3
+ error: string;
4
+ } | {
5
+ reason: 'too-large';
6
+ length: number;
7
+ limit: number;
8
+ };
9
+ /** The `@ToAssistant` context appended to the sent message; context that cannot travel is dropped and reported to onDrop. */
10
+ export declare function formatWidgetContext(context: unknown, options?: {
11
+ limit?: number;
12
+ onDrop?: (drop: WidgetContextDrop) => void;
13
+ }): string;
package/package.json CHANGED
@@ -1,37 +1,78 @@
1
1
  {
2
- "name": "@meistrari/chat-ui-lang",
3
- "version": "0.3.0",
4
- "type": "module",
5
- "main": "./src/index.ts",
6
- "types": "./src/index.ts",
7
- "exports": {
8
- ".": "./src/index.ts"
9
- },
10
- "files": [
11
- "src",
12
- "README.md"
13
- ],
14
- "scripts": {
15
- "test": "bun test",
16
- "typecheck": "tsc --noEmit",
17
- "measure": "bun scripts/measure.ts"
18
- },
19
- "dependencies": {
20
- "@openuidev/lang-core": "1.0.0",
21
- "zod": "^4.1.13"
2
+ "name": "@meistrari/chat-ui-lang",
3
+ "version": "0.4.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
22
11
  },
23
- "devDependencies": {
24
- "@anthropic-ai/sdk": "0.133.0",
25
- "typescript": "5.9.3"
12
+ "./nuxt": {
13
+ "types": "./dist/nuxt/module.d.mts",
14
+ "import": "./dist/nuxt/module.mjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "build": "bun run build:catalogue && bun run build:nuxt",
23
+ "build:catalogue": "rm -rf dist && bun build src/index.ts --outdir dist --target node --format esm --packages external && tsc -p tsconfig.build.json",
24
+ "build:nuxt": "mkdir -p .nuxt && { [ -f .nuxt/tsconfig.json ] || printf '{}' > .nuxt/tsconfig.json; } && { [ -f .nuxt/tsconfig.server.json ] || printf '{}' > .nuxt/tsconfig.server.json; } && nuxt-module-build build --outDir dist/nuxt",
25
+ "prepack": "bun run build",
26
+ "test": "bun test ./test && vitest run",
27
+ "typecheck": "tsc --noEmit && nuxi prepare && vue-tsc --noEmit -p src/nuxt/tsconfig.json"
28
+ },
29
+ "dependencies": {
30
+ "@openuidev/lang-core": "1.0.0",
31
+ "zod": "^4.1.13"
32
+ },
33
+ "peerDependencies": {
34
+ "@meistrari/tela-build": "^1.79.0",
35
+ "nuxt": "^3.17.0 || ^4.0.0",
36
+ "vue": "^3.5.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "@meistrari/tela-build": {
40
+ "optional": true
26
41
  },
27
- "description": "Catalogue of chat widgets the Tela agent may emit as OpenUI Lang, shared by chat-nuxt and agent-api",
28
- "license": "UNLICENSED",
29
- "repository": {
30
- "type": "git",
31
- "url": "https://github.com/meistrari/chat-app.git",
32
- "directory": "packages/ui-lang"
42
+ "nuxt": {
43
+ "optional": true
33
44
  },
34
- "publishConfig": {
35
- "access": "public"
45
+ "vue": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@nuxt/module-builder": "1.0.2",
51
+ "nuxt": "4.4.8",
52
+ "typescript": "5.9.3",
53
+ "vitest": "3.2.4",
54
+ "vue-tsc": "3.2.7"
55
+ },
56
+ "description": "Catalogue of chat widgets the Tela agent may emit as OpenUI Lang, shared by chat-nuxt and agent-api, and its Nuxt renderer",
57
+ "license": "UNLICENSED",
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "https://github.com/meistrari/chat-app.git",
61
+ "directory": "packages/ui-lang"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public",
65
+ "main": "./dist/index.js",
66
+ "types": "./dist/index.d.ts",
67
+ "exports": {
68
+ ".": {
69
+ "types": "./dist/index.d.ts",
70
+ "import": "./dist/index.js"
71
+ },
72
+ "./nuxt": {
73
+ "types": "./dist/nuxt/module.d.mts",
74
+ "import": "./dist/nuxt/module.mjs"
75
+ }
36
76
  }
77
+ }
37
78
  }
package/src/actions.ts DELETED
@@ -1,12 +0,0 @@
1
- import { defineAction } from '@openuidev/lang-core'
2
- import { z } from 'zod'
3
-
4
- export const CopyText = defineAction({
5
- name: 'CopyText',
6
- description: 'Copia um texto para a área de transferência do usuário.',
7
- params: z.object({
8
- text: z.string(),
9
- }),
10
- })
11
-
12
- export const actions = { CopyText }
@@ -1,21 +0,0 @@
1
- import { action } from '@openuidev/lang-core'
2
- import { z } from 'zod'
3
- import { defineUiComponent } from '../define'
4
-
5
- export const Button = defineUiComponent({
6
- name: 'Button',
7
- description: 'Botão de ação. Com @ToAssistant envia uma mensagem como se o usuário tivesse digitado.',
8
- props: z.object({
9
- label: z.string(),
10
- action: action().optional(),
11
- variant: z.enum(['primary', 'secondary']).optional().describe('Padrão secondary; no máximo um primary por grupo'),
12
- }),
13
- })
14
-
15
- export const Buttons = defineUiComponent({
16
- name: 'Buttons',
17
- description: 'Grupo horizontal de botões.',
18
- props: z.object({
19
- buttons: z.array(Button.ref).min(1).max(4),
20
- }),
21
- })
@@ -1,22 +0,0 @@
1
- import { z } from 'zod'
2
- import { defineUiComponent } from '../define'
3
-
4
- export const Bar = defineUiComponent({
5
- name: 'Bar',
6
- description: 'Uma barra do gráfico.',
7
- props: z.object({
8
- label: z.string(),
9
- value: z.number().describe('Valor numérico usado para o tamanho da barra'),
10
- display: z.string().optional().describe('Valor formatado para exibir, ex: "R$ 1,2 mi"'),
11
- }),
12
- })
13
-
14
- export const BarChart = defineUiComponent({
15
- name: 'BarChart',
16
- description: 'Gráfico de barras horizontais para comparar até 8 valores da mesma unidade.',
17
- props: z.object({
18
- title: z.string(),
19
- bars: z.array(Bar.ref).min(2).max(8),
20
- tone: z.enum(['neutral', 'positive', 'negative']).optional(),
21
- }),
22
- })