@stackable-labs/mcp-app-extension 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +325 -1
- package/dist/server.js +325 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2801,6 +2801,324 @@ narrow widths:
|
|
|
2801
2801
|
`;
|
|
2802
2802
|
};
|
|
2803
2803
|
|
|
2804
|
+
// ../../sdk/extension/ai-docs/src/generators/tailwind-utilities.ts
|
|
2805
|
+
var SAFELIST_DIRECTIVES = [
|
|
2806
|
+
"m-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16}",
|
|
2807
|
+
"mx-{0,1,2,3,4,5,6,8,10,12,16,auto} my-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2808
|
+
"mt-{0,1,2,3,4,5,6,8,10,12,16} mb-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2809
|
+
"ml-{0,1,2,3,4,5,6,8,10,12,16,auto} mr-{0,1,2,3,4,5,6,8,10,12,16,auto}",
|
|
2810
|
+
"p-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16}",
|
|
2811
|
+
"px-{0,1,2,3,4,5,6,8,10,12,16} py-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2812
|
+
"pt-{0,1,2,3,4,5,6,8,10,12,16} pb-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2813
|
+
"pl-{0,1,2,3,4,5,6,8,10,12,16} pr-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2814
|
+
"gap-{0,1,2,3,4,5,6,8,10,12} gap-x-{0,1,2,3,4,5,6,8} gap-y-{0,1,2,3,4,5,6,8}",
|
|
2815
|
+
"text-{xs,sm,base,lg,xl,2xl}",
|
|
2816
|
+
"font-{light,normal,medium,semibold,bold,extrabold}",
|
|
2817
|
+
"uppercase lowercase capitalize normal-case",
|
|
2818
|
+
"tracking-{tight,normal,wide,wider,widest}",
|
|
2819
|
+
"leading-{none,tight,snug,normal,relaxed,loose}",
|
|
2820
|
+
"opacity-{0,5,10,20,25,30,40,50,60,70,75,80,90,95,100}",
|
|
2821
|
+
"block inline inline-block flex inline-flex grid hidden",
|
|
2822
|
+
"flex-{1,auto,initial,none,row,col,wrap,nowrap,row-reverse,col-reverse}",
|
|
2823
|
+
"justify-{start,end,center,between,around,evenly}",
|
|
2824
|
+
"items-{start,end,center,baseline,stretch}",
|
|
2825
|
+
"self-{auto,start,end,center,stretch,baseline}",
|
|
2826
|
+
"w-{0,full,auto,screen,1/2,1/3,2/3,1/4,3/4,1,2,3,4,5,6,8,10,12,16,20,24,32,48}",
|
|
2827
|
+
"h-{0,full,auto,screen,1/2,1/3,2/3,1/4,3/4,1,2,3,4,5,6,8,10,12,16,20,24,32,48}",
|
|
2828
|
+
"min-w-{0,full,fit,min,max} max-w-{none,xs,sm,md,lg,xl,2xl,3xl,4xl,5xl,full,fit}",
|
|
2829
|
+
"aspect-{auto,square,video} aspect-[9/16]",
|
|
2830
|
+
"rounded-{none,sm,md,lg,xl,2xl,3xl,full}",
|
|
2831
|
+
"border border-{0,2,4,8} border-{x,y,t,b,l,r}",
|
|
2832
|
+
"cursor-{pointer,default,not-allowed,wait,text,move,help}",
|
|
2833
|
+
"overflow-{auto,hidden,visible,scroll}",
|
|
2834
|
+
"overflow-x-{auto,hidden,scroll} overflow-y-{auto,hidden,scroll}",
|
|
2835
|
+
"transition transition-{none,all,colors,opacity,transform}",
|
|
2836
|
+
"duration-{75,100,150,200,300,500,700,1000} ease-{linear,in,out,in-out}",
|
|
2837
|
+
"shadow-{sm,md,lg,xl,2xl,none,inner}",
|
|
2838
|
+
"relative absolute fixed sticky static",
|
|
2839
|
+
"z-{0,10,20,30,40,50,auto}",
|
|
2840
|
+
"text-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2841
|
+
"text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2842
|
+
"bg-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2843
|
+
"bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2844
|
+
"border-{zinc,slate,gray}-{100,200,300,400} border-transparent",
|
|
2845
|
+
"hover:opacity-{50,75,80,90,100}",
|
|
2846
|
+
"hover:text-{zinc,slate,gray,neutral}-{500,600,700,800,900}",
|
|
2847
|
+
"hover:text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2848
|
+
"hover:bg-{zinc,slate,gray,neutral}-{50,100,200}",
|
|
2849
|
+
"hover:bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2850
|
+
"focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
2851
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
2852
|
+
"dark:text-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2853
|
+
"dark:text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2854
|
+
"dark:bg-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2855
|
+
"dark:bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2856
|
+
"dark:border-{zinc,slate,gray}-{600,700,800,900} dark:border-transparent",
|
|
2857
|
+
"dark:hover:text-{zinc,slate,gray}-{50,100,200,300,400}",
|
|
2858
|
+
"dark:hover:bg-{zinc,slate,gray}-{700,800,900}"
|
|
2859
|
+
];
|
|
2860
|
+
var INLINE_DIRECTIVE_RE = /@source\s+inline\(\s*'([^']+)'\s*\)\s*;/g;
|
|
2861
|
+
var expand = (token) => {
|
|
2862
|
+
const m = token.match(/\{([^{}]+)\}/);
|
|
2863
|
+
if (!m) {
|
|
2864
|
+
return [token];
|
|
2865
|
+
}
|
|
2866
|
+
const head = token.slice(0, m.index);
|
|
2867
|
+
const tail = token.slice(m.index + m[0].length);
|
|
2868
|
+
const out = [];
|
|
2869
|
+
for (const opt of m[1].split(",")) {
|
|
2870
|
+
out.push(...expand(head + opt + tail));
|
|
2871
|
+
}
|
|
2872
|
+
return out;
|
|
2873
|
+
};
|
|
2874
|
+
var stripComments = (css) => css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
2875
|
+
var parseSafelist = (css) => {
|
|
2876
|
+
const stripped = stripComments(css);
|
|
2877
|
+
const classes = [];
|
|
2878
|
+
for (const match of stripped.matchAll(INLINE_DIRECTIVE_RE)) {
|
|
2879
|
+
for (const tok of match[1].split(/\s+/)) {
|
|
2880
|
+
if (tok) {
|
|
2881
|
+
classes.push(...expand(tok));
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
return classes;
|
|
2886
|
+
};
|
|
2887
|
+
var classifyFamily = (cls) => {
|
|
2888
|
+
if (cls.startsWith("dark:hover:text-")) return "Dark \u2014 hover text";
|
|
2889
|
+
if (cls.startsWith("dark:hover:bg-")) return "Dark \u2014 hover background";
|
|
2890
|
+
if (cls.startsWith("dark:text-")) return "Dark \u2014 text color";
|
|
2891
|
+
if (cls.startsWith("dark:bg-")) return "Dark \u2014 background color";
|
|
2892
|
+
if (cls.startsWith("dark:border-")) return "Dark \u2014 border color";
|
|
2893
|
+
if (cls.startsWith("hover:opacity-")) return "Hover \u2014 opacity";
|
|
2894
|
+
if (cls.startsWith("hover:text-")) return "Hover \u2014 text color";
|
|
2895
|
+
if (cls.startsWith("hover:bg-")) return "Hover \u2014 background color";
|
|
2896
|
+
if (cls.startsWith("focus:")) return "Focus";
|
|
2897
|
+
if (cls.startsWith("disabled:")) return "Disabled";
|
|
2898
|
+
if (/^(m|mx|my|mt|mb|ml|mr)-/.test(cls)) {
|
|
2899
|
+
return "Margin";
|
|
2900
|
+
}
|
|
2901
|
+
if (/^(p|px|py|pt|pb|pl|pr)-/.test(cls)) {
|
|
2902
|
+
return "Padding";
|
|
2903
|
+
}
|
|
2904
|
+
if (/^gap(-x|-y)?-/.test(cls)) {
|
|
2905
|
+
return "Gap";
|
|
2906
|
+
}
|
|
2907
|
+
if (/^(min-w|max-w|min-h|max-h)-/.test(cls)) {
|
|
2908
|
+
return "Sizing constraints";
|
|
2909
|
+
}
|
|
2910
|
+
if (/^(w|h)-/.test(cls)) {
|
|
2911
|
+
return "Width / Height";
|
|
2912
|
+
}
|
|
2913
|
+
if (/^text-(xs|sm|base|lg|xl|2xl)$/.test(cls)) {
|
|
2914
|
+
return "Text size";
|
|
2915
|
+
}
|
|
2916
|
+
if (/^text-([a-z]+)-(\d+)$/.test(cls)) {
|
|
2917
|
+
return "Text color";
|
|
2918
|
+
}
|
|
2919
|
+
if (/^font-/.test(cls)) {
|
|
2920
|
+
return "Font weight";
|
|
2921
|
+
}
|
|
2922
|
+
if (["uppercase", "lowercase", "capitalize", "normal-case"].includes(cls)) {
|
|
2923
|
+
return "Text transform";
|
|
2924
|
+
}
|
|
2925
|
+
if (/^tracking-/.test(cls)) {
|
|
2926
|
+
return "Letter spacing (tracking)";
|
|
2927
|
+
}
|
|
2928
|
+
if (/^leading-/.test(cls)) {
|
|
2929
|
+
return "Line height (leading)";
|
|
2930
|
+
}
|
|
2931
|
+
if (/^bg-([a-z]+)-(\d+)$/.test(cls)) {
|
|
2932
|
+
return "Background color";
|
|
2933
|
+
}
|
|
2934
|
+
if (/^border-([a-z]+)-(\d+)$/.test(cls) || cls === "border-transparent") {
|
|
2935
|
+
return "Border color";
|
|
2936
|
+
}
|
|
2937
|
+
if (/^(block|inline|inline-block|flex|inline-flex|grid|hidden)$/.test(cls)) {
|
|
2938
|
+
return "Display";
|
|
2939
|
+
}
|
|
2940
|
+
if (/^flex-/.test(cls)) {
|
|
2941
|
+
return "Flex";
|
|
2942
|
+
}
|
|
2943
|
+
if (/^justify-/.test(cls)) {
|
|
2944
|
+
return "Justify content";
|
|
2945
|
+
}
|
|
2946
|
+
if (/^items-/.test(cls)) {
|
|
2947
|
+
return "Align items";
|
|
2948
|
+
}
|
|
2949
|
+
if (/^self-/.test(cls)) {
|
|
2950
|
+
return "Align self";
|
|
2951
|
+
}
|
|
2952
|
+
if (/^aspect-/.test(cls)) {
|
|
2953
|
+
return "Aspect ratio";
|
|
2954
|
+
}
|
|
2955
|
+
if (/^opacity-/.test(cls)) {
|
|
2956
|
+
return "Opacity";
|
|
2957
|
+
}
|
|
2958
|
+
if (/^rounded(-|$)/.test(cls)) {
|
|
2959
|
+
return "Border radius";
|
|
2960
|
+
}
|
|
2961
|
+
if (/^border(-|$)/.test(cls) && !/^border-([a-z]+)-(\d+)$/.test(cls) && cls !== "border-transparent") {
|
|
2962
|
+
return "Border width";
|
|
2963
|
+
}
|
|
2964
|
+
if (/^cursor-/.test(cls)) {
|
|
2965
|
+
return "Cursor";
|
|
2966
|
+
}
|
|
2967
|
+
if (/^overflow(-|$)/.test(cls)) {
|
|
2968
|
+
return "Overflow";
|
|
2969
|
+
}
|
|
2970
|
+
if (/^transition(-|$)/.test(cls) || /^duration-/.test(cls) || /^ease-/.test(cls)) {
|
|
2971
|
+
return "Transition";
|
|
2972
|
+
}
|
|
2973
|
+
if (/^shadow(-|$)/.test(cls)) {
|
|
2974
|
+
return "Box shadow";
|
|
2975
|
+
}
|
|
2976
|
+
if (/^(relative|absolute|fixed|sticky|static)$/.test(cls)) {
|
|
2977
|
+
return "Position";
|
|
2978
|
+
}
|
|
2979
|
+
if (/^z-/.test(cls)) {
|
|
2980
|
+
return "Z-index";
|
|
2981
|
+
}
|
|
2982
|
+
return "Other";
|
|
2983
|
+
};
|
|
2984
|
+
var SECTIONS = [
|
|
2985
|
+
{
|
|
2986
|
+
title: "Utility families",
|
|
2987
|
+
families: [
|
|
2988
|
+
"Margin",
|
|
2989
|
+
"Padding",
|
|
2990
|
+
"Gap",
|
|
2991
|
+
"Width / Height",
|
|
2992
|
+
"Sizing constraints",
|
|
2993
|
+
"Aspect ratio",
|
|
2994
|
+
"Display",
|
|
2995
|
+
"Flex",
|
|
2996
|
+
"Justify content",
|
|
2997
|
+
"Align items",
|
|
2998
|
+
"Align self",
|
|
2999
|
+
"Position",
|
|
3000
|
+
"Z-index",
|
|
3001
|
+
"Overflow",
|
|
3002
|
+
"Text size",
|
|
3003
|
+
"Font weight",
|
|
3004
|
+
"Text transform",
|
|
3005
|
+
"Letter spacing (tracking)",
|
|
3006
|
+
"Line height (leading)",
|
|
3007
|
+
"Text color",
|
|
3008
|
+
"Background color",
|
|
3009
|
+
"Border color",
|
|
3010
|
+
"Border width",
|
|
3011
|
+
"Border radius",
|
|
3012
|
+
"Opacity",
|
|
3013
|
+
"Box shadow",
|
|
3014
|
+
"Cursor",
|
|
3015
|
+
"Transition",
|
|
3016
|
+
"Other"
|
|
3017
|
+
]
|
|
3018
|
+
},
|
|
3019
|
+
{
|
|
3020
|
+
title: "State variants",
|
|
3021
|
+
families: [
|
|
3022
|
+
"Focus",
|
|
3023
|
+
"Disabled",
|
|
3024
|
+
"Hover \u2014 opacity",
|
|
3025
|
+
"Hover \u2014 text color",
|
|
3026
|
+
"Hover \u2014 background color"
|
|
3027
|
+
]
|
|
3028
|
+
},
|
|
3029
|
+
{
|
|
3030
|
+
title: "Theme variants",
|
|
3031
|
+
families: [
|
|
3032
|
+
"Dark \u2014 text color",
|
|
3033
|
+
"Dark \u2014 background color",
|
|
3034
|
+
"Dark \u2014 border color",
|
|
3035
|
+
"Dark \u2014 hover text",
|
|
3036
|
+
"Dark \u2014 hover background"
|
|
3037
|
+
]
|
|
3038
|
+
}
|
|
3039
|
+
];
|
|
3040
|
+
var groupBySection = (classes) => {
|
|
3041
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3042
|
+
for (const cls of classes) {
|
|
3043
|
+
const family = classifyFamily(cls);
|
|
3044
|
+
const arr = buckets.get(family) ?? [];
|
|
3045
|
+
arr.push(cls);
|
|
3046
|
+
buckets.set(family, arr);
|
|
3047
|
+
}
|
|
3048
|
+
return SECTIONS.map((section) => ({
|
|
3049
|
+
title: section.title,
|
|
3050
|
+
families: section.families.filter((label) => buckets.has(label)).map((label) => ({
|
|
3051
|
+
label,
|
|
3052
|
+
classes: buckets.get(label).sort((a2, b2) => a2.localeCompare(b2, void 0, { numeric: true }))
|
|
3053
|
+
}))
|
|
3054
|
+
})).filter((s) => s.families.length > 0);
|
|
3055
|
+
};
|
|
3056
|
+
var renderFamilySection = (group) => {
|
|
3057
|
+
const lines = [];
|
|
3058
|
+
lines.push(`### ${group.label} (${group.classes.length})`);
|
|
3059
|
+
lines.push("");
|
|
3060
|
+
const formatted = group.classes.map((c) => `\`${c}\``).join(", ");
|
|
3061
|
+
lines.push(formatted);
|
|
3062
|
+
lines.push("");
|
|
3063
|
+
return lines.join("\n");
|
|
3064
|
+
};
|
|
3065
|
+
var generateTailwindUtilities = () => {
|
|
3066
|
+
const fm = frontmatter({
|
|
3067
|
+
root: false,
|
|
3068
|
+
targets: ["*"],
|
|
3069
|
+
description: "Tailwind utility classes pre-emitted in the embedded widget bundle that extensions can use without shipping their own CSS",
|
|
3070
|
+
globs: ["packages/extension/src/**/*.tsx"]
|
|
3071
|
+
});
|
|
3072
|
+
const css = SAFELIST_DIRECTIVES.map((d2) => `@source inline('${d2}');`).join("\n");
|
|
3073
|
+
const classes = parseSafelist(css);
|
|
3074
|
+
const sectionsData = groupBySection(classes);
|
|
3075
|
+
const familyCount = sectionsData.reduce((sum, s) => sum + s.families.length, 0);
|
|
3076
|
+
const sectionsMd = sectionsData.map((section) => {
|
|
3077
|
+
const familyBlocks = section.families.map(renderFamilySection).join("\n");
|
|
3078
|
+
return `## ${section.title}
|
|
3079
|
+
|
|
3080
|
+
${familyBlocks}`;
|
|
3081
|
+
}).join("\n");
|
|
3082
|
+
return `${fm}
|
|
3083
|
+
|
|
3084
|
+
# Supported Tailwind Utilities
|
|
3085
|
+
|
|
3086
|
+
Stackable extensions render inside the embedded widget's Shadow Root via the platform and ship **zero custom CSS by design** \u2014 they rely entirely on the platforms's bundled stylesheet for their visual styling. To keep that bundle small while still covering the common needs of extension UI, the platform pre-emits a curated subset of Tailwind v4 utility classes via a safelist. The classes listed below are guaranteed to be available in any extension that uses them.
|
|
3087
|
+
|
|
3088
|
+
Total: **${classes.length} utility classes** across **${familyCount} families**.
|
|
3089
|
+
|
|
3090
|
+
## Using utilities
|
|
3091
|
+
|
|
3092
|
+
Apply them via the \`className\` prop on \`ui.*\` components:
|
|
3093
|
+
|
|
3094
|
+
\`\`\`tsx
|
|
3095
|
+
<ui.Card className="mt-4 p-6">
|
|
3096
|
+
<ui.Stack className="gap-3">
|
|
3097
|
+
<ui.Text className="text-lg font-semibold uppercase tracking-wide">Title</ui.Text>
|
|
3098
|
+
<ui.Text className="text-zinc-600">Body copy</ui.Text>
|
|
3099
|
+
</ui.Stack>
|
|
3100
|
+
</ui.Card>
|
|
3101
|
+
\`\`\`
|
|
3102
|
+
|
|
3103
|
+
Always prefer:
|
|
3104
|
+
- Component variants (\`<ui.Button variant="primary">\`) over color overrides
|
|
3105
|
+
- Layout components (\`<ui.Stack>\`, \`<ui.Inline>\`) over manual flexbox class chains
|
|
3106
|
+
- The semantic color tokens (\`text-foreground\`, \`bg-background\`) which adapt to the host theme
|
|
3107
|
+
|
|
3108
|
+
## Safelist limits
|
|
3109
|
+
|
|
3110
|
+
The safelist is intentionally a fixed list, **NOT the full Tailwind catalog** to provide UI consistency and reduce bundle size. Anything beyond what is listed below will be missing from the platform stylesheet at runtime \u2014 the class will appear in your DOM but the corresponding CSS rule will not exist, and the style will silently no-op. In particular:
|
|
3111
|
+
|
|
3112
|
+
- **Arbitrary values are NOT supported** in general (e.g. \`w-[123px]\`, \`bg-[#1a1a1a]\`, \`text-[15px]\`). The single exception is \`aspect-[9/16]\` (portrait video). For any other arbitrary value, use the named utility closest to your target.
|
|
3113
|
+
- **Off-list color shades** (e.g. \`bg-cyan-500\`, \`text-rose-500\`) are not pre-emitted. The supported color set below is curated for visual consistency with the host theme.
|
|
3114
|
+
- **Off-list ladder values** (e.g. \`mt-32\`, \`text-4xl\`, \`w-128\`) are not pre-emitted. The ladders below cover sizes appropriate for the embedded widget's narrow viewport.
|
|
3115
|
+
|
|
3116
|
+
If you need something that's not on this list, file an issue with your use case \u2014 the safelist is curated, not frozen.
|
|
3117
|
+
|
|
3118
|
+
${sectionsMd}
|
|
3119
|
+
`;
|
|
3120
|
+
};
|
|
3121
|
+
|
|
2804
3122
|
// ../../sdk/extension/ai-docs/src/generators/store-and-navigation.ts
|
|
2805
3123
|
var generateStoreAndNavigation = () => {
|
|
2806
3124
|
const fm = frontmatter({
|
|
@@ -3740,6 +4058,12 @@ var SKILLS = [
|
|
|
3740
4058
|
type: "knowledge",
|
|
3741
4059
|
content: () => generateStylingAndTheming()
|
|
3742
4060
|
},
|
|
4061
|
+
{
|
|
4062
|
+
id: "tailwind-utilities",
|
|
4063
|
+
description: "Tailwind utility classes pre-emitted in the embedded widget bundle (margin, padding, sizing, color, layout, typography, state variants). Use when picking className values for extension UI \u2014 anything outside this list will silently no-op at runtime.",
|
|
4064
|
+
type: "knowledge",
|
|
4065
|
+
content: () => generateTailwindUtilities()
|
|
4066
|
+
},
|
|
3743
4067
|
{
|
|
3744
4068
|
id: "instance-settings",
|
|
3745
4069
|
description: "Instance Settings: declaring settingsSchema in your extension manifest, the install-time admin form, and reading regular values via useSettings() vs secure values via {{settings.x}} placeholders in data.fetch. Use when adding per-Instance configuration to an extension.",
|
|
@@ -3840,7 +4164,7 @@ var SKILLS = [
|
|
|
3840
4164
|
id: "deploy",
|
|
3841
4165
|
description: "Build the production bundle, host it on Netlify / Vercel / Cloudflare Pages / S3+CloudFront, and register your Bundle URL with Stackable via the admin dashboard or CLI. Use when shipping an extension after validation passes.",
|
|
3842
4166
|
type: "action",
|
|
3843
|
-
// TODO:
|
|
4167
|
+
// TODO: cli deploy — remove `scopes: ['docs']` once `cli deploy` is implemented so Studio's
|
|
3844
4168
|
// AI assistant in the future should be able invoke the deploy flow (not just describe it).
|
|
3845
4169
|
scopes: ["docs"],
|
|
3846
4170
|
content: () => generateDeployExtensionCommand()
|
package/dist/server.js
CHANGED
|
@@ -2794,6 +2794,324 @@ narrow widths:
|
|
|
2794
2794
|
`;
|
|
2795
2795
|
};
|
|
2796
2796
|
|
|
2797
|
+
// ../../sdk/extension/ai-docs/src/generators/tailwind-utilities.ts
|
|
2798
|
+
var SAFELIST_DIRECTIVES = [
|
|
2799
|
+
"m-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16}",
|
|
2800
|
+
"mx-{0,1,2,3,4,5,6,8,10,12,16,auto} my-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2801
|
+
"mt-{0,1,2,3,4,5,6,8,10,12,16} mb-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2802
|
+
"ml-{0,1,2,3,4,5,6,8,10,12,16,auto} mr-{0,1,2,3,4,5,6,8,10,12,16,auto}",
|
|
2803
|
+
"p-{0,0.5,1,1.5,2,2.5,3,3.5,4,5,6,7,8,9,10,11,12,14,16}",
|
|
2804
|
+
"px-{0,1,2,3,4,5,6,8,10,12,16} py-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2805
|
+
"pt-{0,1,2,3,4,5,6,8,10,12,16} pb-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2806
|
+
"pl-{0,1,2,3,4,5,6,8,10,12,16} pr-{0,1,2,3,4,5,6,8,10,12,16}",
|
|
2807
|
+
"gap-{0,1,2,3,4,5,6,8,10,12} gap-x-{0,1,2,3,4,5,6,8} gap-y-{0,1,2,3,4,5,6,8}",
|
|
2808
|
+
"text-{xs,sm,base,lg,xl,2xl}",
|
|
2809
|
+
"font-{light,normal,medium,semibold,bold,extrabold}",
|
|
2810
|
+
"uppercase lowercase capitalize normal-case",
|
|
2811
|
+
"tracking-{tight,normal,wide,wider,widest}",
|
|
2812
|
+
"leading-{none,tight,snug,normal,relaxed,loose}",
|
|
2813
|
+
"opacity-{0,5,10,20,25,30,40,50,60,70,75,80,90,95,100}",
|
|
2814
|
+
"block inline inline-block flex inline-flex grid hidden",
|
|
2815
|
+
"flex-{1,auto,initial,none,row,col,wrap,nowrap,row-reverse,col-reverse}",
|
|
2816
|
+
"justify-{start,end,center,between,around,evenly}",
|
|
2817
|
+
"items-{start,end,center,baseline,stretch}",
|
|
2818
|
+
"self-{auto,start,end,center,stretch,baseline}",
|
|
2819
|
+
"w-{0,full,auto,screen,1/2,1/3,2/3,1/4,3/4,1,2,3,4,5,6,8,10,12,16,20,24,32,48}",
|
|
2820
|
+
"h-{0,full,auto,screen,1/2,1/3,2/3,1/4,3/4,1,2,3,4,5,6,8,10,12,16,20,24,32,48}",
|
|
2821
|
+
"min-w-{0,full,fit,min,max} max-w-{none,xs,sm,md,lg,xl,2xl,3xl,4xl,5xl,full,fit}",
|
|
2822
|
+
"aspect-{auto,square,video} aspect-[9/16]",
|
|
2823
|
+
"rounded-{none,sm,md,lg,xl,2xl,3xl,full}",
|
|
2824
|
+
"border border-{0,2,4,8} border-{x,y,t,b,l,r}",
|
|
2825
|
+
"cursor-{pointer,default,not-allowed,wait,text,move,help}",
|
|
2826
|
+
"overflow-{auto,hidden,visible,scroll}",
|
|
2827
|
+
"overflow-x-{auto,hidden,scroll} overflow-y-{auto,hidden,scroll}",
|
|
2828
|
+
"transition transition-{none,all,colors,opacity,transform}",
|
|
2829
|
+
"duration-{75,100,150,200,300,500,700,1000} ease-{linear,in,out,in-out}",
|
|
2830
|
+
"shadow-{sm,md,lg,xl,2xl,none,inner}",
|
|
2831
|
+
"relative absolute fixed sticky static",
|
|
2832
|
+
"z-{0,10,20,30,40,50,auto}",
|
|
2833
|
+
"text-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2834
|
+
"text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2835
|
+
"bg-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2836
|
+
"bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2837
|
+
"border-{zinc,slate,gray}-{100,200,300,400} border-transparent",
|
|
2838
|
+
"hover:opacity-{50,75,80,90,100}",
|
|
2839
|
+
"hover:text-{zinc,slate,gray,neutral}-{500,600,700,800,900}",
|
|
2840
|
+
"hover:text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2841
|
+
"hover:bg-{zinc,slate,gray,neutral}-{50,100,200}",
|
|
2842
|
+
"hover:bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2843
|
+
"focus:outline-none focus:ring-2 focus:ring-offset-2",
|
|
2844
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
2845
|
+
"dark:text-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2846
|
+
"dark:text-{red,green,blue,amber,emerald,sky,indigo}-{500,600,700}",
|
|
2847
|
+
"dark:bg-{zinc,slate,gray,neutral}-{50,100,200,300,400,500,600,700,800,900}",
|
|
2848
|
+
"dark:bg-{red,green,blue,amber,emerald}-{50,100,500,600}",
|
|
2849
|
+
"dark:border-{zinc,slate,gray}-{600,700,800,900} dark:border-transparent",
|
|
2850
|
+
"dark:hover:text-{zinc,slate,gray}-{50,100,200,300,400}",
|
|
2851
|
+
"dark:hover:bg-{zinc,slate,gray}-{700,800,900}"
|
|
2852
|
+
];
|
|
2853
|
+
var INLINE_DIRECTIVE_RE = /@source\s+inline\(\s*'([^']+)'\s*\)\s*;/g;
|
|
2854
|
+
var expand = (token) => {
|
|
2855
|
+
const m = token.match(/\{([^{}]+)\}/);
|
|
2856
|
+
if (!m) {
|
|
2857
|
+
return [token];
|
|
2858
|
+
}
|
|
2859
|
+
const head = token.slice(0, m.index);
|
|
2860
|
+
const tail = token.slice(m.index + m[0].length);
|
|
2861
|
+
const out = [];
|
|
2862
|
+
for (const opt of m[1].split(",")) {
|
|
2863
|
+
out.push(...expand(head + opt + tail));
|
|
2864
|
+
}
|
|
2865
|
+
return out;
|
|
2866
|
+
};
|
|
2867
|
+
var stripComments = (css) => css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
2868
|
+
var parseSafelist = (css) => {
|
|
2869
|
+
const stripped = stripComments(css);
|
|
2870
|
+
const classes = [];
|
|
2871
|
+
for (const match of stripped.matchAll(INLINE_DIRECTIVE_RE)) {
|
|
2872
|
+
for (const tok of match[1].split(/\s+/)) {
|
|
2873
|
+
if (tok) {
|
|
2874
|
+
classes.push(...expand(tok));
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return classes;
|
|
2879
|
+
};
|
|
2880
|
+
var classifyFamily = (cls) => {
|
|
2881
|
+
if (cls.startsWith("dark:hover:text-")) return "Dark \u2014 hover text";
|
|
2882
|
+
if (cls.startsWith("dark:hover:bg-")) return "Dark \u2014 hover background";
|
|
2883
|
+
if (cls.startsWith("dark:text-")) return "Dark \u2014 text color";
|
|
2884
|
+
if (cls.startsWith("dark:bg-")) return "Dark \u2014 background color";
|
|
2885
|
+
if (cls.startsWith("dark:border-")) return "Dark \u2014 border color";
|
|
2886
|
+
if (cls.startsWith("hover:opacity-")) return "Hover \u2014 opacity";
|
|
2887
|
+
if (cls.startsWith("hover:text-")) return "Hover \u2014 text color";
|
|
2888
|
+
if (cls.startsWith("hover:bg-")) return "Hover \u2014 background color";
|
|
2889
|
+
if (cls.startsWith("focus:")) return "Focus";
|
|
2890
|
+
if (cls.startsWith("disabled:")) return "Disabled";
|
|
2891
|
+
if (/^(m|mx|my|mt|mb|ml|mr)-/.test(cls)) {
|
|
2892
|
+
return "Margin";
|
|
2893
|
+
}
|
|
2894
|
+
if (/^(p|px|py|pt|pb|pl|pr)-/.test(cls)) {
|
|
2895
|
+
return "Padding";
|
|
2896
|
+
}
|
|
2897
|
+
if (/^gap(-x|-y)?-/.test(cls)) {
|
|
2898
|
+
return "Gap";
|
|
2899
|
+
}
|
|
2900
|
+
if (/^(min-w|max-w|min-h|max-h)-/.test(cls)) {
|
|
2901
|
+
return "Sizing constraints";
|
|
2902
|
+
}
|
|
2903
|
+
if (/^(w|h)-/.test(cls)) {
|
|
2904
|
+
return "Width / Height";
|
|
2905
|
+
}
|
|
2906
|
+
if (/^text-(xs|sm|base|lg|xl|2xl)$/.test(cls)) {
|
|
2907
|
+
return "Text size";
|
|
2908
|
+
}
|
|
2909
|
+
if (/^text-([a-z]+)-(\d+)$/.test(cls)) {
|
|
2910
|
+
return "Text color";
|
|
2911
|
+
}
|
|
2912
|
+
if (/^font-/.test(cls)) {
|
|
2913
|
+
return "Font weight";
|
|
2914
|
+
}
|
|
2915
|
+
if (["uppercase", "lowercase", "capitalize", "normal-case"].includes(cls)) {
|
|
2916
|
+
return "Text transform";
|
|
2917
|
+
}
|
|
2918
|
+
if (/^tracking-/.test(cls)) {
|
|
2919
|
+
return "Letter spacing (tracking)";
|
|
2920
|
+
}
|
|
2921
|
+
if (/^leading-/.test(cls)) {
|
|
2922
|
+
return "Line height (leading)";
|
|
2923
|
+
}
|
|
2924
|
+
if (/^bg-([a-z]+)-(\d+)$/.test(cls)) {
|
|
2925
|
+
return "Background color";
|
|
2926
|
+
}
|
|
2927
|
+
if (/^border-([a-z]+)-(\d+)$/.test(cls) || cls === "border-transparent") {
|
|
2928
|
+
return "Border color";
|
|
2929
|
+
}
|
|
2930
|
+
if (/^(block|inline|inline-block|flex|inline-flex|grid|hidden)$/.test(cls)) {
|
|
2931
|
+
return "Display";
|
|
2932
|
+
}
|
|
2933
|
+
if (/^flex-/.test(cls)) {
|
|
2934
|
+
return "Flex";
|
|
2935
|
+
}
|
|
2936
|
+
if (/^justify-/.test(cls)) {
|
|
2937
|
+
return "Justify content";
|
|
2938
|
+
}
|
|
2939
|
+
if (/^items-/.test(cls)) {
|
|
2940
|
+
return "Align items";
|
|
2941
|
+
}
|
|
2942
|
+
if (/^self-/.test(cls)) {
|
|
2943
|
+
return "Align self";
|
|
2944
|
+
}
|
|
2945
|
+
if (/^aspect-/.test(cls)) {
|
|
2946
|
+
return "Aspect ratio";
|
|
2947
|
+
}
|
|
2948
|
+
if (/^opacity-/.test(cls)) {
|
|
2949
|
+
return "Opacity";
|
|
2950
|
+
}
|
|
2951
|
+
if (/^rounded(-|$)/.test(cls)) {
|
|
2952
|
+
return "Border radius";
|
|
2953
|
+
}
|
|
2954
|
+
if (/^border(-|$)/.test(cls) && !/^border-([a-z]+)-(\d+)$/.test(cls) && cls !== "border-transparent") {
|
|
2955
|
+
return "Border width";
|
|
2956
|
+
}
|
|
2957
|
+
if (/^cursor-/.test(cls)) {
|
|
2958
|
+
return "Cursor";
|
|
2959
|
+
}
|
|
2960
|
+
if (/^overflow(-|$)/.test(cls)) {
|
|
2961
|
+
return "Overflow";
|
|
2962
|
+
}
|
|
2963
|
+
if (/^transition(-|$)/.test(cls) || /^duration-/.test(cls) || /^ease-/.test(cls)) {
|
|
2964
|
+
return "Transition";
|
|
2965
|
+
}
|
|
2966
|
+
if (/^shadow(-|$)/.test(cls)) {
|
|
2967
|
+
return "Box shadow";
|
|
2968
|
+
}
|
|
2969
|
+
if (/^(relative|absolute|fixed|sticky|static)$/.test(cls)) {
|
|
2970
|
+
return "Position";
|
|
2971
|
+
}
|
|
2972
|
+
if (/^z-/.test(cls)) {
|
|
2973
|
+
return "Z-index";
|
|
2974
|
+
}
|
|
2975
|
+
return "Other";
|
|
2976
|
+
};
|
|
2977
|
+
var SECTIONS = [
|
|
2978
|
+
{
|
|
2979
|
+
title: "Utility families",
|
|
2980
|
+
families: [
|
|
2981
|
+
"Margin",
|
|
2982
|
+
"Padding",
|
|
2983
|
+
"Gap",
|
|
2984
|
+
"Width / Height",
|
|
2985
|
+
"Sizing constraints",
|
|
2986
|
+
"Aspect ratio",
|
|
2987
|
+
"Display",
|
|
2988
|
+
"Flex",
|
|
2989
|
+
"Justify content",
|
|
2990
|
+
"Align items",
|
|
2991
|
+
"Align self",
|
|
2992
|
+
"Position",
|
|
2993
|
+
"Z-index",
|
|
2994
|
+
"Overflow",
|
|
2995
|
+
"Text size",
|
|
2996
|
+
"Font weight",
|
|
2997
|
+
"Text transform",
|
|
2998
|
+
"Letter spacing (tracking)",
|
|
2999
|
+
"Line height (leading)",
|
|
3000
|
+
"Text color",
|
|
3001
|
+
"Background color",
|
|
3002
|
+
"Border color",
|
|
3003
|
+
"Border width",
|
|
3004
|
+
"Border radius",
|
|
3005
|
+
"Opacity",
|
|
3006
|
+
"Box shadow",
|
|
3007
|
+
"Cursor",
|
|
3008
|
+
"Transition",
|
|
3009
|
+
"Other"
|
|
3010
|
+
]
|
|
3011
|
+
},
|
|
3012
|
+
{
|
|
3013
|
+
title: "State variants",
|
|
3014
|
+
families: [
|
|
3015
|
+
"Focus",
|
|
3016
|
+
"Disabled",
|
|
3017
|
+
"Hover \u2014 opacity",
|
|
3018
|
+
"Hover \u2014 text color",
|
|
3019
|
+
"Hover \u2014 background color"
|
|
3020
|
+
]
|
|
3021
|
+
},
|
|
3022
|
+
{
|
|
3023
|
+
title: "Theme variants",
|
|
3024
|
+
families: [
|
|
3025
|
+
"Dark \u2014 text color",
|
|
3026
|
+
"Dark \u2014 background color",
|
|
3027
|
+
"Dark \u2014 border color",
|
|
3028
|
+
"Dark \u2014 hover text",
|
|
3029
|
+
"Dark \u2014 hover background"
|
|
3030
|
+
]
|
|
3031
|
+
}
|
|
3032
|
+
];
|
|
3033
|
+
var groupBySection = (classes) => {
|
|
3034
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3035
|
+
for (const cls of classes) {
|
|
3036
|
+
const family = classifyFamily(cls);
|
|
3037
|
+
const arr = buckets.get(family) ?? [];
|
|
3038
|
+
arr.push(cls);
|
|
3039
|
+
buckets.set(family, arr);
|
|
3040
|
+
}
|
|
3041
|
+
return SECTIONS.map((section) => ({
|
|
3042
|
+
title: section.title,
|
|
3043
|
+
families: section.families.filter((label) => buckets.has(label)).map((label) => ({
|
|
3044
|
+
label,
|
|
3045
|
+
classes: buckets.get(label).sort((a2, b2) => a2.localeCompare(b2, void 0, { numeric: true }))
|
|
3046
|
+
}))
|
|
3047
|
+
})).filter((s) => s.families.length > 0);
|
|
3048
|
+
};
|
|
3049
|
+
var renderFamilySection = (group) => {
|
|
3050
|
+
const lines = [];
|
|
3051
|
+
lines.push(`### ${group.label} (${group.classes.length})`);
|
|
3052
|
+
lines.push("");
|
|
3053
|
+
const formatted = group.classes.map((c) => `\`${c}\``).join(", ");
|
|
3054
|
+
lines.push(formatted);
|
|
3055
|
+
lines.push("");
|
|
3056
|
+
return lines.join("\n");
|
|
3057
|
+
};
|
|
3058
|
+
var generateTailwindUtilities = () => {
|
|
3059
|
+
const fm = frontmatter({
|
|
3060
|
+
root: false,
|
|
3061
|
+
targets: ["*"],
|
|
3062
|
+
description: "Tailwind utility classes pre-emitted in the embedded widget bundle that extensions can use without shipping their own CSS",
|
|
3063
|
+
globs: ["packages/extension/src/**/*.tsx"]
|
|
3064
|
+
});
|
|
3065
|
+
const css = SAFELIST_DIRECTIVES.map((d2) => `@source inline('${d2}');`).join("\n");
|
|
3066
|
+
const classes = parseSafelist(css);
|
|
3067
|
+
const sectionsData = groupBySection(classes);
|
|
3068
|
+
const familyCount = sectionsData.reduce((sum, s) => sum + s.families.length, 0);
|
|
3069
|
+
const sectionsMd = sectionsData.map((section) => {
|
|
3070
|
+
const familyBlocks = section.families.map(renderFamilySection).join("\n");
|
|
3071
|
+
return `## ${section.title}
|
|
3072
|
+
|
|
3073
|
+
${familyBlocks}`;
|
|
3074
|
+
}).join("\n");
|
|
3075
|
+
return `${fm}
|
|
3076
|
+
|
|
3077
|
+
# Supported Tailwind Utilities
|
|
3078
|
+
|
|
3079
|
+
Stackable extensions render inside the embedded widget's Shadow Root via the platform and ship **zero custom CSS by design** \u2014 they rely entirely on the platforms's bundled stylesheet for their visual styling. To keep that bundle small while still covering the common needs of extension UI, the platform pre-emits a curated subset of Tailwind v4 utility classes via a safelist. The classes listed below are guaranteed to be available in any extension that uses them.
|
|
3080
|
+
|
|
3081
|
+
Total: **${classes.length} utility classes** across **${familyCount} families**.
|
|
3082
|
+
|
|
3083
|
+
## Using utilities
|
|
3084
|
+
|
|
3085
|
+
Apply them via the \`className\` prop on \`ui.*\` components:
|
|
3086
|
+
|
|
3087
|
+
\`\`\`tsx
|
|
3088
|
+
<ui.Card className="mt-4 p-6">
|
|
3089
|
+
<ui.Stack className="gap-3">
|
|
3090
|
+
<ui.Text className="text-lg font-semibold uppercase tracking-wide">Title</ui.Text>
|
|
3091
|
+
<ui.Text className="text-zinc-600">Body copy</ui.Text>
|
|
3092
|
+
</ui.Stack>
|
|
3093
|
+
</ui.Card>
|
|
3094
|
+
\`\`\`
|
|
3095
|
+
|
|
3096
|
+
Always prefer:
|
|
3097
|
+
- Component variants (\`<ui.Button variant="primary">\`) over color overrides
|
|
3098
|
+
- Layout components (\`<ui.Stack>\`, \`<ui.Inline>\`) over manual flexbox class chains
|
|
3099
|
+
- The semantic color tokens (\`text-foreground\`, \`bg-background\`) which adapt to the host theme
|
|
3100
|
+
|
|
3101
|
+
## Safelist limits
|
|
3102
|
+
|
|
3103
|
+
The safelist is intentionally a fixed list, **NOT the full Tailwind catalog** to provide UI consistency and reduce bundle size. Anything beyond what is listed below will be missing from the platform stylesheet at runtime \u2014 the class will appear in your DOM but the corresponding CSS rule will not exist, and the style will silently no-op. In particular:
|
|
3104
|
+
|
|
3105
|
+
- **Arbitrary values are NOT supported** in general (e.g. \`w-[123px]\`, \`bg-[#1a1a1a]\`, \`text-[15px]\`). The single exception is \`aspect-[9/16]\` (portrait video). For any other arbitrary value, use the named utility closest to your target.
|
|
3106
|
+
- **Off-list color shades** (e.g. \`bg-cyan-500\`, \`text-rose-500\`) are not pre-emitted. The supported color set below is curated for visual consistency with the host theme.
|
|
3107
|
+
- **Off-list ladder values** (e.g. \`mt-32\`, \`text-4xl\`, \`w-128\`) are not pre-emitted. The ladders below cover sizes appropriate for the embedded widget's narrow viewport.
|
|
3108
|
+
|
|
3109
|
+
If you need something that's not on this list, file an issue with your use case \u2014 the safelist is curated, not frozen.
|
|
3110
|
+
|
|
3111
|
+
${sectionsMd}
|
|
3112
|
+
`;
|
|
3113
|
+
};
|
|
3114
|
+
|
|
2797
3115
|
// ../../sdk/extension/ai-docs/src/generators/store-and-navigation.ts
|
|
2798
3116
|
var generateStoreAndNavigation = () => {
|
|
2799
3117
|
const fm = frontmatter({
|
|
@@ -3733,6 +4051,12 @@ var SKILLS = [
|
|
|
3733
4051
|
type: "knowledge",
|
|
3734
4052
|
content: () => generateStylingAndTheming()
|
|
3735
4053
|
},
|
|
4054
|
+
{
|
|
4055
|
+
id: "tailwind-utilities",
|
|
4056
|
+
description: "Tailwind utility classes pre-emitted in the embedded widget bundle (margin, padding, sizing, color, layout, typography, state variants). Use when picking className values for extension UI \u2014 anything outside this list will silently no-op at runtime.",
|
|
4057
|
+
type: "knowledge",
|
|
4058
|
+
content: () => generateTailwindUtilities()
|
|
4059
|
+
},
|
|
3736
4060
|
{
|
|
3737
4061
|
id: "instance-settings",
|
|
3738
4062
|
description: "Instance Settings: declaring settingsSchema in your extension manifest, the install-time admin form, and reading regular values via useSettings() vs secure values via {{settings.x}} placeholders in data.fetch. Use when adding per-Instance configuration to an extension.",
|
|
@@ -3833,7 +4157,7 @@ var SKILLS = [
|
|
|
3833
4157
|
id: "deploy",
|
|
3834
4158
|
description: "Build the production bundle, host it on Netlify / Vercel / Cloudflare Pages / S3+CloudFront, and register your Bundle URL with Stackable via the admin dashboard or CLI. Use when shipping an extension after validation passes.",
|
|
3835
4159
|
type: "action",
|
|
3836
|
-
// TODO:
|
|
4160
|
+
// TODO: cli deploy — remove `scopes: ['docs']` once `cli deploy` is implemented so Studio's
|
|
3837
4161
|
// AI assistant in the future should be able invoke the deploy flow (not just describe it).
|
|
3838
4162
|
scopes: ["docs"],
|
|
3839
4163
|
content: () => generateDeployExtensionCommand()
|