storybook-addon-playwright 5.6.1 → 5.8.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.
- package/README.md +40 -0
- package/decorator.d.ts +1 -0
- package/decorator.js +1 -0
- package/dist/ai/index.d.mts +22 -8
- package/dist/ai/index.d.ts +22 -8
- package/dist/ai/index.js +20 -2
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/index.mjs +20 -2
- package/dist/ai/index.mjs.map +1 -1
- package/dist/api/server/routes.d.mts +3 -3
- package/dist/api/server/routes.d.ts +3 -3
- package/dist/api/server/routes.js +7 -7
- package/dist/api/server/routes.js.map +1 -1
- package/dist/api/server/routes.mjs +7 -7
- package/dist/api/server/routes.mjs.map +1 -1
- package/dist/decorator/index.d.mts +15 -0
- package/dist/decorator/index.d.ts +15 -0
- package/dist/decorator/index.js +2 -0
- package/dist/decorator/index.js.map +1 -0
- package/dist/decorator/index.mjs +2 -0
- package/dist/decorator/index.mjs.map +1 -0
- package/dist/get-screenshots.d.mts +2 -2
- package/dist/get-screenshots.d.ts +2 -2
- package/dist/get-screenshots.js +2 -2
- package/dist/get-screenshots.js.map +1 -1
- package/dist/get-screenshots.mjs +2 -2
- package/dist/get-screenshots.mjs.map +1 -1
- package/dist/{image-diff-BDN_FVyD.d.mts → image-diff-DNP2iheS.d.mts} +1 -1
- package/dist/{image-diff-BKKtLPxv.d.ts → image-diff-Dgb4eq6u.d.ts} +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/register.js +9 -9
- package/dist/register.js.map +1 -1
- package/dist/register.mjs +9 -9
- package/dist/register.mjs.map +1 -1
- package/dist/{request-CGNOwtQo.d.mts → request-BduSDqMg.d.mts} +1 -1
- package/dist/{request-DVZ6cFCg.d.ts → request-CWwxYmNe.d.ts} +1 -1
- package/dist/run-image-diff.d.mts +3 -3
- package/dist/run-image-diff.d.ts +3 -3
- package/dist/run-image-diff.js +3 -3
- package/dist/run-image-diff.js.map +1 -1
- package/dist/run-image-diff.mjs +3 -3
- package/dist/run-image-diff.mjs.map +1 -1
- package/dist/{story-DSpVrFjG.d.mts → story-Cpro8pd5.d.mts} +12 -0
- package/dist/{story-DSpVrFjG.d.ts → story-Cpro8pd5.d.ts} +12 -0
- package/dist/to-match-screenshots.js +3 -3
- package/dist/to-match-screenshots.js.map +1 -1
- package/dist/to-match-screenshots.mjs +3 -3
- package/dist/to-match-screenshots.mjs.map +1 -1
- package/dist/trpc/router.d.mts +15 -3
- package/dist/trpc/router.d.ts +15 -3
- package/dist/trpc/router.js +7 -7
- package/dist/trpc/router.js.map +1 -1
- package/dist/trpc/router.mjs +7 -7
- package/dist/trpc/router.mjs.map +1 -1
- package/package.json +34 -1
package/README.md
CHANGED
|
@@ -81,6 +81,46 @@ module.exports = function (router) {
|
|
|
81
81
|
};
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
### Story Readiness Before Screenshot
|
|
85
|
+
|
|
86
|
+
The addon will wait briefly for story readiness before taking screenshots.
|
|
87
|
+
|
|
88
|
+
Built-in readiness checks:
|
|
89
|
+
|
|
90
|
+
- If a marker is present at navigation time (`body[data-playwright-mounted]`), marker mode is enabled and the addon waits for mounted state.
|
|
91
|
+
- In marker mode, readiness is:
|
|
92
|
+
- `body[data-playwright-mounted="true"]`.
|
|
93
|
+
- Otherwise, it checks Storybook preview render phases when available.
|
|
94
|
+
- If Storybook internals are not available, it falls back to mounted content under `#storybook-root` or `#storybook-docs`.
|
|
95
|
+
|
|
96
|
+
If you do not use a mount marker decorator, no marker wait is applied.
|
|
97
|
+
|
|
98
|
+
For stricter post-mount readiness in React, use the built-in decorator helper from `storybook-addon-playwright/decorator`.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import type { Preview } from '@storybook/react';
|
|
102
|
+
import { withReactMounted } from 'storybook-addon-playwright/decorator';
|
|
103
|
+
|
|
104
|
+
const preview: Preview = {
|
|
105
|
+
decorators: [withReactMounted()],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default preview;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This avoids JSX component typing issues that can happen in some projects when using `ReactMountedDecorator` directly in `preview.tsx`.
|
|
112
|
+
|
|
113
|
+
What the React decorator does:
|
|
114
|
+
|
|
115
|
+
- Sets `data-playwright-mounted="pending"` immediately so marker mode is enabled.
|
|
116
|
+
- Updates the value to `"true"` after React mounts.
|
|
117
|
+
- Removes the attribute on unmount.
|
|
118
|
+
|
|
119
|
+
For non-React frameworks (Vue, Svelte, Angular, etc.), create a small framework-specific decorator/composable that follows the same marker contract:
|
|
120
|
+
|
|
121
|
+
- Set `data-playwright-mounted="pending"` before mount.
|
|
122
|
+
- Set `data-playwright-mounted="true"` when mounted.
|
|
123
|
+
|
|
84
124
|
## setConfig Options
|
|
85
125
|
|
|
86
126
|
For a full list of available options with detailed descriptions, see the [`Config` interface in `src/typings/config.ts`](src/typings/config.ts).
|
package/decorator.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/decorator/index';
|
package/decorator.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/decorator/index');
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { G as GenerateScreenshotTitleInput } from '../story-
|
|
1
|
+
import { G as GenerateScreenshotTitleInput } from '../story-Cpro8pd5.mjs';
|
|
2
2
|
import 'playwright';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
interface CreateScreenshotTitlePromptOptions {
|
|
6
6
|
/**
|
|
7
7
|
* Maximum number of characters allowed in the returned title.
|
|
8
|
+
* Note: the generated title is also truncated in code as a safety net,
|
|
9
|
+
* since LLMs count tokens, not characters.
|
|
8
10
|
*
|
|
9
11
|
* @default 80
|
|
10
12
|
*/
|
|
11
13
|
maxTitleLength?: number;
|
|
12
14
|
/**
|
|
13
|
-
* Fallback title the model should use when input is too sparse
|
|
15
|
+
* Fallback title the model should use when input is too sparse to produce
|
|
16
|
+
* a meaningful title (no changedArgs, no actions, and story.name is absent
|
|
17
|
+
* or a single generic word).
|
|
14
18
|
*
|
|
15
19
|
* @default 'Should render correctly.'
|
|
16
20
|
*/
|
|
@@ -28,19 +32,29 @@ interface CreateScreenshotTitlePromptOptions {
|
|
|
28
32
|
*/
|
|
29
33
|
includeStoryId?: boolean;
|
|
30
34
|
/**
|
|
31
|
-
* Additional instructions for the output format, appended to the prompt's
|
|
35
|
+
* Additional instructions for the output format, appended to the prompt's
|
|
36
|
+
* output contract section.
|
|
32
37
|
*
|
|
33
|
-
* @default 'Return only the generated title as
|
|
38
|
+
* @default 'Return only the generated title as a plain text string, without
|
|
39
|
+
* any additional text, reasoning, or formatting.'
|
|
34
40
|
*/
|
|
35
41
|
outputPrompt?: string | string[];
|
|
36
42
|
}
|
|
37
43
|
type PartialContext = Partial<GenerateScreenshotTitleInput> & Pick<GenerateScreenshotTitleInput, 'story'>;
|
|
38
44
|
/**
|
|
39
|
-
*
|
|
45
|
+
* Truncates a generated title to the configured maximum length, appending
|
|
46
|
+
* an ellipsis if the string was cut. This acts as a safety net because LLMs
|
|
47
|
+
* count tokens rather than characters and may slightly exceed the limit.
|
|
48
|
+
*/
|
|
49
|
+
declare function truncateTitle(title: string, maxLength: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a prompt for LLMs to generate a screenshot title from Storybook
|
|
52
|
+
* Playwright data.
|
|
40
53
|
*
|
|
41
|
-
* The prompt
|
|
42
|
-
*
|
|
54
|
+
* The prompt instructs the model to return the title as a plain text string
|
|
55
|
+
* with no additional text or formatting. Use {@link truncateTitle} as a
|
|
56
|
+
* client-side safety net in case the model slightly exceeds maxTitleLength.
|
|
43
57
|
*/
|
|
44
58
|
declare function createScreenshotTitlePrompt(context: PartialContext, options?: CreateScreenshotTitlePromptOptions): string;
|
|
45
59
|
|
|
46
|
-
export { type CreateScreenshotTitlePromptOptions, createScreenshotTitlePrompt };
|
|
60
|
+
export { type CreateScreenshotTitlePromptOptions, createScreenshotTitlePrompt, truncateTitle };
|
package/dist/ai/index.d.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { G as GenerateScreenshotTitleInput } from '../story-
|
|
1
|
+
import { G as GenerateScreenshotTitleInput } from '../story-Cpro8pd5.js';
|
|
2
2
|
import 'playwright';
|
|
3
3
|
import 'zod';
|
|
4
4
|
|
|
5
5
|
interface CreateScreenshotTitlePromptOptions {
|
|
6
6
|
/**
|
|
7
7
|
* Maximum number of characters allowed in the returned title.
|
|
8
|
+
* Note: the generated title is also truncated in code as a safety net,
|
|
9
|
+
* since LLMs count tokens, not characters.
|
|
8
10
|
*
|
|
9
11
|
* @default 80
|
|
10
12
|
*/
|
|
11
13
|
maxTitleLength?: number;
|
|
12
14
|
/**
|
|
13
|
-
* Fallback title the model should use when input is too sparse
|
|
15
|
+
* Fallback title the model should use when input is too sparse to produce
|
|
16
|
+
* a meaningful title (no changedArgs, no actions, and story.name is absent
|
|
17
|
+
* or a single generic word).
|
|
14
18
|
*
|
|
15
19
|
* @default 'Should render correctly.'
|
|
16
20
|
*/
|
|
@@ -28,19 +32,29 @@ interface CreateScreenshotTitlePromptOptions {
|
|
|
28
32
|
*/
|
|
29
33
|
includeStoryId?: boolean;
|
|
30
34
|
/**
|
|
31
|
-
* Additional instructions for the output format, appended to the prompt's
|
|
35
|
+
* Additional instructions for the output format, appended to the prompt's
|
|
36
|
+
* output contract section.
|
|
32
37
|
*
|
|
33
|
-
* @default 'Return only the generated title as
|
|
38
|
+
* @default 'Return only the generated title as a plain text string, without
|
|
39
|
+
* any additional text, reasoning, or formatting.'
|
|
34
40
|
*/
|
|
35
41
|
outputPrompt?: string | string[];
|
|
36
42
|
}
|
|
37
43
|
type PartialContext = Partial<GenerateScreenshotTitleInput> & Pick<GenerateScreenshotTitleInput, 'story'>;
|
|
38
44
|
/**
|
|
39
|
-
*
|
|
45
|
+
* Truncates a generated title to the configured maximum length, appending
|
|
46
|
+
* an ellipsis if the string was cut. This acts as a safety net because LLMs
|
|
47
|
+
* count tokens rather than characters and may slightly exceed the limit.
|
|
48
|
+
*/
|
|
49
|
+
declare function truncateTitle(title: string, maxLength: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a prompt for LLMs to generate a screenshot title from Storybook
|
|
52
|
+
* Playwright data.
|
|
40
53
|
*
|
|
41
|
-
* The prompt
|
|
42
|
-
*
|
|
54
|
+
* The prompt instructs the model to return the title as a plain text string
|
|
55
|
+
* with no additional text or formatting. Use {@link truncateTitle} as a
|
|
56
|
+
* client-side safety net in case the model slightly exceeds maxTitleLength.
|
|
43
57
|
*/
|
|
44
58
|
declare function createScreenshotTitlePrompt(context: PartialContext, options?: CreateScreenshotTitlePromptOptions): string;
|
|
45
59
|
|
|
46
|
-
export { type CreateScreenshotTitlePromptOptions, createScreenshotTitlePrompt };
|
|
60
|
+
export { type CreateScreenshotTitlePromptOptions, createScreenshotTitlePrompt, truncateTitle };
|
package/dist/ai/index.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
'use strict';var
|
|
2
|
-
|
|
1
|
+
'use strict';var i={fallbackTitle:"Should render correctly.",includeBrowserType:true,includeStoryId:false,maxTitleLength:80,outputPrompt:"Return only the generated title as a plain text string, without any additional text, reasoning, or formatting."},u=2,h=10,d={changedArgs:"The arguments explicitly changed from their defaults for this story. This is the single highest-priority field \u2014 always prefer these details when building the title.",initialArgs:"The default arguments for the story. Use these only when changedArgs is empty or absent, to understand the base state of the component.",argTypes:"Metadata about the story arguments. Consult this only when the meaning of a changedArgs value is unclear without extra context.",name:'The display name of the story (e.g. "Primary Button"). Use as the component anchor when changedArgs and actions are both absent.',title:'The hierarchical Storybook path in the format "Group/Component/Variant" (e.g. "Forms/Button/Disabled"). The rightmost segment is the most specific and useful; earlier segments provide grouping context only.',parameters:"Storybook-level metadata. Only relevant when it directly affects what the screenshot shows \u2014 for example a viewport override, a forced background colour, or a locale setting. Ignore generic infrastructure keys.",filePath:"The source file path of the story. Use only as a last resort to infer the component or feature name when no other field provides it.",id:"The unique story identifier. Include in the title only when no other field provides enough uniqueness and includeStoryId is permitted.",actions:'An ordered sequence of Playwright page interactions \u2014 called an action set \u2014 executed on the page immediately before the screenshot was taken. Each entry is a discrete action such as mouseMove, mouseDown, click, press, scroll, dragDrop, hover, or a custom page extension. Together they define the exact UI state captured (e.g. "after hovering a tooltip" or "after dragging the slider to max"). When present, describe the resulting UI state rather than listing the individual action names. Treat this as a high-priority field alongside changedArgs.'},p={type:"The Playwright browser engine used: chromium, firefox, or webkit. Include in the title only when cross-browser differences are the point of the screenshot (e.g. a webkit-specific rendering bug).",options:"Browser or device options such as viewport dimensions and device profile. Include when they meaningfully change the visual output \u2014 e.g. a mobile viewport that triggers a responsive layout shift."},g="Options that control how the screenshot is captured and may change its meaning: fullPage (captures beyond the visible viewport), clip (captures a specific region), animations (disabled/enabled), mask (hides elements), omitBackground (transparent background). Mention these in the title only when they are the distinguishing factor of this screenshot.",l={story:d,browser:p,screenshotOptions:g},f=`Examples (input \u2192 output):
|
|
2
|
+
|
|
3
|
+
Input: {"story":{"name":"Button","changedArgs":{"variant":"destructive","disabled":true}}}
|
|
4
|
+
Output: Button - Destructive and Disabled
|
|
5
|
+
|
|
6
|
+
Input: {"story":{"name":"Tooltip","changedArgs":{}},"actions":[{"type":"mouseMove","selector":"#trigger"}]}
|
|
7
|
+
Output: Tooltip visible on trigger hover
|
|
8
|
+
|
|
9
|
+
Input: {"story":{"name":"Slider","changedArgs":{"min":0,"max":100}},"actions":[{"type":"dragDrop","selector":"#thumb","position":{"x":300,"y":0}}]}
|
|
10
|
+
Output: Slider thumb dragged to far right
|
|
11
|
+
|
|
12
|
+
Input: {"story":{"name":"Modal","changedArgs":{"open":true}},"browser":{"type":"firefox"}}
|
|
13
|
+
Output: Modal open (Firefox)
|
|
14
|
+
|
|
15
|
+
Input: {"story":{"name":"Card"},"browser":{"type":"webkit","options":{"viewport":{"width":375,"height":812}}}}
|
|
16
|
+
Output: Card on mobile viewport (WebKit)
|
|
17
|
+
|
|
18
|
+
Input: {"story":{"name":"A"}}
|
|
19
|
+
Output: Should render correctly.`;function c(e,t){return e[t]!==void 0}function m(e={}){let t=Number.isFinite(e.maxTitleLength)?Math.max(h,Math.floor(e.maxTitleLength)):i.maxTitleLength,r=(Array.isArray(e.outputPrompt)?e.outputPrompt:typeof e.outputPrompt=="string"?[e.outputPrompt]:[i.outputPrompt]).map(n=>n.trim()).map(n=>"- "+n);return {fallbackTitle:typeof e.fallbackTitle=="string"&&e.fallbackTitle.trim()?e.fallbackTitle.trim():i.fallbackTitle,includeBrowserType:e.includeBrowserType??i.includeBrowserType,includeStoryId:e.includeStoryId??i.includeStoryId,maxTitleLength:t,outputPrompt:r}}function y(e,t){return e.length<=t?e:e.slice(0,t-1).trimEnd()+"\u2026"}function b(e,t){let r=m(t),n=JSON.stringify(e,null,u),o=["Field guide:"];return c(e,"story")&&e.story&&o.push(...Object.entries(l.story).map(([s,a])=>`- story.${s}: ${a}`)),c(e,"browser")&&e.browser&&o.push(...Object.entries(l.browser).map(([s,a])=>`- browser.${s}: ${a}`)),c(e,"screenshotOptions")&&e.screenshotOptions&&o.push(`- screenshotOptions: ${l.screenshotOptions}`),o.push(""),["You are a screenshot title generator for Storybook Playwright.","Your goal is to read the input JSON and produce one clear screenshot title.","","Follow this process INTERNALLY before writing the final answer.","Do NOT output your reasoning, intermediate steps, or any text other than the final title:","1) Parse INPUT_JSON.","2) Identify the highest-priority signals using the field guide and priority chain below.","3) Build a short, human-friendly title following the title rules.","4) Output ONLY the final title.","",...o,"Priority chain (use the first level that provides meaningful content):"," 1. actions + changedArgs (what the user did AND what changed)"," 2. actions alone (describe the resulting UI state)"," 3. changedArgs alone (describe the changed configuration)"," 4. story.name (use as a component anchor)"," 5. story.title rightmost segment"," 6. filePath component/feature name",` 7. Fallback title: ${JSON.stringify(r.fallbackTitle)}`," (use ONLY when no changedArgs, no actions, and story.name is absent or a single generic word)","","Title rules:",`- Maximum length: ${r.maxTitleLength} characters (hard limit \u2014 the output will be truncated in code if exceeded).`,`- ${r.includeBrowserType?"Include browser type only when it adds useful context (e.g. a browser-specific rendering difference).":"Do not include browser type in the title."}`,`- ${r.includeStoryId?"You may include story.id when needed for uniqueness.":"Do not include story.id unless no other unique context exists."}`,"- Prefer changedArgs and actions details when present.","- Avoid filler words and generic phrases.","- Title must be plain text: no markdown, no surrounding quotes.","- Use sentence case with a capital first letter. End with a period only if the title is a full sentence.","","Title style contract:",'- Prefer UI state over component names (e.g. "Dropdown open" not "Dropdown component").','- Prefer observable behavior over configuration (e.g. "Error message visible" not "hasError=true").','- Prefer post-action result when actions are present (e.g. "Tooltip shown after hover" not "mouseMove on tooltip trigger").','- Avoid generic terms: "renders correctly", "display", "view", "screenshot", "story", "component".',"","Examples of BAD titles:",'- "Component with some changes" \u2190 too generic, no specifics','- "Story screenshot" \u2190 pure filler','- "mouseMove, click" \u2190 lists actions instead of describing result','- "Button renders correctly" \u2190 generic fallback used when real content exists',"",f,"","Output contract (strict):","- Output ONLY the final title.","- Do NOT output your internal thinking, reasoning steps, or any introductory text.","- Do NOT wrap the title in quotes, markdown, or JSON.",...r.outputPrompt,`- If context is insufficient (no changedArgs, no actions, story.name absent or generic), use fallback: ${JSON.stringify(r.fallbackTitle)}`,"","INPUT_JSON:",n].join(`
|
|
20
|
+
`)}exports.createScreenshotTitlePrompt=b;exports.truncateTitle=y;//# sourceMappingURL=index.js.map
|
|
3
21
|
//# sourceMappingURL=index.js.map
|
package/dist/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ai/generate-screenshot-title-prompt.ts"],"names":["DEFAULT_OPTIONS","INPUT_JSON_INDENT","MIN_TITLE_LENGTH","storyGuide","browserGuide","screenshotOptionsGuide","fieldGuide","hasOwnDefinedValue","value","key","normalizeOptions","options","maxTitleLength","outputPrompt","instruction","x","createScreenshotTitlePrompt","context","resolved","inputJson","fieldGuideSections","description"],"mappings":"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","file":"index.js","sourcesContent":["import type { GenerateScreenshotTitleInput } from '../schema';\n\nexport interface CreateScreenshotTitlePromptOptions {\n /**\n * Maximum number of characters allowed in the returned title.\n *\n * @default 80\n */\n maxTitleLength?: number;\n\n /**\n * Fallback title the model should use when input is too sparse.\n *\n * @default 'Should render correctly.'\n */\n fallbackTitle?: string;\n\n /**\n * Whether the generated title should include the browser type when useful.\n *\n * @default true\n */\n includeBrowserType?: boolean;\n\n /**\n * Whether the generated title can include the story id.\n *\n * @default false\n */\n includeStoryId?: boolean;\n\n /**\n * Additional instructions for the output format, appended to the prompt's output contract section.\n *\n * @default 'Return only the generated title as string, without any additional text or formatting.'\n */\n outputPrompt?: string | string[];\n}\n\ntype PartialContext = Partial<GenerateScreenshotTitleInput> &\n Pick<GenerateScreenshotTitleInput, 'story'>;\n\ntype ScreenshotTitleFieldGuide = {\n story: Record<keyof GenerateScreenshotTitleInput['story'], string>;\n browser: Record<keyof GenerateScreenshotTitleInput['browser'], string>;\n screenshotOptions: string;\n};\n\nconst DEFAULT_OPTIONS: Required<CreateScreenshotTitlePromptOptions> = {\n fallbackTitle: 'Should render correctly.',\n includeBrowserType: true,\n includeStoryId: false,\n maxTitleLength: 80,\n outputPrompt:\n 'Return only the generated title as string, without any additional text or formatting.',\n};\n\nconst INPUT_JSON_INDENT = 2;\nconst MIN_TITLE_LENGTH = 10;\n\nconst storyGuide = {\n changedArgs:\n 'The specific arguments that have been changed from their default values for this story; these are high-priority signals for understanding what is unique about this screenshot.',\n initialArgs:\n 'The default arguments for the story, which can provide context but are less important than changedArgs for title generation.',\n argTypes:\n 'Metadata about the story arguments, which should only be used if it clarifies the meaning of changedArgs.',\n name: 'The display name of the story, often concise and descriptive.',\n title:\n 'The hierarchical path of the story in Storybook, which may include groupings and can provide context about the component and its variations.',\n parameters:\n 'Additional metadata at the Storybook level, which should only be considered if it impacts the meaning of the screenshot.',\n filePath:\n 'The file path to the source story, which may provide context about the component or feature being tested.',\n id: 'The unique identifier for the Storybook story, which should only be included in the title if no other unique context exists.',\n} satisfies ScreenshotTitleFieldGuide['story'];\n\nconst browserGuide = {\n type: 'The type of browser engine used (e.g., chromium, firefox, webkit), which should be included in the title only when it adds useful context.',\n options:\n 'Options related to the browser or device, such as viewport size and device profile, which may influence the meaning of the screenshot.',\n} satisfies ScreenshotTitleFieldGuide['browser'];\n\nconst screenshotOptionsGuide =\n 'Behavior options for taking the screenshot that may change its meaning or context.';\n\nconst fieldGuide: ScreenshotTitleFieldGuide = {\n story: storyGuide,\n browser: browserGuide,\n screenshotOptions: screenshotOptionsGuide,\n};\n\nfunction hasOwnDefinedValue<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): boolean {\n return value[key] !== undefined;\n}\n\nfunction normalizeOptions(\n options: CreateScreenshotTitlePromptOptions = {},\n): Required<CreateScreenshotTitlePromptOptions> {\n const maxTitleLength = Number.isFinite(options.maxTitleLength)\n ? Math.max(MIN_TITLE_LENGTH, Math.floor(options.maxTitleLength as number))\n : DEFAULT_OPTIONS.maxTitleLength;\n\n const outputPrompt = (\n Array.isArray(options.outputPrompt)\n ? options.outputPrompt\n : typeof options.outputPrompt === 'string'\n ? [options.outputPrompt]\n : [DEFAULT_OPTIONS.outputPrompt]\n )\n .map((instruction) => (instruction as string).trim())\n .map((x) => '- ' + x);\n\n return {\n fallbackTitle:\n typeof options.fallbackTitle === 'string' && options.fallbackTitle.trim()\n ? options.fallbackTitle.trim()\n : DEFAULT_OPTIONS.fallbackTitle,\n includeBrowserType: options.includeBrowserType ?? DEFAULT_OPTIONS.includeBrowserType,\n includeStoryId: options.includeStoryId ?? DEFAULT_OPTIONS.includeStoryId,\n maxTitleLength,\n outputPrompt: outputPrompt,\n };\n}\n\n/**\n * Creates a strict prompt for LLMs to generate a screenshot title from Storybook data.\n *\n * The prompt enforces JSON-only output in the form:\n * `{\"title\":\"...\"}`.\n */\nexport function createScreenshotTitlePrompt(\n context: PartialContext,\n options?: CreateScreenshotTitlePromptOptions,\n): string {\n const resolved = normalizeOptions(options);\n const inputJson = JSON.stringify(context, null, INPUT_JSON_INDENT);\n const fieldGuideSections: string[] = ['Field guide:'];\n\n if (hasOwnDefinedValue(context, 'story') && context.story) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.story).map(\n ([key, description]) => `- story.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'browser') && context.browser) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.browser).map(\n ([key, description]) => `- browser.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'screenshotOptions') && context.screenshotOptions) {\n fieldGuideSections.push(`- screenshotOptions: ${fieldGuide.screenshotOptions}`);\n }\n\n fieldGuideSections.push('');\n\n return [\n 'You are a screenshot title generator for Storybook Playwright.',\n 'Your goal is to read the input JSON and produce one clear screenshot title.',\n '',\n 'Follow this process internally before writing the final answer:',\n '1) Parse INPUT_JSON as JSON.',\n '2) Review every top-level and nested field and understand what each field says about the screenshot context.',\n '3) Decide the most important details for naming this screenshot.',\n '4) Build a short, human-friendly title.',\n '',\n ...fieldGuideSections,\n 'Title rules:',\n `- Maximum length: ${resolved.maxTitleLength} characters.`,\n `- ${resolved.includeBrowserType ? 'Include browser type only when it adds useful context.' : 'Do not include browser type in the title.'}`,\n `- ${resolved.includeStoryId ? 'You may include story.id when needed for uniqueness.' : 'Do not include story.id unless no other unique context exists.'}`,\n '- Prefer changedArgs details when present.',\n '- Avoid filler words and generic phrases.',\n '- Title must be plain text (no markdown, no quotes around the full title).',\n '',\n 'Output contract (strict):',\n ...resolved.outputPrompt,\n `- If context is insufficient, use fallback title: ${JSON.stringify(resolved.fallbackTitle)}`,\n '',\n 'INPUT_JSON:',\n inputJson,\n ].join('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ai/generate-screenshot-title-prompt.ts"],"names":["DEFAULT_OPTIONS","INPUT_JSON_INDENT","MIN_TITLE_LENGTH","storyGuide","browserGuide","screenshotOptionsGuide","fieldGuide","FEW_SHOT_EXAMPLES","hasOwnDefinedValue","value","key","normalizeOptions","options","maxTitleLength","outputPrompt","instruction","x","truncateTitle","title","maxLength","createScreenshotTitlePrompt","context","resolved","inputJson","fieldGuideSections","description"],"mappings":"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","file":"index.js","sourcesContent":["import type { GenerateScreenshotTitleInput } from '../schema';\n\nexport interface CreateScreenshotTitlePromptOptions {\n /**\n * Maximum number of characters allowed in the returned title.\n * Note: the generated title is also truncated in code as a safety net,\n * since LLMs count tokens, not characters.\n *\n * @default 80\n */\n maxTitleLength?: number;\n\n /**\n * Fallback title the model should use when input is too sparse to produce\n * a meaningful title (no changedArgs, no actions, and story.name is absent\n * or a single generic word).\n *\n * @default 'Should render correctly.'\n */\n fallbackTitle?: string;\n\n /**\n * Whether the generated title should include the browser type when useful.\n *\n * @default true\n */\n includeBrowserType?: boolean;\n\n /**\n * Whether the generated title can include the story id.\n *\n * @default false\n */\n includeStoryId?: boolean;\n\n /**\n * Additional instructions for the output format, appended to the prompt's\n * output contract section.\n *\n * @default 'Return only the generated title as a plain text string, without\n * any additional text, reasoning, or formatting.'\n */\n outputPrompt?: string | string[];\n}\n\ntype PartialContext = Partial<GenerateScreenshotTitleInput> &\n Pick<GenerateScreenshotTitleInput, 'story'>;\n\ntype ScreenshotTitleFieldGuide = {\n story: Record<keyof GenerateScreenshotTitleInput['story'], string>;\n browser: Record<keyof GenerateScreenshotTitleInput['browser'], string>;\n screenshotOptions: string;\n};\n\nconst DEFAULT_OPTIONS: Required<CreateScreenshotTitlePromptOptions> = {\n fallbackTitle: 'Should render correctly.',\n includeBrowserType: true,\n includeStoryId: false,\n maxTitleLength: 80,\n outputPrompt:\n 'Return only the generated title as a plain text string, without any additional text, reasoning, or formatting.',\n};\n\nconst INPUT_JSON_INDENT = 2;\nconst MIN_TITLE_LENGTH = 10;\n\nconst storyGuide = {\n changedArgs:\n 'The arguments explicitly changed from their defaults for this story. ' +\n 'This is the single highest-priority field — always prefer these details ' +\n 'when building the title.',\n\n initialArgs:\n 'The default arguments for the story. Use these only when changedArgs is ' +\n 'empty or absent, to understand the base state of the component.',\n\n argTypes:\n 'Metadata about the story arguments. Consult this only when the meaning ' +\n 'of a changedArgs value is unclear without extra context.',\n\n name:\n 'The display name of the story (e.g. \"Primary Button\"). Use as the ' +\n 'component anchor when changedArgs and actions are both absent.',\n\n title:\n 'The hierarchical Storybook path in the format \"Group/Component/Variant\" ' +\n '(e.g. \"Forms/Button/Disabled\"). The rightmost segment is the most ' +\n 'specific and useful; earlier segments provide grouping context only.',\n\n parameters:\n 'Storybook-level metadata. Only relevant when it directly affects what ' +\n 'the screenshot shows — for example a viewport override, a forced ' +\n 'background colour, or a locale setting. Ignore generic infrastructure keys.',\n\n filePath:\n 'The source file path of the story. Use only as a last resort to infer ' +\n 'the component or feature name when no other field provides it.',\n\n id:\n 'The unique story identifier. Include in the title only when no other ' +\n 'field provides enough uniqueness and includeStoryId is permitted.',\n\n actions:\n 'An ordered sequence of Playwright page interactions — called an action ' +\n 'set — executed on the page immediately before the screenshot was taken. ' +\n 'Each entry is a discrete action such as mouseMove, mouseDown, click, ' +\n 'press, scroll, dragDrop, hover, or a custom page extension. Together ' +\n 'they define the exact UI state captured (e.g. \"after hovering a tooltip\" ' +\n 'or \"after dragging the slider to max\"). When present, describe the ' +\n 'resulting UI state rather than listing the individual action names. ' +\n 'Treat this as a high-priority field alongside changedArgs.',\n} satisfies ScreenshotTitleFieldGuide['story'];\n\nconst browserGuide = {\n type:\n 'The Playwright browser engine used: chromium, firefox, or webkit. ' +\n 'Include in the title only when cross-browser differences are the point ' +\n 'of the screenshot (e.g. a webkit-specific rendering bug).',\n\n options:\n 'Browser or device options such as viewport dimensions and device ' +\n 'profile. Include when they meaningfully change the visual output — ' +\n 'e.g. a mobile viewport that triggers a responsive layout shift.',\n} satisfies ScreenshotTitleFieldGuide['browser'];\n\nconst screenshotOptionsGuide =\n 'Options that control how the screenshot is captured and may change its ' +\n 'meaning: fullPage (captures beyond the visible viewport), clip (captures ' +\n 'a specific region), animations (disabled/enabled), mask (hides elements), ' +\n 'omitBackground (transparent background). Mention these in the title only ' +\n 'when they are the distinguishing factor of this screenshot.';\n\nconst fieldGuide: ScreenshotTitleFieldGuide = {\n story: storyGuide,\n browser: browserGuide,\n screenshotOptions: screenshotOptionsGuide,\n};\n\n// ---------------------------------------------------------------------------\n// Few-shot examples embedded in the prompt\n// ---------------------------------------------------------------------------\nconst FEW_SHOT_EXAMPLES = `\\\nExamples (input → output):\n\nInput: {\"story\":{\"name\":\"Button\",\"changedArgs\":{\"variant\":\"destructive\",\"disabled\":true}}}\nOutput: Button - Destructive and Disabled\n\nInput: {\"story\":{\"name\":\"Tooltip\",\"changedArgs\":{}},\"actions\":[{\"type\":\"mouseMove\",\"selector\":\"#trigger\"}]}\nOutput: Tooltip visible on trigger hover\n\nInput: {\"story\":{\"name\":\"Slider\",\"changedArgs\":{\"min\":0,\"max\":100}},\"actions\":[{\"type\":\"dragDrop\",\"selector\":\"#thumb\",\"position\":{\"x\":300,\"y\":0}}]}\nOutput: Slider thumb dragged to far right\n\nInput: {\"story\":{\"name\":\"Modal\",\"changedArgs\":{\"open\":true}},\"browser\":{\"type\":\"firefox\"}}\nOutput: Modal open (Firefox)\n\nInput: {\"story\":{\"name\":\"Card\"},\"browser\":{\"type\":\"webkit\",\"options\":{\"viewport\":{\"width\":375,\"height\":812}}}}\nOutput: Card on mobile viewport (WebKit)\n\nInput: {\"story\":{\"name\":\"A\"}}\nOutput: Should render correctly.`;\n\nfunction hasOwnDefinedValue<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): boolean {\n return value[key] !== undefined;\n}\n\nfunction normalizeOptions(\n options: CreateScreenshotTitlePromptOptions = {},\n): Required<CreateScreenshotTitlePromptOptions> {\n const maxTitleLength = Number.isFinite(options.maxTitleLength)\n ? Math.max(MIN_TITLE_LENGTH, Math.floor(options.maxTitleLength as number))\n : DEFAULT_OPTIONS.maxTitleLength;\n\n const outputPrompt = (\n Array.isArray(options.outputPrompt)\n ? options.outputPrompt\n : typeof options.outputPrompt === 'string'\n ? [options.outputPrompt]\n : [DEFAULT_OPTIONS.outputPrompt]\n )\n .map((instruction) => (instruction as string).trim())\n .map((x) => '- ' + x);\n\n return {\n fallbackTitle:\n typeof options.fallbackTitle === 'string' && options.fallbackTitle.trim()\n ? options.fallbackTitle.trim()\n : DEFAULT_OPTIONS.fallbackTitle,\n includeBrowserType: options.includeBrowserType ?? DEFAULT_OPTIONS.includeBrowserType,\n includeStoryId: options.includeStoryId ?? DEFAULT_OPTIONS.includeStoryId,\n maxTitleLength,\n outputPrompt,\n };\n}\n\n/**\n * Truncates a generated title to the configured maximum length, appending\n * an ellipsis if the string was cut. This acts as a safety net because LLMs\n * count tokens rather than characters and may slightly exceed the limit.\n */\nexport function truncateTitle(title: string, maxLength: number): string {\n if (title.length <= maxLength) return title;\n return title.slice(0, maxLength - 1).trimEnd() + '…';\n}\n\n/**\n * Creates a prompt for LLMs to generate a screenshot title from Storybook\n * Playwright data.\n *\n * The prompt instructs the model to return the title as a plain text string\n * with no additional text or formatting. Use {@link truncateTitle} as a\n * client-side safety net in case the model slightly exceeds maxTitleLength.\n */\nexport function createScreenshotTitlePrompt(\n context: PartialContext,\n options?: CreateScreenshotTitlePromptOptions,\n): string {\n const resolved = normalizeOptions(options);\n const inputJson = JSON.stringify(context, null, INPUT_JSON_INDENT);\n const fieldGuideSections: string[] = ['Field guide:'];\n\n if (hasOwnDefinedValue(context, 'story') && context.story) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.story).map(\n ([key, description]) => `- story.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'browser') && context.browser) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.browser).map(\n ([key, description]) => `- browser.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'screenshotOptions') && context.screenshotOptions) {\n fieldGuideSections.push(`- screenshotOptions: ${fieldGuide.screenshotOptions}`);\n }\n\n fieldGuideSections.push('');\n\n return [\n 'You are a screenshot title generator for Storybook Playwright.',\n 'Your goal is to read the input JSON and produce one clear screenshot title.',\n '',\n 'Follow this process INTERNALLY before writing the final answer.',\n 'Do NOT output your reasoning, intermediate steps, or any text other than the final title:',\n '1) Parse INPUT_JSON.',\n '2) Identify the highest-priority signals using the field guide and priority chain below.',\n '3) Build a short, human-friendly title following the title rules.',\n '4) Output ONLY the final title.',\n '',\n ...fieldGuideSections,\n\n // -----------------------------------------------------------------------\n // Priority chain\n // -----------------------------------------------------------------------\n 'Priority chain (use the first level that provides meaningful content):',\n ' 1. actions + changedArgs (what the user did AND what changed)',\n ' 2. actions alone (describe the resulting UI state)',\n ' 3. changedArgs alone (describe the changed configuration)',\n ' 4. story.name (use as a component anchor)',\n ' 5. story.title rightmost segment',\n ' 6. filePath component/feature name',\n ` 7. Fallback title: ${JSON.stringify(resolved.fallbackTitle)}`,\n ' (use ONLY when no changedArgs, no actions, and story.name is absent or a single generic word)',\n '',\n\n // -----------------------------------------------------------------------\n // Title rules\n // -----------------------------------------------------------------------\n 'Title rules:',\n `- Maximum length: ${resolved.maxTitleLength} characters (hard limit — the output will be truncated in code if exceeded).`,\n `- ${resolved.includeBrowserType ? 'Include browser type only when it adds useful context (e.g. a browser-specific rendering difference).' : 'Do not include browser type in the title.'}`,\n `- ${resolved.includeStoryId ? 'You may include story.id when needed for uniqueness.' : 'Do not include story.id unless no other unique context exists.'}`,\n '- Prefer changedArgs and actions details when present.',\n '- Avoid filler words and generic phrases.',\n '- Title must be plain text: no markdown, no surrounding quotes.',\n '- Use sentence case with a capital first letter. End with a period only if the title is a full sentence.',\n '',\n\n // -----------------------------------------------------------------------\n // Title style contract\n // -----------------------------------------------------------------------\n 'Title style contract:',\n '- Prefer UI state over component names (e.g. \"Dropdown open\" not \"Dropdown component\").',\n '- Prefer observable behavior over configuration (e.g. \"Error message visible\" not \"hasError=true\").',\n '- Prefer post-action result when actions are present (e.g. \"Tooltip shown after hover\" not \"mouseMove on tooltip trigger\").',\n '- Avoid generic terms: \"renders correctly\", \"display\", \"view\", \"screenshot\", \"story\", \"component\".',\n '',\n 'Examples of BAD titles:',\n '- \"Component with some changes\" ← too generic, no specifics',\n '- \"Story screenshot\" ← pure filler',\n '- \"mouseMove, click\" ← lists actions instead of describing result',\n '- \"Button renders correctly\" ← generic fallback used when real content exists',\n '',\n\n // -----------------------------------------------------------------------\n // Few-shot examples\n // -----------------------------------------------------------------------\n FEW_SHOT_EXAMPLES,\n '',\n\n // -----------------------------------------------------------------------\n // Output contract\n // -----------------------------------------------------------------------\n 'Output contract (strict):',\n '- Output ONLY the final title.',\n '- Do NOT output your internal thinking, reasoning steps, or any introductory text.',\n '- Do NOT wrap the title in quotes, markdown, or JSON.',\n ...resolved.outputPrompt,\n `- If context is insufficient (no changedArgs, no actions, story.name absent or generic), use fallback: ${JSON.stringify(resolved.fallbackTitle)}`,\n '',\n\n // -----------------------------------------------------------------------\n // Input\n // -----------------------------------------------------------------------\n 'INPUT_JSON:',\n inputJson,\n ].join('\\n');\n}\n"]}
|
package/dist/ai/index.mjs
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
1
|
+
var i={fallbackTitle:"Should render correctly.",includeBrowserType:true,includeStoryId:false,maxTitleLength:80,outputPrompt:"Return only the generated title as a plain text string, without any additional text, reasoning, or formatting."},u=2,h=10,d={changedArgs:"The arguments explicitly changed from their defaults for this story. This is the single highest-priority field \u2014 always prefer these details when building the title.",initialArgs:"The default arguments for the story. Use these only when changedArgs is empty or absent, to understand the base state of the component.",argTypes:"Metadata about the story arguments. Consult this only when the meaning of a changedArgs value is unclear without extra context.",name:'The display name of the story (e.g. "Primary Button"). Use as the component anchor when changedArgs and actions are both absent.',title:'The hierarchical Storybook path in the format "Group/Component/Variant" (e.g. "Forms/Button/Disabled"). The rightmost segment is the most specific and useful; earlier segments provide grouping context only.',parameters:"Storybook-level metadata. Only relevant when it directly affects what the screenshot shows \u2014 for example a viewport override, a forced background colour, or a locale setting. Ignore generic infrastructure keys.",filePath:"The source file path of the story. Use only as a last resort to infer the component or feature name when no other field provides it.",id:"The unique story identifier. Include in the title only when no other field provides enough uniqueness and includeStoryId is permitted.",actions:'An ordered sequence of Playwright page interactions \u2014 called an action set \u2014 executed on the page immediately before the screenshot was taken. Each entry is a discrete action such as mouseMove, mouseDown, click, press, scroll, dragDrop, hover, or a custom page extension. Together they define the exact UI state captured (e.g. "after hovering a tooltip" or "after dragging the slider to max"). When present, describe the resulting UI state rather than listing the individual action names. Treat this as a high-priority field alongside changedArgs.'},p={type:"The Playwright browser engine used: chromium, firefox, or webkit. Include in the title only when cross-browser differences are the point of the screenshot (e.g. a webkit-specific rendering bug).",options:"Browser or device options such as viewport dimensions and device profile. Include when they meaningfully change the visual output \u2014 e.g. a mobile viewport that triggers a responsive layout shift."},g="Options that control how the screenshot is captured and may change its meaning: fullPage (captures beyond the visible viewport), clip (captures a specific region), animations (disabled/enabled), mask (hides elements), omitBackground (transparent background). Mention these in the title only when they are the distinguishing factor of this screenshot.",l={story:d,browser:p,screenshotOptions:g},f=`Examples (input \u2192 output):
|
|
2
|
+
|
|
3
|
+
Input: {"story":{"name":"Button","changedArgs":{"variant":"destructive","disabled":true}}}
|
|
4
|
+
Output: Button - Destructive and Disabled
|
|
5
|
+
|
|
6
|
+
Input: {"story":{"name":"Tooltip","changedArgs":{}},"actions":[{"type":"mouseMove","selector":"#trigger"}]}
|
|
7
|
+
Output: Tooltip visible on trigger hover
|
|
8
|
+
|
|
9
|
+
Input: {"story":{"name":"Slider","changedArgs":{"min":0,"max":100}},"actions":[{"type":"dragDrop","selector":"#thumb","position":{"x":300,"y":0}}]}
|
|
10
|
+
Output: Slider thumb dragged to far right
|
|
11
|
+
|
|
12
|
+
Input: {"story":{"name":"Modal","changedArgs":{"open":true}},"browser":{"type":"firefox"}}
|
|
13
|
+
Output: Modal open (Firefox)
|
|
14
|
+
|
|
15
|
+
Input: {"story":{"name":"Card"},"browser":{"type":"webkit","options":{"viewport":{"width":375,"height":812}}}}
|
|
16
|
+
Output: Card on mobile viewport (WebKit)
|
|
17
|
+
|
|
18
|
+
Input: {"story":{"name":"A"}}
|
|
19
|
+
Output: Should render correctly.`;function c(e,t){return e[t]!==void 0}function m(e={}){let t=Number.isFinite(e.maxTitleLength)?Math.max(h,Math.floor(e.maxTitleLength)):i.maxTitleLength,r=(Array.isArray(e.outputPrompt)?e.outputPrompt:typeof e.outputPrompt=="string"?[e.outputPrompt]:[i.outputPrompt]).map(n=>n.trim()).map(n=>"- "+n);return {fallbackTitle:typeof e.fallbackTitle=="string"&&e.fallbackTitle.trim()?e.fallbackTitle.trim():i.fallbackTitle,includeBrowserType:e.includeBrowserType??i.includeBrowserType,includeStoryId:e.includeStoryId??i.includeStoryId,maxTitleLength:t,outputPrompt:r}}function y(e,t){return e.length<=t?e:e.slice(0,t-1).trimEnd()+"\u2026"}function b(e,t){let r=m(t),n=JSON.stringify(e,null,u),o=["Field guide:"];return c(e,"story")&&e.story&&o.push(...Object.entries(l.story).map(([s,a])=>`- story.${s}: ${a}`)),c(e,"browser")&&e.browser&&o.push(...Object.entries(l.browser).map(([s,a])=>`- browser.${s}: ${a}`)),c(e,"screenshotOptions")&&e.screenshotOptions&&o.push(`- screenshotOptions: ${l.screenshotOptions}`),o.push(""),["You are a screenshot title generator for Storybook Playwright.","Your goal is to read the input JSON and produce one clear screenshot title.","","Follow this process INTERNALLY before writing the final answer.","Do NOT output your reasoning, intermediate steps, or any text other than the final title:","1) Parse INPUT_JSON.","2) Identify the highest-priority signals using the field guide and priority chain below.","3) Build a short, human-friendly title following the title rules.","4) Output ONLY the final title.","",...o,"Priority chain (use the first level that provides meaningful content):"," 1. actions + changedArgs (what the user did AND what changed)"," 2. actions alone (describe the resulting UI state)"," 3. changedArgs alone (describe the changed configuration)"," 4. story.name (use as a component anchor)"," 5. story.title rightmost segment"," 6. filePath component/feature name",` 7. Fallback title: ${JSON.stringify(r.fallbackTitle)}`," (use ONLY when no changedArgs, no actions, and story.name is absent or a single generic word)","","Title rules:",`- Maximum length: ${r.maxTitleLength} characters (hard limit \u2014 the output will be truncated in code if exceeded).`,`- ${r.includeBrowserType?"Include browser type only when it adds useful context (e.g. a browser-specific rendering difference).":"Do not include browser type in the title."}`,`- ${r.includeStoryId?"You may include story.id when needed for uniqueness.":"Do not include story.id unless no other unique context exists."}`,"- Prefer changedArgs and actions details when present.","- Avoid filler words and generic phrases.","- Title must be plain text: no markdown, no surrounding quotes.","- Use sentence case with a capital first letter. End with a period only if the title is a full sentence.","","Title style contract:",'- Prefer UI state over component names (e.g. "Dropdown open" not "Dropdown component").','- Prefer observable behavior over configuration (e.g. "Error message visible" not "hasError=true").','- Prefer post-action result when actions are present (e.g. "Tooltip shown after hover" not "mouseMove on tooltip trigger").','- Avoid generic terms: "renders correctly", "display", "view", "screenshot", "story", "component".',"","Examples of BAD titles:",'- "Component with some changes" \u2190 too generic, no specifics','- "Story screenshot" \u2190 pure filler','- "mouseMove, click" \u2190 lists actions instead of describing result','- "Button renders correctly" \u2190 generic fallback used when real content exists',"",f,"","Output contract (strict):","- Output ONLY the final title.","- Do NOT output your internal thinking, reasoning steps, or any introductory text.","- Do NOT wrap the title in quotes, markdown, or JSON.",...r.outputPrompt,`- If context is insufficient (no changedArgs, no actions, story.name absent or generic), use fallback: ${JSON.stringify(r.fallbackTitle)}`,"","INPUT_JSON:",n].join(`
|
|
20
|
+
`)}export{b as createScreenshotTitlePrompt,y as truncateTitle};//# sourceMappingURL=index.mjs.map
|
|
3
21
|
//# sourceMappingURL=index.mjs.map
|
package/dist/ai/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ai/generate-screenshot-title-prompt.ts"],"names":["DEFAULT_OPTIONS","INPUT_JSON_INDENT","MIN_TITLE_LENGTH","storyGuide","browserGuide","screenshotOptionsGuide","fieldGuide","hasOwnDefinedValue","value","key","normalizeOptions","options","maxTitleLength","outputPrompt","instruction","x","createScreenshotTitlePrompt","context","resolved","inputJson","fieldGuideSections","description"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { GenerateScreenshotTitleInput } from '../schema';\n\nexport interface CreateScreenshotTitlePromptOptions {\n /**\n * Maximum number of characters allowed in the returned title.\n *\n * @default 80\n */\n maxTitleLength?: number;\n\n /**\n * Fallback title the model should use when input is too sparse.\n *\n * @default 'Should render correctly.'\n */\n fallbackTitle?: string;\n\n /**\n * Whether the generated title should include the browser type when useful.\n *\n * @default true\n */\n includeBrowserType?: boolean;\n\n /**\n * Whether the generated title can include the story id.\n *\n * @default false\n */\n includeStoryId?: boolean;\n\n /**\n * Additional instructions for the output format, appended to the prompt's output contract section.\n *\n * @default 'Return only the generated title as string, without any additional text or formatting.'\n */\n outputPrompt?: string | string[];\n}\n\ntype PartialContext = Partial<GenerateScreenshotTitleInput> &\n Pick<GenerateScreenshotTitleInput, 'story'>;\n\ntype ScreenshotTitleFieldGuide = {\n story: Record<keyof GenerateScreenshotTitleInput['story'], string>;\n browser: Record<keyof GenerateScreenshotTitleInput['browser'], string>;\n screenshotOptions: string;\n};\n\nconst DEFAULT_OPTIONS: Required<CreateScreenshotTitlePromptOptions> = {\n fallbackTitle: 'Should render correctly.',\n includeBrowserType: true,\n includeStoryId: false,\n maxTitleLength: 80,\n outputPrompt:\n 'Return only the generated title as string, without any additional text or formatting.',\n};\n\nconst INPUT_JSON_INDENT = 2;\nconst MIN_TITLE_LENGTH = 10;\n\nconst storyGuide = {\n changedArgs:\n 'The specific arguments that have been changed from their default values for this story; these are high-priority signals for understanding what is unique about this screenshot.',\n initialArgs:\n 'The default arguments for the story, which can provide context but are less important than changedArgs for title generation.',\n argTypes:\n 'Metadata about the story arguments, which should only be used if it clarifies the meaning of changedArgs.',\n name: 'The display name of the story, often concise and descriptive.',\n title:\n 'The hierarchical path of the story in Storybook, which may include groupings and can provide context about the component and its variations.',\n parameters:\n 'Additional metadata at the Storybook level, which should only be considered if it impacts the meaning of the screenshot.',\n filePath:\n 'The file path to the source story, which may provide context about the component or feature being tested.',\n id: 'The unique identifier for the Storybook story, which should only be included in the title if no other unique context exists.',\n} satisfies ScreenshotTitleFieldGuide['story'];\n\nconst browserGuide = {\n type: 'The type of browser engine used (e.g., chromium, firefox, webkit), which should be included in the title only when it adds useful context.',\n options:\n 'Options related to the browser or device, such as viewport size and device profile, which may influence the meaning of the screenshot.',\n} satisfies ScreenshotTitleFieldGuide['browser'];\n\nconst screenshotOptionsGuide =\n 'Behavior options for taking the screenshot that may change its meaning or context.';\n\nconst fieldGuide: ScreenshotTitleFieldGuide = {\n story: storyGuide,\n browser: browserGuide,\n screenshotOptions: screenshotOptionsGuide,\n};\n\nfunction hasOwnDefinedValue<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): boolean {\n return value[key] !== undefined;\n}\n\nfunction normalizeOptions(\n options: CreateScreenshotTitlePromptOptions = {},\n): Required<CreateScreenshotTitlePromptOptions> {\n const maxTitleLength = Number.isFinite(options.maxTitleLength)\n ? Math.max(MIN_TITLE_LENGTH, Math.floor(options.maxTitleLength as number))\n : DEFAULT_OPTIONS.maxTitleLength;\n\n const outputPrompt = (\n Array.isArray(options.outputPrompt)\n ? options.outputPrompt\n : typeof options.outputPrompt === 'string'\n ? [options.outputPrompt]\n : [DEFAULT_OPTIONS.outputPrompt]\n )\n .map((instruction) => (instruction as string).trim())\n .map((x) => '- ' + x);\n\n return {\n fallbackTitle:\n typeof options.fallbackTitle === 'string' && options.fallbackTitle.trim()\n ? options.fallbackTitle.trim()\n : DEFAULT_OPTIONS.fallbackTitle,\n includeBrowserType: options.includeBrowserType ?? DEFAULT_OPTIONS.includeBrowserType,\n includeStoryId: options.includeStoryId ?? DEFAULT_OPTIONS.includeStoryId,\n maxTitleLength,\n outputPrompt: outputPrompt,\n };\n}\n\n/**\n * Creates a strict prompt for LLMs to generate a screenshot title from Storybook data.\n *\n * The prompt enforces JSON-only output in the form:\n * `{\"title\":\"...\"}`.\n */\nexport function createScreenshotTitlePrompt(\n context: PartialContext,\n options?: CreateScreenshotTitlePromptOptions,\n): string {\n const resolved = normalizeOptions(options);\n const inputJson = JSON.stringify(context, null, INPUT_JSON_INDENT);\n const fieldGuideSections: string[] = ['Field guide:'];\n\n if (hasOwnDefinedValue(context, 'story') && context.story) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.story).map(\n ([key, description]) => `- story.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'browser') && context.browser) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.browser).map(\n ([key, description]) => `- browser.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'screenshotOptions') && context.screenshotOptions) {\n fieldGuideSections.push(`- screenshotOptions: ${fieldGuide.screenshotOptions}`);\n }\n\n fieldGuideSections.push('');\n\n return [\n 'You are a screenshot title generator for Storybook Playwright.',\n 'Your goal is to read the input JSON and produce one clear screenshot title.',\n '',\n 'Follow this process internally before writing the final answer:',\n '1) Parse INPUT_JSON as JSON.',\n '2) Review every top-level and nested field and understand what each field says about the screenshot context.',\n '3) Decide the most important details for naming this screenshot.',\n '4) Build a short, human-friendly title.',\n '',\n ...fieldGuideSections,\n 'Title rules:',\n `- Maximum length: ${resolved.maxTitleLength} characters.`,\n `- ${resolved.includeBrowserType ? 'Include browser type only when it adds useful context.' : 'Do not include browser type in the title.'}`,\n `- ${resolved.includeStoryId ? 'You may include story.id when needed for uniqueness.' : 'Do not include story.id unless no other unique context exists.'}`,\n '- Prefer changedArgs details when present.',\n '- Avoid filler words and generic phrases.',\n '- Title must be plain text (no markdown, no quotes around the full title).',\n '',\n 'Output contract (strict):',\n ...resolved.outputPrompt,\n `- If context is insufficient, use fallback title: ${JSON.stringify(resolved.fallbackTitle)}`,\n '',\n 'INPUT_JSON:',\n inputJson,\n ].join('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ai/generate-screenshot-title-prompt.ts"],"names":["DEFAULT_OPTIONS","INPUT_JSON_INDENT","MIN_TITLE_LENGTH","storyGuide","browserGuide","screenshotOptionsGuide","fieldGuide","FEW_SHOT_EXAMPLES","hasOwnDefinedValue","value","key","normalizeOptions","options","maxTitleLength","outputPrompt","instruction","x","truncateTitle","title","maxLength","createScreenshotTitlePrompt","context","resolved","inputJson","fieldGuideSections","description"],"mappings":"AAsDA,IAAMA,CAAAA,CAAgE,CACpE,aAAA,CAAe,0BAAA,CACf,kBAAA,CAAoB,IAAA,CACpB,cAAA,CAAgB,KAAA,CAChB,cAAA,CAAgB,EAAA,CAChB,YAAA,CACE,gHACJ,CAAA,CAEMC,EAAoB,CAAA,CACpBC,CAAAA,CAAmB,EAAA,CAEnBC,CAAAA,CAAa,CACjB,WAAA,CACE,4KAAA,CAIF,WAAA,CACE,yIAAA,CAGF,QAAA,CACE,iIAAA,CAGF,IAAA,CACE,kIAAA,CAGF,KAAA,CACE,iNAIF,UAAA,CACE,yNAAA,CAIF,QAAA,CACE,sIAAA,CAGF,EAAA,CACE,wIAAA,CAGF,OAAA,CACE,+iBAQJ,CAAA,CAEMC,CAAAA,CAAe,CACnB,IAAA,CACE,oMAAA,CAIF,OAAA,CACE,0MAGJ,CAAA,CAEMC,CAAAA,CACJ,gWAAA,CAMIC,CAAAA,CAAwC,CAC5C,KAAA,CAAOH,CAAAA,CACP,OAAA,CAASC,CAAAA,CACT,iBAAA,CAAmBC,CACrB,CAAA,CAKME,CAAAA,CAAoB,CAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA,gCAAA,CAAA,CAqB1B,SAASC,CAAAA,CACPC,CAAAA,CACAC,CAAAA,CACS,CACT,OAAOD,CAAAA,CAAMC,CAAG,CAAA,GAAM,MACxB,CAEA,SAASC,CAAAA,CACPC,EAA8C,EAAC,CACD,CAC9C,IAAMC,CAAAA,CAAiB,MAAA,CAAO,QAAA,CAASD,CAAAA,CAAQ,cAAc,CAAA,CACzD,IAAA,CAAK,GAAA,CAAIV,CAAAA,CAAkB,KAAK,KAAA,CAAMU,CAAAA,CAAQ,cAAwB,CAAC,EACvEZ,CAAAA,CAAgB,cAAA,CAEdc,CAAAA,CAAAA,CACJ,KAAA,CAAM,OAAA,CAAQF,CAAAA,CAAQ,YAAY,CAAA,CAC9BA,EAAQ,YAAA,CACR,OAAOA,CAAAA,CAAQ,YAAA,EAAiB,QAAA,CAC9B,CAACA,CAAAA,CAAQ,YAAY,EACrB,CAACZ,CAAAA,CAAgB,YAAY,CAAA,EAElC,GAAA,CAAKe,CAAAA,EAAiBA,CAAAA,CAAuB,IAAA,EAAM,CAAA,CACnD,GAAA,CAAKC,CAAAA,EAAM,IAAA,CAAOA,CAAC,CAAA,CAEtB,OAAO,CACL,aAAA,CACE,OAAOJ,CAAAA,CAAQ,aAAA,EAAkB,QAAA,EAAYA,CAAAA,CAAQ,aAAA,CAAc,IAAA,EAAK,CACpEA,CAAAA,CAAQ,cAAc,IAAA,EAAK,CAC3BZ,CAAAA,CAAgB,aAAA,CACtB,kBAAA,CAAoBY,CAAAA,CAAQ,kBAAA,EAAsBZ,CAAAA,CAAgB,mBAClE,cAAA,CAAgBY,CAAAA,CAAQ,cAAA,EAAkBZ,CAAAA,CAAgB,cAAA,CAC1D,cAAA,CAAAa,CAAAA,CACA,YAAA,CAAAC,CACF,CACF,CAOO,SAASG,CAAAA,CAAcC,EAAeC,CAAAA,CAA2B,CACtE,OAAID,CAAAA,CAAM,QAAUC,CAAAA,CAAkBD,CAAAA,CAC/BA,CAAAA,CAAM,KAAA,CAAM,CAAA,CAAGC,CAAAA,CAAY,CAAC,CAAA,CAAE,SAAQ,CAAI,QACnD,CAUO,SAASC,EACdC,CAAAA,CACAT,CAAAA,CACQ,CACR,IAAMU,EAAWX,CAAAA,CAAiBC,CAAO,CAAA,CACnCW,CAAAA,CAAY,IAAA,CAAK,SAAA,CAAUF,CAAAA,CAAS,IAAA,CAAMpB,CAAiB,CAAA,CAC3DuB,CAAAA,CAA+B,CAAC,cAAc,EAEpD,OAAIhB,CAAAA,CAAmBa,CAAAA,CAAS,OAAO,GAAKA,CAAAA,CAAQ,KAAA,EAClDG,CAAAA,CAAmB,IAAA,CACjB,GAAG,MAAA,CAAO,OAAA,CAAQlB,CAAAA,CAAW,KAAK,CAAA,CAAE,GAAA,CAClC,CAAC,CAACI,CAAAA,CAAKe,CAAW,CAAA,GAAM,CAAA,QAAA,EAAWf,CAAG,CAAA,EAAA,EAAKe,CAAW,CAAA,CACxD,CACF,CAAA,CAGEjB,CAAAA,CAAmBa,CAAAA,CAAS,SAAS,GAAKA,CAAAA,CAAQ,OAAA,EACpDG,CAAAA,CAAmB,IAAA,CACjB,GAAG,MAAA,CAAO,OAAA,CAAQlB,CAAAA,CAAW,OAAO,EAAE,GAAA,CACpC,CAAC,CAACI,CAAAA,CAAKe,CAAW,CAAA,GAAM,CAAA,UAAA,EAAaf,CAAG,KAAKe,CAAW,CAAA,CAC1D,CACF,CAAA,CAGEjB,EAAmBa,CAAAA,CAAS,mBAAmB,CAAA,EAAKA,CAAAA,CAAQ,mBAC9DG,CAAAA,CAAmB,IAAA,CAAK,CAAA,qBAAA,EAAwBlB,CAAAA,CAAW,iBAAiB,CAAA,CAAE,CAAA,CAGhFkB,CAAAA,CAAmB,KAAK,EAAE,CAAA,CAEnB,CACL,gEAAA,CACA,8EACA,EAAA,CACA,iEAAA,CACA,2FAAA,CACA,sBAAA,CACA,2FACA,mEAAA,CACA,iCAAA,CACA,EAAA,CACA,GAAGA,CAAAA,CAKH,wEAAA,CACA,mEAAA,CACA,gEAAA,CACA,oEACA,0DAAA,CACA,oCAAA,CACA,sCAAA,CACA,CAAA,qBAAA,EAAwB,IAAA,CAAK,SAAA,CAAUF,CAAAA,CAAS,aAAa,CAAC,CAAA,CAAA,CAC9D,oGAAA,CACA,EAAA,CAKA,cAAA,CACA,CAAA,kBAAA,EAAqBA,CAAAA,CAAS,cAAc,CAAA,iFAAA,CAAA,CAC5C,KAAKA,CAAAA,CAAS,kBAAA,CAAqB,uGAAA,CAA0G,2CAA2C,GACxL,CAAA,EAAA,EAAKA,CAAAA,CAAS,cAAA,CAAiB,sDAAA,CAAyD,gEAAgE,CAAA,CAAA,CACxJ,wDAAA,CACA,2CAAA,CACA,iEAAA,CACA,0GAAA,CACA,EAAA,CAKA,uBAAA,CACA,yFAAA,CACA,sGACA,6HAAA,CACA,oGAAA,CACA,EAAA,CACA,yBAAA,CACA,qEACA,sDAAA,CACA,qFAAA,CACA,yFAAA,CACA,EAAA,CAKAf,EACA,EAAA,CAKA,2BAAA,CACA,gCAAA,CACA,oFAAA,CACA,uDAAA,CACA,GAAGe,CAAAA,CAAS,YAAA,CACZ,0GAA0G,IAAA,CAAK,SAAA,CAAUA,CAAAA,CAAS,aAAa,CAAC,CAAA,CAAA,CAChJ,EAAA,CAKA,aAAA,CACAC,CACF,EAAE,IAAA,CAAK;AAAA,CAAI,CACb","file":"index.mjs","sourcesContent":["import type { GenerateScreenshotTitleInput } from '../schema';\n\nexport interface CreateScreenshotTitlePromptOptions {\n /**\n * Maximum number of characters allowed in the returned title.\n * Note: the generated title is also truncated in code as a safety net,\n * since LLMs count tokens, not characters.\n *\n * @default 80\n */\n maxTitleLength?: number;\n\n /**\n * Fallback title the model should use when input is too sparse to produce\n * a meaningful title (no changedArgs, no actions, and story.name is absent\n * or a single generic word).\n *\n * @default 'Should render correctly.'\n */\n fallbackTitle?: string;\n\n /**\n * Whether the generated title should include the browser type when useful.\n *\n * @default true\n */\n includeBrowserType?: boolean;\n\n /**\n * Whether the generated title can include the story id.\n *\n * @default false\n */\n includeStoryId?: boolean;\n\n /**\n * Additional instructions for the output format, appended to the prompt's\n * output contract section.\n *\n * @default 'Return only the generated title as a plain text string, without\n * any additional text, reasoning, or formatting.'\n */\n outputPrompt?: string | string[];\n}\n\ntype PartialContext = Partial<GenerateScreenshotTitleInput> &\n Pick<GenerateScreenshotTitleInput, 'story'>;\n\ntype ScreenshotTitleFieldGuide = {\n story: Record<keyof GenerateScreenshotTitleInput['story'], string>;\n browser: Record<keyof GenerateScreenshotTitleInput['browser'], string>;\n screenshotOptions: string;\n};\n\nconst DEFAULT_OPTIONS: Required<CreateScreenshotTitlePromptOptions> = {\n fallbackTitle: 'Should render correctly.',\n includeBrowserType: true,\n includeStoryId: false,\n maxTitleLength: 80,\n outputPrompt:\n 'Return only the generated title as a plain text string, without any additional text, reasoning, or formatting.',\n};\n\nconst INPUT_JSON_INDENT = 2;\nconst MIN_TITLE_LENGTH = 10;\n\nconst storyGuide = {\n changedArgs:\n 'The arguments explicitly changed from their defaults for this story. ' +\n 'This is the single highest-priority field — always prefer these details ' +\n 'when building the title.',\n\n initialArgs:\n 'The default arguments for the story. Use these only when changedArgs is ' +\n 'empty or absent, to understand the base state of the component.',\n\n argTypes:\n 'Metadata about the story arguments. Consult this only when the meaning ' +\n 'of a changedArgs value is unclear without extra context.',\n\n name:\n 'The display name of the story (e.g. \"Primary Button\"). Use as the ' +\n 'component anchor when changedArgs and actions are both absent.',\n\n title:\n 'The hierarchical Storybook path in the format \"Group/Component/Variant\" ' +\n '(e.g. \"Forms/Button/Disabled\"). The rightmost segment is the most ' +\n 'specific and useful; earlier segments provide grouping context only.',\n\n parameters:\n 'Storybook-level metadata. Only relevant when it directly affects what ' +\n 'the screenshot shows — for example a viewport override, a forced ' +\n 'background colour, or a locale setting. Ignore generic infrastructure keys.',\n\n filePath:\n 'The source file path of the story. Use only as a last resort to infer ' +\n 'the component or feature name when no other field provides it.',\n\n id:\n 'The unique story identifier. Include in the title only when no other ' +\n 'field provides enough uniqueness and includeStoryId is permitted.',\n\n actions:\n 'An ordered sequence of Playwright page interactions — called an action ' +\n 'set — executed on the page immediately before the screenshot was taken. ' +\n 'Each entry is a discrete action such as mouseMove, mouseDown, click, ' +\n 'press, scroll, dragDrop, hover, or a custom page extension. Together ' +\n 'they define the exact UI state captured (e.g. \"after hovering a tooltip\" ' +\n 'or \"after dragging the slider to max\"). When present, describe the ' +\n 'resulting UI state rather than listing the individual action names. ' +\n 'Treat this as a high-priority field alongside changedArgs.',\n} satisfies ScreenshotTitleFieldGuide['story'];\n\nconst browserGuide = {\n type:\n 'The Playwright browser engine used: chromium, firefox, or webkit. ' +\n 'Include in the title only when cross-browser differences are the point ' +\n 'of the screenshot (e.g. a webkit-specific rendering bug).',\n\n options:\n 'Browser or device options such as viewport dimensions and device ' +\n 'profile. Include when they meaningfully change the visual output — ' +\n 'e.g. a mobile viewport that triggers a responsive layout shift.',\n} satisfies ScreenshotTitleFieldGuide['browser'];\n\nconst screenshotOptionsGuide =\n 'Options that control how the screenshot is captured and may change its ' +\n 'meaning: fullPage (captures beyond the visible viewport), clip (captures ' +\n 'a specific region), animations (disabled/enabled), mask (hides elements), ' +\n 'omitBackground (transparent background). Mention these in the title only ' +\n 'when they are the distinguishing factor of this screenshot.';\n\nconst fieldGuide: ScreenshotTitleFieldGuide = {\n story: storyGuide,\n browser: browserGuide,\n screenshotOptions: screenshotOptionsGuide,\n};\n\n// ---------------------------------------------------------------------------\n// Few-shot examples embedded in the prompt\n// ---------------------------------------------------------------------------\nconst FEW_SHOT_EXAMPLES = `\\\nExamples (input → output):\n\nInput: {\"story\":{\"name\":\"Button\",\"changedArgs\":{\"variant\":\"destructive\",\"disabled\":true}}}\nOutput: Button - Destructive and Disabled\n\nInput: {\"story\":{\"name\":\"Tooltip\",\"changedArgs\":{}},\"actions\":[{\"type\":\"mouseMove\",\"selector\":\"#trigger\"}]}\nOutput: Tooltip visible on trigger hover\n\nInput: {\"story\":{\"name\":\"Slider\",\"changedArgs\":{\"min\":0,\"max\":100}},\"actions\":[{\"type\":\"dragDrop\",\"selector\":\"#thumb\",\"position\":{\"x\":300,\"y\":0}}]}\nOutput: Slider thumb dragged to far right\n\nInput: {\"story\":{\"name\":\"Modal\",\"changedArgs\":{\"open\":true}},\"browser\":{\"type\":\"firefox\"}}\nOutput: Modal open (Firefox)\n\nInput: {\"story\":{\"name\":\"Card\"},\"browser\":{\"type\":\"webkit\",\"options\":{\"viewport\":{\"width\":375,\"height\":812}}}}\nOutput: Card on mobile viewport (WebKit)\n\nInput: {\"story\":{\"name\":\"A\"}}\nOutput: Should render correctly.`;\n\nfunction hasOwnDefinedValue<T extends object, K extends keyof T>(\n value: T,\n key: K,\n): boolean {\n return value[key] !== undefined;\n}\n\nfunction normalizeOptions(\n options: CreateScreenshotTitlePromptOptions = {},\n): Required<CreateScreenshotTitlePromptOptions> {\n const maxTitleLength = Number.isFinite(options.maxTitleLength)\n ? Math.max(MIN_TITLE_LENGTH, Math.floor(options.maxTitleLength as number))\n : DEFAULT_OPTIONS.maxTitleLength;\n\n const outputPrompt = (\n Array.isArray(options.outputPrompt)\n ? options.outputPrompt\n : typeof options.outputPrompt === 'string'\n ? [options.outputPrompt]\n : [DEFAULT_OPTIONS.outputPrompt]\n )\n .map((instruction) => (instruction as string).trim())\n .map((x) => '- ' + x);\n\n return {\n fallbackTitle:\n typeof options.fallbackTitle === 'string' && options.fallbackTitle.trim()\n ? options.fallbackTitle.trim()\n : DEFAULT_OPTIONS.fallbackTitle,\n includeBrowserType: options.includeBrowserType ?? DEFAULT_OPTIONS.includeBrowserType,\n includeStoryId: options.includeStoryId ?? DEFAULT_OPTIONS.includeStoryId,\n maxTitleLength,\n outputPrompt,\n };\n}\n\n/**\n * Truncates a generated title to the configured maximum length, appending\n * an ellipsis if the string was cut. This acts as a safety net because LLMs\n * count tokens rather than characters and may slightly exceed the limit.\n */\nexport function truncateTitle(title: string, maxLength: number): string {\n if (title.length <= maxLength) return title;\n return title.slice(0, maxLength - 1).trimEnd() + '…';\n}\n\n/**\n * Creates a prompt for LLMs to generate a screenshot title from Storybook\n * Playwright data.\n *\n * The prompt instructs the model to return the title as a plain text string\n * with no additional text or formatting. Use {@link truncateTitle} as a\n * client-side safety net in case the model slightly exceeds maxTitleLength.\n */\nexport function createScreenshotTitlePrompt(\n context: PartialContext,\n options?: CreateScreenshotTitlePromptOptions,\n): string {\n const resolved = normalizeOptions(options);\n const inputJson = JSON.stringify(context, null, INPUT_JSON_INDENT);\n const fieldGuideSections: string[] = ['Field guide:'];\n\n if (hasOwnDefinedValue(context, 'story') && context.story) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.story).map(\n ([key, description]) => `- story.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'browser') && context.browser) {\n fieldGuideSections.push(\n ...Object.entries(fieldGuide.browser).map(\n ([key, description]) => `- browser.${key}: ${description}`,\n ),\n );\n }\n\n if (hasOwnDefinedValue(context, 'screenshotOptions') && context.screenshotOptions) {\n fieldGuideSections.push(`- screenshotOptions: ${fieldGuide.screenshotOptions}`);\n }\n\n fieldGuideSections.push('');\n\n return [\n 'You are a screenshot title generator for Storybook Playwright.',\n 'Your goal is to read the input JSON and produce one clear screenshot title.',\n '',\n 'Follow this process INTERNALLY before writing the final answer.',\n 'Do NOT output your reasoning, intermediate steps, or any text other than the final title:',\n '1) Parse INPUT_JSON.',\n '2) Identify the highest-priority signals using the field guide and priority chain below.',\n '3) Build a short, human-friendly title following the title rules.',\n '4) Output ONLY the final title.',\n '',\n ...fieldGuideSections,\n\n // -----------------------------------------------------------------------\n // Priority chain\n // -----------------------------------------------------------------------\n 'Priority chain (use the first level that provides meaningful content):',\n ' 1. actions + changedArgs (what the user did AND what changed)',\n ' 2. actions alone (describe the resulting UI state)',\n ' 3. changedArgs alone (describe the changed configuration)',\n ' 4. story.name (use as a component anchor)',\n ' 5. story.title rightmost segment',\n ' 6. filePath component/feature name',\n ` 7. Fallback title: ${JSON.stringify(resolved.fallbackTitle)}`,\n ' (use ONLY when no changedArgs, no actions, and story.name is absent or a single generic word)',\n '',\n\n // -----------------------------------------------------------------------\n // Title rules\n // -----------------------------------------------------------------------\n 'Title rules:',\n `- Maximum length: ${resolved.maxTitleLength} characters (hard limit — the output will be truncated in code if exceeded).`,\n `- ${resolved.includeBrowserType ? 'Include browser type only when it adds useful context (e.g. a browser-specific rendering difference).' : 'Do not include browser type in the title.'}`,\n `- ${resolved.includeStoryId ? 'You may include story.id when needed for uniqueness.' : 'Do not include story.id unless no other unique context exists.'}`,\n '- Prefer changedArgs and actions details when present.',\n '- Avoid filler words and generic phrases.',\n '- Title must be plain text: no markdown, no surrounding quotes.',\n '- Use sentence case with a capital first letter. End with a period only if the title is a full sentence.',\n '',\n\n // -----------------------------------------------------------------------\n // Title style contract\n // -----------------------------------------------------------------------\n 'Title style contract:',\n '- Prefer UI state over component names (e.g. \"Dropdown open\" not \"Dropdown component\").',\n '- Prefer observable behavior over configuration (e.g. \"Error message visible\" not \"hasError=true\").',\n '- Prefer post-action result when actions are present (e.g. \"Tooltip shown after hover\" not \"mouseMove on tooltip trigger\").',\n '- Avoid generic terms: \"renders correctly\", \"display\", \"view\", \"screenshot\", \"story\", \"component\".',\n '',\n 'Examples of BAD titles:',\n '- \"Component with some changes\" ← too generic, no specifics',\n '- \"Story screenshot\" ← pure filler',\n '- \"mouseMove, click\" ← lists actions instead of describing result',\n '- \"Button renders correctly\" ← generic fallback used when real content exists',\n '',\n\n // -----------------------------------------------------------------------\n // Few-shot examples\n // -----------------------------------------------------------------------\n FEW_SHOT_EXAMPLES,\n '',\n\n // -----------------------------------------------------------------------\n // Output contract\n // -----------------------------------------------------------------------\n 'Output contract (strict):',\n '- Output ONLY the final title.',\n '- Do NOT output your internal thinking, reasoning steps, or any introductory text.',\n '- Do NOT wrap the title in quotes, markdown, or JSON.',\n ...resolved.outputPrompt,\n `- If context is insufficient (no changedArgs, no actions, story.name absent or generic), use fallback: ${JSON.stringify(resolved.fallbackTitle)}`,\n '',\n\n // -----------------------------------------------------------------------\n // Input\n // -----------------------------------------------------------------------\n 'INPUT_JSON:',\n inputJson,\n ].join('\\n');\n}\n"]}
|
|
@@ -2,13 +2,13 @@ import { appRouter } from '../../trpc/router.mjs';
|
|
|
2
2
|
import '@material-ui/core';
|
|
3
3
|
import 'playwright';
|
|
4
4
|
import 'ts-to-json';
|
|
5
|
-
import '../../story-
|
|
5
|
+
import '../../story-Cpro8pd5.mjs';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import '@trpc/server';
|
|
8
8
|
import 'node:http';
|
|
9
9
|
import 'jest-image-snapshot';
|
|
10
|
-
import '../../image-diff-
|
|
11
|
-
import '../../request-
|
|
10
|
+
import '../../image-diff-DNP2iheS.mjs';
|
|
11
|
+
import '../../request-BduSDqMg.mjs';
|
|
12
12
|
import '../../get-screenshot-paths-BhXNedbs.mjs';
|
|
13
13
|
|
|
14
14
|
|
|
@@ -2,13 +2,13 @@ import { appRouter } from '../../trpc/router.js';
|
|
|
2
2
|
import '@material-ui/core';
|
|
3
3
|
import 'playwright';
|
|
4
4
|
import 'ts-to-json';
|
|
5
|
-
import '../../story-
|
|
5
|
+
import '../../story-Cpro8pd5.js';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import '@trpc/server';
|
|
8
8
|
import 'node:http';
|
|
9
9
|
import 'jest-image-snapshot';
|
|
10
|
-
import '../../image-diff-
|
|
11
|
-
import '../../request-
|
|
10
|
+
import '../../image-diff-Dgb4eq6u.js';
|
|
11
|
+
import '../../request-CWwxYmNe.js';
|
|
12
12
|
import '../../get-screenshot-paths-BhXNedbs.js';
|
|
13
13
|
|
|
14
14
|
|