lavish-axi 0.1.29 → 0.1.31

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
@@ -130,12 +130,15 @@ pnpm link
130
130
  - **File-path identity** - Sessions are keyed by the canonical HTML file path, so agents do not need opaque IDs.
131
131
  - **Portable artifacts** - The artifact runs in an iframe while Lavish injects a small SDK for annotations, snapshots, and feedback controls.
132
132
  Lavish does not inject any design system, so the saved HTML file renders identically whether you open it through `lavish-axi` or directly in a browser.
133
- Before writing HTML, choose a design system in strict priority order: follow a user-requested look first; otherwise inspect the current project for a Tailwind or theme config, CSS variables or design tokens, a component library, brand assets, or existing styled pages and match what you find; only when both come up empty, run `lavish-axi design` for a copy-pasteable Tailwind CSS v4 + DaisyUI v5 CDN fallback.
133
+ Before writing HTML, choose a design system in strict priority order: follow a user-requested look first; otherwise inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's Tailwind or theme config, CSS variables or design tokens, component library, brand assets, or existing styled pages.
134
+ If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo.
135
+ Only when both come up empty, run `lavish-axi design` for a copy-pasteable Tailwind CSS v4 + DaisyUI v5 CDN fallback.
134
136
  That fallback guidance recommends DaisyUI's `luxury` theme by default and warns not to `@apply` DaisyUI classes inside Tailwind browser-runtime style blocks.
135
137
  - **Local assets** - Copy local images, CSS, fonts, and scripts next to the HTML artifact and reference them with relative paths from that directory; root-prefixed paths such as `/assets/logo.png` will not resolve through Lavish's artifact route.
136
138
  - **Live reload** - Lavish watches the HTML artifact file by default and preserves the artifact iframe scroll position across reloads. To also reload on sibling asset changes, add `data-lavish-live-reload-root` to the root element or `<meta name="lavish-live-reload" content="root">`.
137
- - **Feedback controls** - Native form controls (radios, checkboxes, inputs, selects, buttons, labels, contenteditable) are interactive automatically, so they do not need `data-lavish-action`; wire their handlers to `window.lavish.queuePrompt()` or `window.lavish.sendQueuedPrompts()` to send feedback.
138
- Mark only custom (non-native) clickable elements with `data-lavish-action` so Lavish does not annotate them.
139
+ - **Feedback controls** - Native form controls (radios, checkboxes, inputs, selects, buttons, labels, contenteditable) are interactive automatically, so they do not need `data-lavish-action`.
140
+ For reversible choices, let option clicks update local state, then queue exactly one final answer from a per-question submit or Queue answer button with `window.lavish.queuePrompt()`.
141
+ Mark only custom (non-native) clickable elements with `data-lavish-action` so Lavish does not annotate them, and use `data-lavish-question` or `queueKey` when pre-send updates for the same question should replace each other.
139
142
  The browser chrome keeps editing actions in the overflow menu (copy path, reload artifact, copy DOM snapshot, end session) and can submit queued prompts with **Send & end session**, which delivers the prompts before ending the session.
140
143
  - **Keyboard shortcuts** - In the chrome composer, Enter sends queued prompts and Shift+Enter inserts a newline.
141
144
  In the annotation card, Enter queues the annotation, Shift+Enter inserts a newline, and Ctrl+Enter (Cmd+Enter on macOS) queues it and sends all queued prompts immediately.
@@ -5,6 +5,7 @@ const sessionData = JSON.parse(sessionDataElement?.textContent || "{}");
5
5
  const key = String(sessionData.key || "");
6
6
  const filePath = String(sessionData.file || "");
7
7
  const queueStorageKey = "lavish-axi:queued:" + key;
8
+ const internalQueueKeyField = "_lavishQueueKey";
8
9
  const initialChat = Array.isArray(sessionData.initialChat) ? sessionData.initialChat : [];
9
10
 
10
11
  const frame = /** @type {HTMLIFrameElement} */ (document.getElementById("artifact"));
@@ -212,6 +213,36 @@ function removeQueuedPrompt(index, event) {
212
213
  render();
213
214
  }
214
215
 
216
+ function promptQueueKey(prompt) {
217
+ return prompt && typeof prompt[internalQueueKeyField] === "string" ? prompt[internalQueueKeyField].trim() : "";
218
+ }
219
+
220
+ function enqueuePrompt(prompt) {
221
+ if (!prompt || typeof prompt !== "object") return;
222
+
223
+ const queueKey = promptQueueKey(prompt);
224
+ if (queueKey) {
225
+ const index = queued.findIndex((item) => promptQueueKey(item) === queueKey);
226
+ if (index !== -1) {
227
+ queued[index] = prompt;
228
+ } else {
229
+ queued.push(prompt);
230
+ }
231
+ } else {
232
+ queued.push(prompt);
233
+ }
234
+
235
+ persistQueuedPrompts();
236
+ render();
237
+ }
238
+
239
+ function stripInternalPromptFields(prompt) {
240
+ if (!prompt || typeof prompt !== "object") return prompt;
241
+ const clean = { ...prompt };
242
+ delete clean[internalQueueKeyField];
243
+ return clean;
244
+ }
245
+
215
246
  function postToFrame(message) {
216
247
  if (frame.contentWindow) frame.contentWindow.postMessage(message, "*");
217
248
  }
@@ -279,7 +310,7 @@ async function submitQueuedOnce() {
279
310
  const response = await fetch("/api/" + key + "/prompts", {
280
311
  method: "POST",
281
312
  headers: { "content-type": "application/json" },
282
- body: JSON.stringify({ prompts, domSnapshot: pendingSnapshot }),
313
+ body: JSON.stringify({ prompts: prompts.map(stripInternalPromptFields), domSnapshot: pendingSnapshot }),
283
314
  });
284
315
  if (!response.ok) throw new Error("failed to submit queued prompts");
285
316
  for (const prompt of prompts) {
@@ -359,9 +390,7 @@ window.addEventListener("message", (event) => {
359
390
 
360
391
  const msg = event.data || {};
361
392
  if (msg.type === "lavish:queuePrompt") {
362
- queued.push(msg.prompt);
363
- persistQueuedPrompts();
364
- render();
393
+ enqueuePrompt(msg.prompt);
365
394
  }
366
395
  if (msg.type === "lavish:snapshot") {
367
396
  const snapshotAction = snapshotRequests.shift() || "submit";
package/dist/cli.mjs CHANGED
@@ -20,7 +20,7 @@ var DESIGN_CDN_URLS = {
20
20
  var DESIGN_CDN_SNIPPET = `<link rel="stylesheet" href="${DESIGN_CDN_URLS.daisyui}">
21
21
  <link rel="stylesheet" href="${DESIGN_CDN_URLS.daisyuiThemes}">
22
22
  <script src="${DESIGN_CDN_URLS.tailwind}"></script>`;
23
- var DESIGN_SYSTEM_HINT = "Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the current project for design conventions - look for a Tailwind or theme config, shared CSS variables or design tokens, a component library, brand assets, or existing styled pages - and match what you find so the artifact fits in; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `lavish-axi design` for a copy-pasteable CDN snippet plus component reference, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why.";
23
+ var DESIGN_SYSTEM_HINT = "Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `lavish-axi design` for a copy-pasteable CDN snippet plus component reference, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why.";
24
24
  var DAISYUI_THEMES = [
25
25
  "light",
26
26
  "dark",
@@ -61,7 +61,7 @@ var DAISYUI_THEMES = [
61
61
  function createDesignOutput() {
62
62
  return {
63
63
  design: {
64
- summary: "Use this Lavish CDN fallback only if (1) the user gave no design direction and (2) you already inspected the current project and found no design system or style conventions to match. If you have not checked the project yet, check first. Lavish does not auto-inject any design system; artifacts stay portable HTML. The strict priority order is: (1) a look or named design system the user asked for; (2) the current project's design system or style conventions - look for a Tailwind or theme config, shared CSS variables or design tokens, a component library, brand assets, or existing styled pages; (3) this Tailwind CSS browser runtime v4 + DaisyUI v5 + themes snippet - paste the CDN snippet below into your `<head>` and prefer the CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user.",
64
+ summary: "Use this Lavish CDN fallback only if (1) the user gave no design direction and (2) you already inspected the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and found no design system or style conventions to match. If you have not checked the subject project yet, check first. Lavish does not auto-inject any design system; artifacts stay portable HTML. The strict priority order is: (1) a look or named design system the user asked for; (2) the subject project's design system or style conventions - look for a Tailwind or theme config, shared CSS variables or design tokens, a component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) this Tailwind CSS browser runtime v4 + DaisyUI v5 + themes snippet - paste the CDN snippet below into your `<head>` and prefer the CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user.",
65
65
  cdn_snippet: DESIGN_CDN_SNIPPET,
66
66
  cdn_urls: DESIGN_CDN_URLS,
67
67
  versions: { tailwind: TAILWIND_BROWSER_VERSION, daisyui: DAISYUI_VERSION },
@@ -691,28 +691,31 @@ var PLAYBOOKS = [
691
691
  ],
692
692
  structure: [
693
693
  "Make each decision surface visible: what is being chosen, what the options mean, and what happens next.",
694
- "Pair controls with a readable summary of the prompt they will queue.",
695
- "Show queued or selected state clearly so the user trusts what will be sent back."
694
+ "Keep reversible selection state local in the artifact until the user explicitly submits that question.",
695
+ "Pair each question with a Submit or Queue answer control that sends exactly one prompt for the final answer.",
696
+ "Show selected state separately from queued state so the user trusts what will be sent back."
696
697
  ],
697
698
  design_rules: [
698
699
  "Native form controls - radios, checkboxes, text inputs, selects, textareas, buttons, options, labels, and contenteditable regions - are interactive automatically: clicks toggle, focus, and type instead of annotating, so they do not need data-lavish-action. Build choice and option UIs from these whenever you can.",
699
- "To turn a native control into agent feedback, wire its own change, click, or submit handler to window.lavish.queuePrompt() (for example a radio group's onchange, or a submit button that reads the selected values) - no marker required.",
700
+ "For reversible choices, do not call window.lavish.queuePrompt() from radio change handlers or option click handlers. Those handlers should only update local selected state.",
701
+ "Use a per-question form submit or explicit Queue answer button to read the current values and call window.lavish.queuePrompt() exactly once for the final answer.",
700
702
  "Put data-lavish-action only on custom (non-native) elements that should act like a feedback control - typically a styled div or span you made clickable - so Lavish does not annotate it and shows a pointer cursor instead.",
701
- "Call window.lavish.queuePrompt(prompt, options) from the control's click, change, or submit handler to queue a precise request for the agent.",
702
- "Pass options such as tag, text, selector, target, or data when they help the agent understand exactly what the user chose.",
703
- "Call window.lavish.sendQueuedPrompts() when the control should immediately send the queued feedback instead of waiting for the user to press Send to Agent.",
703
+ "Use data-lavish-question on a question wrapper or pass queueKey when multiple pre-send updates should replace the prior unsent answer for the same question.",
704
+ "Pass options such as tag, text, selector, target, data, queueKey, or element when they help the agent understand exactly what the user chose.",
705
+ "Call window.lavish.sendQueuedPrompts() only when the control should immediately send committed feedback instead of waiting for the user to press Send to Agent.",
704
706
  "Make queued prompts specific enough that the agent can act without asking a follow-up question.",
705
707
  "Keep native browser controls accessible and readable on mobile."
706
708
  ],
707
709
  pitfalls: [
710
+ "Do not queue one prompt per radio change, checkbox toggle, dropdown change, or choice-button click when the user can still change their mind.",
708
711
  "Do not create controls whose queued prompt is unclear or too vague to execute.",
709
- "Do not hide the fact that a click queues feedback for the agent.",
712
+ "Do not hide the difference between selected locally and queued for the agent.",
710
713
  "Do not require interaction for content the user only needs to read."
711
714
  ],
712
715
  lavish_notes: [
713
716
  "Lavish is strongest when the artifact becomes a focused review surface and not just a static page.",
714
- `A native choice looks like \`<label><input type="radio" name="plan" onchange="window.lavish.queuePrompt('Use the Pro plan', { tag: 'choice', text: 'Pro' })"> Pro</label>\` - it just works, no marker needed.`,
715
- `A custom (non-native) control needs the marker: \`<div role="button" tabindex="0" data-lavish-action onclick="window.lavish.queuePrompt('Choose option A', { tag: 'choice', text: 'Option A' })">Choose option A</div>\`.`,
717
+ `A native single-choice question should submit the final value: \`<form data-lavish-question="plan" onsubmit="event.preventDefault(); const choice = new FormData(event.currentTarget).get('plan'); if (choice) window.lavish.queuePrompt('Use the ' + choice + ' plan', { tag: 'choice', text: 'Plan: ' + choice, element: event.currentTarget, data: { question: 'plan', answer: choice } });"><label><input type="radio" name="plan" value="Starter"> Starter</label><label><input type="radio" name="plan" value="Pro"> Pro</label><button type="submit">Queue this answer</button></form>\`.`,
718
+ "A custom choice UI should make option buttons update local state, then use a separate Queue answer button with data-lavish-action to queue the final selected value.",
716
719
  "Use window.lavish.queuePrompt for user intent, not internal analytics or UI-only state changes.",
717
720
  "End input paths with an obvious way for the user to send feedback back to the agent."
718
721
  ]
@@ -765,7 +768,90 @@ import chokidar from "chokidar";
765
768
  import express from "express";
766
769
 
767
770
  // src/artifact-sdk.js
768
- function createArtifactSdk() {
771
+ function deriveLavishQueueKey(element, options = {}) {
772
+ function stringValue(value) {
773
+ return value === null || value === void 0 ? "" : String(value);
774
+ }
775
+ function attributeValue(el, name) {
776
+ if (!el) return "";
777
+ if (el.getAttribute) {
778
+ const value = el.getAttribute(name);
779
+ if (value !== null && value !== void 0) return value;
780
+ }
781
+ return el[name] || "";
782
+ }
783
+ function tagName(el) {
784
+ return stringValue(el?.tagName || el?.nodeName).toLowerCase();
785
+ }
786
+ function closestElementMatching(el, selector) {
787
+ return el && el.closest ? el.closest(selector) : null;
788
+ }
789
+ function elementPath(el) {
790
+ const parts = [];
791
+ let node = el;
792
+ while (node && node.nodeType === 1 && parts.length < 6) {
793
+ let part = tagName(node) || "element";
794
+ const id = stringValue(attributeValue(node, "id") || node.id).trim();
795
+ if (id) {
796
+ part += `#${id}`;
797
+ parts.unshift(part);
798
+ break;
799
+ }
800
+ const parent2 = node.parentElement;
801
+ if (parent2 && parent2.children) {
802
+ const siblings = [...parent2.children].filter((child) => tagName(child) === tagName(node));
803
+ if (siblings.length > 1) part += `:nth-of-type(${siblings.indexOf(node) + 1})`;
804
+ }
805
+ parts.unshift(part);
806
+ node = parent2;
807
+ }
808
+ return parts.join(" > ");
809
+ }
810
+ function scopeKey(el) {
811
+ const scope2 = closestElementMatching(el, "form,fieldset") || el?.parentElement || el;
812
+ const tag2 = tagName(scope2) || "scope";
813
+ const explicit = stringValue(
814
+ attributeValue(scope2, "data-lavish-question") || attributeValue(scope2, "id") || attributeValue(scope2, "name")
815
+ ).trim();
816
+ if (explicit) return `${tag2}:${explicit}`;
817
+ return elementPath(scope2) || tag2;
818
+ }
819
+ function controlIdentity(el) {
820
+ const identity = stringValue(attributeValue(el, "name") || attributeValue(el, "id") || el?.name).trim();
821
+ if (identity) return identity;
822
+ return elementPath(el);
823
+ }
824
+ function isKeyedInputType(type2) {
825
+ return !(/* @__PURE__ */ new Set(["button", "submit", "reset", "file", "image", "hidden", "radio", "checkbox"])).has(type2);
826
+ }
827
+ if (Object.hasOwn(options, "queueKey")) {
828
+ return stringValue(options.queueKey).trim();
829
+ }
830
+ const question = closestElementMatching(element, "[data-lavish-question]");
831
+ const questionKey = stringValue(attributeValue(question, "data-lavish-question")).trim();
832
+ if (questionKey) return `question:${questionKey}`;
833
+ const tag = tagName(element);
834
+ const type = stringValue(attributeValue(element, "type") || element?.type).toLowerCase();
835
+ const scope = scopeKey(element);
836
+ if (tag === "input" && type === "radio") {
837
+ const name = stringValue(attributeValue(element, "name") || element?.name).trim();
838
+ if (name) return `radio:${scope}:${name}`;
839
+ return "";
840
+ }
841
+ if (tag === "input" && type === "checkbox") {
842
+ const identity = controlIdentity(element);
843
+ const explicitValue = stringValue(element?.getAttribute ? element.getAttribute("value") : "").trim();
844
+ const option = explicitValue || stringValue(attributeValue(element, "id") || elementPath(element)).trim();
845
+ if (identity) return `checkbox:${scope}:${identity}:${option}`;
846
+ return "";
847
+ }
848
+ if (tag === "select" || tag === "textarea" || tag === "input" && isKeyedInputType(type)) {
849
+ const identity = controlIdentity(element);
850
+ if (identity) return `field:${scope}:${identity}`;
851
+ }
852
+ return "";
853
+ }
854
+ function createArtifactSdk(deriveQueueKey) {
769
855
  let annotationMode = true;
770
856
  let hovered = null;
771
857
  let selected = null;
@@ -904,10 +990,13 @@ function createArtifactSdk() {
904
990
  if (!annotationMode) closeCard();
905
991
  }
906
992
  function queuePrompt(prompt, options = {}) {
993
+ const originElement = options.element || document.activeElement || document.body;
907
994
  const item = {
908
- ...context(options.element || document.activeElement || document.body),
995
+ ...context(originElement),
909
996
  prompt: String(prompt || "")
910
997
  };
998
+ const queueKey = typeof deriveQueueKey === "function" ? deriveQueueKey(originElement, options) : "";
999
+ if (queueKey) item._lavishQueueKey = String(queueKey);
911
1000
  if (options.uid) item.uid = String(options.uid);
912
1001
  if (options.selector) item.selector = String(options.selector);
913
1002
  if (options.tag) item.tag = String(options.tag);
@@ -993,7 +1082,7 @@ function createArtifactSdk() {
993
1082
  cancelButton.onclick = closeCard;
994
1083
  sendButton.onclick = () => {
995
1084
  const prompt = textarea.value.trim();
996
- if (prompt) queuePrompt(prompt, c);
1085
+ if (prompt) queuePrompt(prompt, { ...c, queueKey: "" });
997
1086
  closeCard();
998
1087
  };
999
1088
  textarea.addEventListener("keydown", (event) => {
@@ -1819,7 +1908,8 @@ function createSdkJs(key) {
1819
1908
  return `(() => {
1820
1909
  const key=${JSON.stringify(key)};
1821
1910
  void key;
1822
- (${createArtifactSdk.toString()})();
1911
+ const deriveQueueKey=${deriveLavishQueueKey.toString()};
1912
+ (${createArtifactSdk.toString()})(deriveQueueKey);
1823
1913
  })();`;
1824
1914
  }
1825
1915
  function escapeHtml(value) {
@@ -2004,7 +2094,7 @@ function normalizePagePath(path5) {
2004
2094
  // src/cli.js
2005
2095
  var COMMANDS = /* @__PURE__ */ new Set(["open", "poll", "end", "stop", "server", "playbook", "design", "setup"]);
2006
2096
  var DESCRIPTION = "Lavish Editor helps agents turn rich HTML artifacts into collaborative human review surfaces. Whenever you are about to give user a complex response that will be easier to understand via a rich / interactive page, consider using Lavish Editor. First generate an interactive HTML artifact according to user request, then run `lavish-axi <html-file>` so the user can visually review it, annotate elements or selected text, queue prompts, and send feedback back through `lavish-axi poll`.";
2007
- var VERSION = "0.1.29";
2097
+ var VERSION = "0.1.31";
2008
2098
  async function run(argv) {
2009
2099
  await ensureStateDir();
2010
2100
  const normalizedArgv = normalizeArgv(argv);
@@ -2591,7 +2681,7 @@ Examples:
2591
2681
  `,
2592
2682
  design: `Usage: lavish-axi design
2593
2683
 
2594
- Show a copy-pasteable CDN snippet for Tailwind CSS browser runtime v4 + DaisyUI v5 + themes, plus technical reference for DaisyUI components. Lavish artifacts stay portable HTML. This CDN snippet is the design fallback, not the default: inspect the project before falling back. The strict priority order is: (1) if the user asked for a specific look or named design system, follow that; (2) otherwise, if the current project already has a design system or style conventions, match those; (3) only when both come up empty, prefer the Lavish-recommended Tailwind + DaisyUI CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user.
2684
+ Show a copy-pasteable CDN snippet for Tailwind CSS browser runtime v4 + DaisyUI v5 + themes, plus technical reference for DaisyUI components. Lavish artifacts stay portable HTML. This CDN snippet is the design fallback, not the default: inspect the subject project before falling back. The strict priority order is: (1) if the user asked for a specific look or named design system, follow that; (2) otherwise, match the design system of the project the artifact is about, not necessarily your current working directory. If the artifact previews, proposes, or mocks a specific app's UI, use that app's own design system; (3) only when both come up empty, prefer the Lavish-recommended Tailwind + DaisyUI CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user.
2595
2685
  `,
2596
2686
  setup: `Usage: lavish-axi setup hooks
2597
2687
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavish-axi",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "packageManager": "pnpm@11.1.1",
5
5
  "description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
6
6
  "type": "module",
@@ -66,5 +66,5 @@ One artifact often combines several playbooks (for example a plan that includes
66
66
  - Run `npx -y lavish-axi end <html-file>` to end a session
67
67
  - Run `npx -y lavish-axi stop` to shut down the background server (it also self-stops when idle or after the last session ends with nothing connected)
68
68
  - Run `npx -y lavish-axi playbook <playbook_id>` for focused artifact guidance. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so read every playbook relevant to the artifact, not just one, for the best quality
69
- - Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the current project for design conventions - look for a Tailwind or theme config, shared CSS variables or design tokens, a component library, brand assets, or existing styled pages - and match what you find so the artifact fits in; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `npx -y lavish-axi design` for a copy-pasteable CDN snippet plus component reference, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why.
69
+ - Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML, decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN - run `npx -y lavish-axi design` for a copy-pasteable CDN snippet plus component reference, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. When you deliver the artifact, state which of the three design sources you used and why.
70
70
  - Use lavish-axi when the user asks for a visual artifact, HTML explainer, interactive prototype, review surface, product or technical plan, comparison, report, or browser-based feedback loop