@devalok/shilp-sutra 0.47.0 → 0.49.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/AGENTS.md +36 -12
- package/BREAKING.json +78 -0
- package/MIGRATION.md +891 -846
- package/dist/_chunks/badge-group.js +50 -63
- package/dist/_chunks/badge-group.js.map +1 -1
- package/dist/_chunks/chat.js +1 -1
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/emoji-suggestion.js +124 -0
- package/dist/_chunks/emoji-suggestion.js.map +1 -0
- package/dist/_chunks/emoji.js +1115 -0
- package/dist/_chunks/emoji.js.map +1 -0
- package/dist/_chunks/field-state.js +8 -0
- package/dist/_chunks/field-state.js.map +1 -0
- package/dist/_chunks/popover.js +50 -47
- package/dist/_chunks/popover.js.map +1 -1
- package/dist/_chunks/primitives.js +155 -155
- package/dist/_chunks/primitives.js.map +1 -1
- package/dist/_chunks/shared.js +4 -4
- package/dist/_chunks/tiptap.js +10 -10
- package/dist/_chunks/tiptap.js.map +1 -1
- package/dist/_chunks/use-calendar.js +1 -1
- package/dist/_chunks/use-calendar.js.map +1 -1
- package/dist/_chunks/use-controllable-open.js +15 -0
- package/dist/_chunks/use-controllable-open.js.map +1 -0
- package/dist/composed/command-palette.js +3 -3
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/confirm-dialog.js +3 -3
- package/dist/composed/confirm-dialog.js.map +1 -1
- package/dist/composed/date-picker/index.js +3 -3
- package/dist/composed/date-picker/index.js.map +1 -1
- package/dist/composed/emoji-picker.d.ts +19 -9
- package/dist/composed/emoji-picker.d.ts.map +1 -1
- package/dist/composed/emoji-picker.js +87 -68
- package/dist/composed/emoji-picker.js.map +1 -1
- package/dist/composed/error-boundary.js +1 -1
- package/dist/composed/error-boundary.js.map +1 -1
- package/dist/composed/extensions/emoji-data.d.ts +1 -14
- package/dist/composed/extensions/emoji-data.d.ts.map +1 -1
- package/dist/composed/extensions/emoji-node.d.ts +0 -3
- package/dist/composed/extensions/emoji-node.d.ts.map +1 -1
- package/dist/composed/extensions/emoji-node.js +9 -34
- package/dist/composed/extensions/emoji-node.js.map +1 -1
- package/dist/composed/extensions/emoji-suggestion.d.ts +2 -2
- package/dist/composed/extensions/emoji-suggestion.d.ts.map +1 -1
- package/dist/composed/extensions/emoji-suggestion.js +2 -116
- package/dist/composed/index.d.ts +3 -1
- package/dist/composed/index.d.ts.map +1 -1
- package/dist/composed/index.js +4 -3
- package/dist/composed/loading-skeleton.js +15 -15
- package/dist/composed/loading-skeleton.js.map +1 -1
- package/dist/composed/page-header.d.ts.map +1 -1
- package/dist/composed/page-header.js +32 -45
- package/dist/composed/page-header.js.map +1 -1
- package/dist/composed/page-skeletons.js +19 -19
- package/dist/composed/page-skeletons.js.map +1 -1
- package/dist/composed/responsive-modal.d.ts +39 -0
- package/dist/composed/responsive-modal.d.ts.map +1 -0
- package/dist/composed/responsive-modal.js +212 -0
- package/dist/composed/responsive-modal.js.map +1 -0
- package/dist/composed/rich-chat-input.d.ts.map +1 -1
- package/dist/composed/rich-chat-input.js +558 -579
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.d.ts.map +1 -1
- package/dist/composed/rich-text-editor.js +167 -190
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/composed/schedule-view.d.ts +1 -1
- package/dist/composed/schedule-view.d.ts.map +1 -1
- package/dist/composed/schedule-view.js +48 -54
- package/dist/composed/schedule-view.js.map +1 -1
- package/dist/composed/status-badge.d.ts +13 -11
- package/dist/composed/status-badge.d.ts.map +1 -1
- package/dist/composed/status-badge.js +54 -130
- package/dist/composed/status-badge.js.map +1 -1
- package/dist/shell/bottom-navbar.js +1 -1
- package/dist/shell/bottom-navbar.js.map +1 -1
- package/dist/shell/top-bar.js +1 -1
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/semantic.css +8 -1
- package/dist/ui/accordion.js +9 -9
- package/dist/ui/alert-dialog.js +26 -26
- package/dist/ui/aspect-ratio.js +1 -1
- package/dist/ui/autocomplete.js +7 -7
- package/dist/ui/avatar.d.ts.map +1 -1
- package/dist/ui/avatar.js +87 -82
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/badge.d.ts.map +1 -1
- package/dist/ui/breadcrumb.d.ts +0 -1
- package/dist/ui/breadcrumb.d.ts.map +1 -1
- package/dist/ui/breadcrumb.js +58 -35
- package/dist/ui/breadcrumb.js.map +1 -1
- package/dist/ui/button.js +3 -3
- package/dist/ui/checkbox.d.ts +4 -2
- package/dist/ui/checkbox.d.ts.map +1 -1
- package/dist/ui/checkbox.js +33 -28
- package/dist/ui/checkbox.js.map +1 -1
- package/dist/ui/collapsible.js +4 -4
- package/dist/ui/combobox.d.ts +3 -0
- package/dist/ui/combobox.d.ts.map +1 -1
- package/dist/ui/combobox.js +137 -132
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/context-menu.js +46 -46
- package/dist/ui/dialog.d.ts.map +1 -1
- package/dist/ui/dialog.js +53 -50
- package/dist/ui/dialog.js.map +1 -1
- package/dist/ui/dot.d.ts +38 -0
- package/dist/ui/dot.d.ts.map +1 -0
- package/dist/ui/dot.js +92 -0
- package/dist/ui/dot.js.map +1 -0
- package/dist/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/ui/dropdown-menu.js +70 -65
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/hover-card.js +11 -11
- package/dist/ui/index.d.ts +3 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +67 -67
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/input-otp.js +1 -1
- package/dist/ui/input-otp.js.map +1 -1
- package/dist/ui/input.d.ts +4 -2
- package/dist/ui/input.d.ts.map +1 -1
- package/dist/ui/input.js +38 -37
- package/dist/ui/input.js.map +1 -1
- package/dist/ui/label.js +1 -1
- package/dist/ui/lib/field-state.d.ts +21 -0
- package/dist/ui/lib/field-state.d.ts.map +1 -0
- package/dist/ui/lib/use-controllable-open.d.ts +32 -0
- package/dist/ui/lib/use-controllable-open.d.ts.map +1 -0
- package/dist/ui/link.js +1 -1
- package/dist/ui/number-input.d.ts +4 -2
- package/dist/ui/number-input.d.ts.map +1 -1
- package/dist/ui/number-input.js +51 -50
- package/dist/ui/number-input.js.map +1 -1
- package/dist/ui/pagination.js +1 -1
- package/dist/ui/popover.d.ts.map +1 -1
- package/dist/ui/progress.d.ts +66 -42
- package/dist/ui/progress.d.ts.map +1 -1
- package/dist/ui/progress.js +144 -40
- package/dist/ui/progress.js.map +1 -1
- package/dist/ui/radio.d.ts +6 -2
- package/dist/ui/radio.d.ts.map +1 -1
- package/dist/ui/radio.js +44 -33
- package/dist/ui/radio.js.map +1 -1
- package/dist/ui/select.d.ts +1 -1
- package/dist/ui/select.d.ts.map +1 -1
- package/dist/ui/select.js +58 -57
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/separator.js +2 -2
- package/dist/ui/separator.js.map +1 -1
- package/dist/ui/sheet.d.ts.map +1 -1
- package/dist/ui/sheet.js +59 -56
- package/dist/ui/sheet.js.map +1 -1
- package/dist/ui/sidebar.js +5 -5
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/slider.js +6 -6
- package/dist/ui/slider.js.map +1 -1
- package/dist/ui/stat-card.d.ts.map +1 -1
- package/dist/ui/stat-card.js +122 -125
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/surface.js +1 -1
- package/dist/ui/switch.d.ts +3 -1
- package/dist/ui/switch.d.ts.map +1 -1
- package/dist/ui/switch.js +32 -27
- package/dist/ui/switch.js.map +1 -1
- package/dist/ui/textarea.d.ts +3 -3
- package/dist/ui/textarea.d.ts.map +1 -1
- package/dist/ui/textarea.js +18 -17
- package/dist/ui/textarea.js.map +1 -1
- package/dist/ui/toast.js +2 -2
- package/dist/ui/toast.js.map +1 -1
- package/dist/ui/toggle-group.js +1 -1
- package/dist/ui/toggle.js +1 -1
- package/dist/ui/tooltip.d.ts.map +1 -1
- package/dist/ui/tooltip.js +36 -33
- package/dist/ui/tooltip.js.map +1 -1
- package/docs/components/ai/ai-command-provider.md +46 -0
- package/docs/components/ai/block-renderer.md +59 -0
- package/docs/components/ai/command-bar.md +78 -0
- package/docs/components/ai/conversation.md +59 -0
- package/docs/components/ai/devadoot-icon.md +41 -0
- package/docs/components/composed/emoji-picker.md +19 -17
- package/docs/components/composed/page-header.md +1 -1
- package/docs/components/composed/responsive-modal.md +66 -0
- package/docs/components/composed/rich-text-editor.md +1 -1
- package/docs/components/composed/schedule-view.md +6 -2
- package/docs/components/ui/breadcrumb.md +1 -1
- package/docs/components/ui/checkbox.md +4 -1
- package/docs/components/ui/combobox.md +4 -0
- package/docs/components/ui/dot.md +61 -0
- package/docs/components/ui/progress.md +54 -16
- package/docs/components/ui/radio.md +6 -2
- package/docs/components/ui/select.md +9 -5
- package/docs/components/ui/switch.md +4 -1
- package/docs/components/ui/table-row-link.md +1 -1
- package/docs/components/ui/table.md +2 -1
- package/docs/recipes/index.md +1 -1
- package/docs/recipes/install-astro.md +15 -12
- package/docs/recipes/install-next-app-router.md +20 -12
- package/docs/recipes/install-next-pages.md +3 -1
- package/docs/recipes/install-remix.md +16 -13
- package/docs/recipes/install-tanstack-start.md +19 -12
- package/docs/recipes/install-vite.md +18 -15
- package/docs/recipes/troubleshoot.md +7 -8
- package/llms.txt +12 -4
- package/make-kit/components/form.md +2 -2
- package/make-kit/components/icon.md +2 -2
- package/make-kit/components/overview.md +2 -2
- package/make-kit/components/select.md +1 -1
- package/make-kit/components/table.md +1 -1
- package/mcp-manifest.json +2696 -1145
- package/mcp-manifest.schema.json +18 -0
- package/package.json +16 -14
- package/skill/SKILL.md +1 -1
- package/skill/references/components.md +12 -4
- package/skill/references/setup-astro.md +15 -12
- package/skill/references/setup-next-app-router.md +20 -12
- package/skill/references/setup-next-pages.md +3 -1
- package/skill/references/setup-remix.md +16 -13
- package/skill/references/setup-tanstack-start.md +19 -12
- package/skill/references/setup-vite.md +18 -15
- package/skill/references/troubleshoot.md +7 -8
- package/dist/_chunks/emoji-data.js +0 -45
- package/dist/_chunks/emoji-data.js.map +0 -1
- package/dist/composed/extensions/emoji-suggestion.js.map +0 -1
- package/dist/ui/status-dot.d.ts +0 -27
- package/dist/ui/status-dot.d.ts.map +0 -1
- package/dist/ui/status-dot.js +0 -68
- package/dist/ui/status-dot.js.map +0 -1
- package/docs/components/ui/status-dot.md +0 -35
package/dist/ui/tooltip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const
|
|
1
|
+
{"version":3,"file":"tooltip.js","names":[],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["'use client'\n\nimport * as TooltipPrimitive from '@primitives/react-tooltip'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { springs, tweens } from './lib/motion'\nimport { useControllableOpen } from './lib/use-controllable-open'\nimport { cn } from './lib/utils'\n\n// ── Auto-provider: wraps with TooltipPrimitive.Provider if none exists ──\n\nconst TooltipProviderContext = React.createContext(false)\n\nconst TooltipProvider: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider>> = ({\n children,\n ...props\n}) => (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider {...props}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n)\n\nfunction AutoProvider({ children }: { children: React.ReactNode }) {\n const hasProvider = React.useContext(TooltipProviderContext)\n if (hasProvider) return <>{children}</>\n return (\n <TooltipProviderContext.Provider value={true}>\n <TooltipPrimitive.Provider delayDuration={300}>\n {children}\n </TooltipPrimitive.Provider>\n </TooltipProviderContext.Provider>\n )\n}\n\n// ── Internal context to thread `open` state to animated children ──\n\ntype TooltipContextValue = { open: boolean }\nconst TooltipContext = React.createContext<TooltipContextValue>({ open: false })\n\nconst Tooltip: React.FC<React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Root>> = ({\n open: openProp,\n defaultOpen,\n onOpenChange,\n ...props\n}) => {\n const { open, setOpen } = useControllableOpen({ open: openProp, defaultOpen, onOpenChange })\n\n const contextValue = React.useMemo(() => ({ open }), [open])\n\n return (\n <AutoProvider>\n <TooltipContext.Provider value={contextValue}>\n <TooltipPrimitive.Root open={open} onOpenChange={setOpen} {...props} />\n </TooltipContext.Provider>\n </AutoProvider>\n )\n}\nTooltip.displayName = 'Tooltip'\n\nconst TooltipTrigger = TooltipPrimitive.Trigger\n\n// ── Side-based slide offsets ──\n\nconst sideOffset: Record<string, { x?: number; y?: number }> = {\n top: { y: 4 },\n bottom: { y: -4 },\n left: { x: 4 },\n right: { x: -4 },\n}\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset: sideOffsetProp = 4, side = 'top', children, ...props }, ref) => {\n const { open } = React.useContext(TooltipContext)\n const slideInit = sideOffset[side] ?? {}\n\n return (\n <AnimatePresence>\n {open && (\n <TooltipPrimitive.Portal forceMount>\n <TooltipPrimitive.Content\n ref={ref}\n forceMount\n sideOffset={sideOffsetProp}\n side={side}\n {...props}\n asChild\n >\n <motion.div\n initial={{ opacity: 0, scale: 0.95, ...slideInit }}\n animate={{ opacity: 1, scale: 1, x: 0, y: 0 }}\n exit={{ opacity: 0, scale: 0.95, ...slideInit }}\n transition={{ ...springs.snappy, opacity: tweens.fade }}\n className={cn(\n 'z-tooltip overflow-hidden rounded-overlay-sm bg-surface-inverted px-ds-04 py-ds-02b text-ds-sm text-surface-inverted-fg shadow-floating',\n className,\n )}\n >\n {children}\n </motion.div>\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )}\n </AnimatePresence>\n )\n})\nTooltipContent.displayName = TooltipPrimitive.Content.displayName\n\nexport type TooltipContentProps = React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n\nexport { Tooltip, TooltipContent, TooltipProvider,TooltipTrigger }\n"],"mappings":";;;;;;;;;AAYA,IAAM,IAAyB,EAAM,cAAc,GAAM,EAEnD,KAA+F,EACnG,aACA,GAAG,QAEH,kBAAC,EAAuB,UAAxB;CAAiC,OAAO;WACtC,kBAAC,GAAD;EAA2B,GAAI;EAC5B;EACyB,CAAA;CACI,CAAA;AAGpC,SAAS,EAAa,EAAE,eAA2C;AAGjE,QAFoB,EAAM,WAAW,EAAuB,GACpC,kBAAA,GAAA,EAAG,aAAY,CAAA,GAErC,kBAAC,EAAuB,UAAxB;EAAiC,OAAO;YACtC,kBAAC,GAAD;GAA2B,eAAe;GACvC;GACyB,CAAA;EACI,CAAA;;AAOtC,IAAM,IAAiB,EAAM,cAAmC,EAAE,MAAM,IAAO,CAAC,EAE1E,KAAmF,EACvF,MAAM,GACN,gBACA,iBACA,GAAG,QACC;CACJ,IAAM,EAAE,SAAM,eAAY,EAAoB;EAAE,MAAM;EAAU;EAAa;EAAc,CAAC,EAEtF,IAAe,EAAM,eAAe,EAAE,SAAM,GAAG,CAAC,EAAK,CAAC;AAE5D,QACE,kBAAC,GAAD,EAAA,UACE,kBAAC,EAAe,UAAhB;EAAyB,OAAO;YAC9B,kBAAC,GAAD;GAA6B;GAAM,cAAc;GAAS,GAAI;GAAS,CAAA;EAC/C,CAAA,EACb,CAAA;;AAGnB,EAAQ,cAAc;AAEtB,IAAM,IAAiB,GAIjB,IAAyD;CAC7D,KAAK,EAAE,GAAG,GAAG;CACb,QAAQ,EAAE,GAAG,IAAI;CACjB,MAAM,EAAE,GAAG,GAAG;CACd,OAAO,EAAE,GAAG,IAAI;CACjB,EAEK,IAAiB,EAAM,YAG1B,EAAE,cAAW,YAAY,IAAiB,GAAG,UAAO,OAAO,aAAU,GAAG,KAAS,MAAQ;CAC1F,IAAM,EAAE,YAAS,EAAM,WAAW,EAAe,EAC3C,IAAY,EAAW,MAAS,EAAE;AAExC,QACE,kBAAC,GAAD,EAAA,UACG,KACC,kBAAC,GAAD;EAAyB,YAAA;YACvB,kBAAC,GAAD;GACO;GACL,YAAA;GACA,YAAY;GACN;GACN,GAAI;GACJ,SAAA;aAEA,kBAAC,EAAO,KAAR;IACE,SAAS;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAClD,SAAS;KAAE,SAAS;KAAG,OAAO;KAAG,GAAG;KAAG,GAAG;KAAG;IAC7C,MAAM;KAAE,SAAS;KAAG,OAAO;KAAM,GAAG;KAAW;IAC/C,YAAY;KAAE,GAAG,EAAQ;KAAQ,SAAS,EAAO;KAAM;IACvD,WAAW,EACT,2IACA,EACD;IAEA;IACU,CAAA;GACY,CAAA;EACH,CAAA,EAEZ,CAAA;EAEpB;AACF,EAAe,cAAA,EAAuC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# AICommandProvider
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ai/ai-command-provider
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ai
|
|
6
|
+
|
|
7
|
+
Context provider for the AI command system. Supplies custom block renderers, the
|
|
8
|
+
action handler, and the agent identity to `AIConversation` / `BlockRenderer` below
|
|
9
|
+
it, so you configure them once instead of threading props through every message.
|
|
10
|
+
Ships a `useAICommand()` hook to read the context.
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
children: ReactNode (required)
|
|
14
|
+
customBlocks: Record<string, ComponentType<BlockComponentProps>> — custom block-type renderers available to all descendants
|
|
15
|
+
onAction: (actionId: string, type: "confirm" | "cancel" | "undo") => void — default action handler for descendant blocks
|
|
16
|
+
agent: { name: string, icon?: IconInput } — default agent identity for assistant messages
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
```jsx
|
|
20
|
+
<AICommandProvider
|
|
21
|
+
agent={{ name: 'Devadoot', icon: <IconSparkles /> }}
|
|
22
|
+
customBlocks={{ chart: ChartBlock }}
|
|
23
|
+
onAction={(id, type) => handleAction(id, type)}
|
|
24
|
+
>
|
|
25
|
+
<AIConversation messages={messages} />
|
|
26
|
+
</AICommandProvider>
|
|
27
|
+
|
|
28
|
+
// Read the context in a descendant:
|
|
29
|
+
function MyBlock() {
|
|
30
|
+
const ctx = useAICommand() // { customBlocks, onAction, agent } | null
|
|
31
|
+
// ...
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Composability
|
|
36
|
+
- **Configure once** — set `customBlocks`, `onAction`, and `agent` here; `AIConversation` and `BlockRenderer` read them via context. Per-component props still win over the context (prop overrides provider).
|
|
37
|
+
- **`useAICommand()`** — returns the context (`{ customBlocks, onAction, agent }`) or `null` when no provider is mounted. Custom blocks can use it to reach the shared action handler.
|
|
38
|
+
- **Optional** — `AIConversation`/`BlockRenderer` work without a provider if you pass their props directly; the provider is a convenience for app-wide config.
|
|
39
|
+
|
|
40
|
+
## Gotchas
|
|
41
|
+
- Client component — mount inside a client boundary.
|
|
42
|
+
- `useAICommand()` returns `null` (not a throw) when there's no provider — guard for it in custom blocks.
|
|
43
|
+
|
|
44
|
+
## Changes
|
|
45
|
+
### v0.49.0
|
|
46
|
+
- **Added** Component documentation (the AI layer is now covered by the docs gate + hosted MCP).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# BlockRenderer
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ai/block-renderer
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ai
|
|
6
|
+
|
|
7
|
+
Renders an ordered list of typed AI "blocks" — the structured chunks an assistant/
|
|
8
|
+
server emits instead of plain text. A registry maps each block's `type` to a React
|
|
9
|
+
component; consumers can register their own types. This doc also covers the block
|
|
10
|
+
system and how to author a custom block.
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
blocks: Block[] (required) — ordered blocks to render
|
|
14
|
+
onAction: (actionId: string, type: "confirm" | "cancel" | "undo") => void — fired by interactive blocks (e.g. confirm)
|
|
15
|
+
customBlocks: Record<string, ComponentType<BlockComponentProps>> — extra type→component renderers, merged OVER the built-ins (prop wins over context)
|
|
16
|
+
staggerDelay: number — ms between each block's entrance animation (default 50)
|
|
17
|
+
className: string
|
|
18
|
+
|
|
19
|
+
Block: { id?: string, type: string, data: Record<string, unknown>, confidence?: "high" | "medium" | "low" } (see ai/types)
|
|
20
|
+
BlockComponentProps<T>: { data: T, blockId?: string, confidence?: "high" | "medium" | "low", onAction?: (actionId, type) => void }
|
|
21
|
+
|
|
22
|
+
## Defaults
|
|
23
|
+
staggerDelay defaults to 50
|
|
24
|
+
|
|
25
|
+
## Example
|
|
26
|
+
```jsx
|
|
27
|
+
// Render server-emitted blocks:
|
|
28
|
+
<BlockRenderer
|
|
29
|
+
blocks={message.blocks}
|
|
30
|
+
onAction={(id, type) => handleAction(id, type)}
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
// Register a custom block type:
|
|
34
|
+
function ChartBlock({ data }) {
|
|
35
|
+
return <MyChart series={data.series} />
|
|
36
|
+
}
|
|
37
|
+
<BlockRenderer
|
|
38
|
+
blocks={message.blocks}
|
|
39
|
+
customBlocks={{ chart: ChartBlock }}
|
|
40
|
+
/>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Composability
|
|
44
|
+
- **The block system** — the AI conversation renders assistant output as a `Block[]`. Each block has a `type` (string) and a `data` payload. `BlockRenderer` looks up the type in its registry and renders the matching component; an unknown type falls back to an `Alert` showing the raw JSON (so a new server-emitted type degrades gracefully instead of crashing).
|
|
45
|
+
- **Built-in block types** (9): `text` (Markdown prose), `table` (tabular data), `confirm` (action confirmation — confirm/cancel buttons + a "Why this action?" rationale drawer), `success` / `error` / `info` (status messages), `loading` (pending indicator), `divider` (separator), `stat_row` (a row of metric cards, composes `StatCard`).
|
|
46
|
+
- **Confidence** — every block may carry `confidence: "high" | "medium" | "low"`. Low-confidence blocks get a faint warning wash + a "Low confidence" chip (owned by the shared `BlockShell` wrapper, so the treatment is consistent across all block types). This is an AI-native affordance: the UI signals how sure the model is.
|
|
47
|
+
- **Writing a custom block** — a block component receives `BlockComponentProps<T>` (`data`, `confidence`, `onAction`, `blockId`). Wrap your content in `BlockShell` to inherit the confidence treatment. Register it via `customBlocks` (on `BlockRenderer` or `AICommandProvider`) keyed by the `type` string your server emits.
|
|
48
|
+
- **Two public block subpaths** — `text` and `error` import `react-markdown` + `remark-gfm` (a Markdown peer dependency). They were split out of the `ai/blocks` barrel in 0.40.0 so consumers don't pull those peers unless they render Markdown. Import them directly when needed: `@devalok/shilp-sutra/ai/blocks/text`, `@devalok/shilp-sutra/ai/blocks/error`.
|
|
49
|
+
- **Provider merge** — `customBlocks` from `AICommandProvider` (context) are merged with the `customBlocks` prop; the prop wins on key collision. Same for `onAction`.
|
|
50
|
+
|
|
51
|
+
## Gotchas
|
|
52
|
+
- Client component — mount inside a client boundary.
|
|
53
|
+
- Requires a mounted `MotionProvider` (block entrances respect reduced-motion).
|
|
54
|
+
- Rendering `text`/`error` blocks requires the `react-markdown` + `remark-gfm` peer dependencies — install them if your blocks include Markdown.
|
|
55
|
+
- The `ai/blocks/*` components are an implementation detail of the registry; consume blocks via `BlockRenderer`/`AIConversation`, not by importing each block (except the two documented Markdown subpaths).
|
|
56
|
+
|
|
57
|
+
## Changes
|
|
58
|
+
### v0.49.0
|
|
59
|
+
- **Added** Component + block-system documentation (the AI layer is now covered by the docs gate + hosted MCP).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# CommandBar
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ai/command-bar
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ai
|
|
6
|
+
|
|
7
|
+
A unified AI command input with three layout variants. Supports natural-language
|
|
8
|
+
submission (`onSubmit`) and, optionally, command-palette filtering (`groups`) in the
|
|
9
|
+
same bar. Shows an animated brand-gradient border while processing.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
onSubmit: (query: string) => void — called on Enter (natural-language submission)
|
|
13
|
+
state: "idle" | "typing" | "processing" | "responded" — drives visual feedback (gradient border, placeholder)
|
|
14
|
+
groups: CommandGroup[] — when provided, enables command-palette filtering alongside AI submission
|
|
15
|
+
onSearch: (query: string) => void — fired as the query changes (palette mode)
|
|
16
|
+
emptyMessage: string — text when a palette search yields nothing
|
|
17
|
+
emptyState: ReactNode — replaces the default empty UI
|
|
18
|
+
variant: "hero" | "inline" | "floating" — layout (default "hero")
|
|
19
|
+
placeholder: string | string[] — static text, or an array that rotates on an interval
|
|
20
|
+
placeholderInterval: number — rotation interval in ms when placeholder is an array (default 4000)
|
|
21
|
+
greeting: string — text above the input (hero variant)
|
|
22
|
+
hints: string[] — hint strings below the input (hero variant)
|
|
23
|
+
agentName: string
|
|
24
|
+
agentIcon: ReactNode
|
|
25
|
+
open: boolean — controlled open state (floating variant)
|
|
26
|
+
defaultOpen: boolean — uncontrolled initial open (floating variant)
|
|
27
|
+
onOpenChange: (open: boolean) => void — floating variant
|
|
28
|
+
keybinding: string | string[] | false — global toggle for the floating bar (default "mod+j")
|
|
29
|
+
disabled: boolean
|
|
30
|
+
maxHeight: string | number — cap on the results/scroll region (default "320px")
|
|
31
|
+
children: ReactNode
|
|
32
|
+
|
|
33
|
+
CommandGroup / CommandItem: shared with composed/command-palette (see that doc)
|
|
34
|
+
|
|
35
|
+
## Defaults
|
|
36
|
+
variant defaults to "hero"
|
|
37
|
+
placeholderInterval defaults to 4000
|
|
38
|
+
keybinding defaults to "mod+j"
|
|
39
|
+
maxHeight defaults to "320px"
|
|
40
|
+
|
|
41
|
+
## Example
|
|
42
|
+
```jsx
|
|
43
|
+
// Hero variant — the primary AI entry point on a dashboard:
|
|
44
|
+
<CommandBar
|
|
45
|
+
variant="hero"
|
|
46
|
+
greeting="What can I help with?"
|
|
47
|
+
placeholder={['Summarize this project…', 'Draft a status update…', 'Find overdue tasks…']}
|
|
48
|
+
hints={['⌘J to open anywhere', 'Ask in plain English']}
|
|
49
|
+
state={aiState}
|
|
50
|
+
onSubmit={(q) => runAgent(q)}
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
// Floating variant — global ⌘J overlay, palette + AI in one:
|
|
54
|
+
<CommandBar
|
|
55
|
+
variant="floating"
|
|
56
|
+
keybinding="mod+j"
|
|
57
|
+
groups={commandGroups}
|
|
58
|
+
onSearch={setQuery}
|
|
59
|
+
onSubmit={(q) => runAgent(q)}
|
|
60
|
+
/>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Composability
|
|
64
|
+
- **Two modes in one bar** — pass `onSubmit` for natural-language AI; add `groups` to also get command-palette filtering (arrow-key navigation, grouped items). Reuses `CommandGroup`/`CommandItem` from composed/command-palette.
|
|
65
|
+
- **`floating` reuses the Dialog primitive** (`DialogContentRaw`) with a global keybinding — same overlay pattern as CommandPalette, so focus-trap/Escape/scroll-lock come for free.
|
|
66
|
+
- **State-driven feedback** — set `state="processing"` to show the animated brand-gradient border; `"responded"` to settle it. Wire `state` to your agent's lifecycle.
|
|
67
|
+
- **Placeholder rotation** — pass an array to `placeholder` for the rotating-prompt effect (paused while typing; respects reduced-motion).
|
|
68
|
+
- **Pairs with `AIConversation`** — CommandBar is the input; AIConversation renders the response thread.
|
|
69
|
+
|
|
70
|
+
## Gotchas
|
|
71
|
+
- Client component — mount inside a client boundary.
|
|
72
|
+
- Requires a mounted `MotionProvider`.
|
|
73
|
+
- `keybinding` only applies to the `floating` variant; pass `false` to disable the global shortcut.
|
|
74
|
+
- Largest component in the AI layer (3 variants share one implementation) — prefer `inline`/`hero` for embedded bars, `floating` for the app-wide launcher.
|
|
75
|
+
|
|
76
|
+
## Changes
|
|
77
|
+
### v0.49.0
|
|
78
|
+
- **Added** Component documentation (the AI layer is now covered by the docs gate + hosted MCP).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# AIConversation
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ai/conversation
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ai
|
|
6
|
+
|
|
7
|
+
A scrollable AI conversation thread: user + assistant turns, streaming processing
|
|
8
|
+
steps, an agent header, and structured content blocks (rendered via BlockRenderer)
|
|
9
|
+
with confirm/cancel/undo action support.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
messages: ConversationMessage[] (required) — ordered user + assistant turns
|
|
13
|
+
isProcessing: boolean — shows a live processing indicator below the last message
|
|
14
|
+
processingSteps: ProcessingStep[] — granular steps shown during processing (status: pending | active | done | error)
|
|
15
|
+
agent: { name: string, icon?: IconInput } — agent identity shown as a header on assistant messages
|
|
16
|
+
onAction: (actionId: string, type: "confirm" | "cancel" | "undo") => void — fired by action blocks
|
|
17
|
+
customBlocks: Record<string, ComponentType<BlockComponentProps>> — extra block-type renderers merged over the built-ins
|
|
18
|
+
maxHeight: string | number — max height of the container (enables scrolling)
|
|
19
|
+
autoScroll: boolean — auto-scroll to bottom on new messages (default true)
|
|
20
|
+
className: string
|
|
21
|
+
|
|
22
|
+
ConversationMessage: { id: string, role: "user" | "assistant", blocks: Block[], ... } (see ai/types)
|
|
23
|
+
ProcessingStep: { label: string, status: "pending" | "active" | "done" | "error" }
|
|
24
|
+
|
|
25
|
+
## Defaults
|
|
26
|
+
autoScroll defaults to true
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
```jsx
|
|
30
|
+
<AICommandProvider agent={{ name: 'Devadoot' }} onAction={handleAction}>
|
|
31
|
+
<AIConversation
|
|
32
|
+
messages={messages}
|
|
33
|
+
isProcessing={isThinking}
|
|
34
|
+
processingSteps={[
|
|
35
|
+
{ label: 'Reading project', status: 'done' },
|
|
36
|
+
{ label: 'Drafting reply', status: 'active' },
|
|
37
|
+
]}
|
|
38
|
+
agent={{ name: 'Devadoot', icon: <IconSparkles /> }}
|
|
39
|
+
onAction={(id, type) => handleAction(id, type)}
|
|
40
|
+
maxHeight="60vh"
|
|
41
|
+
/>
|
|
42
|
+
</AICommandProvider>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Composability
|
|
46
|
+
- **Renders blocks, not raw text** — each assistant turn is a list of typed `Block`s rendered by `BlockRenderer`. See the block-renderer doc for the block system and how to register custom block types.
|
|
47
|
+
- **Pairs with `AICommandProvider`** — the provider supplies `customBlocks`, `onAction`, and `agent` via context so you don't thread them through every message. Props on `AIConversation` win over context.
|
|
48
|
+
- **Pairs with `CommandBar`** — CommandBar is the input surface (natural-language submission); AIConversation is the output thread. Common layout: CommandBar on top/bottom, AIConversation as the scroll region.
|
|
49
|
+
- **Streaming** — set `isProcessing` + feed `processingSteps` to show step-by-step progress (Reading → Drafting → …) while the model works.
|
|
50
|
+
- **Actions** — action blocks (e.g. `confirm`) call `onAction(actionId, type)`; wire it to your confirm/cancel/undo handlers.
|
|
51
|
+
|
|
52
|
+
## Gotchas
|
|
53
|
+
- Client component — mount inside a client boundary.
|
|
54
|
+
- Requires a mounted `MotionProvider` (respects reduced-motion for the step spinner and block entrances).
|
|
55
|
+
- `agent` set here labels assistant messages; `AICommandProvider`'s `agent` is the fallback when not passed per-conversation.
|
|
56
|
+
|
|
57
|
+
## Changes
|
|
58
|
+
### v0.49.0
|
|
59
|
+
- **Added** Component documentation (the AI layer is now covered by the docs gate + hosted MCP).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# DevadootIcon
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ai
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ai
|
|
6
|
+
|
|
7
|
+
The animated Devalok chakra glyph — the visual identity for the AI command system.
|
|
8
|
+
Uses fill-first animation (the gradient color *is* the animation, Gemini-style),
|
|
9
|
+
with a distinct state for idle, processing, responded, and error.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
state: "idle" | "processing" | "responded" | "error" — animation mode (default "idle")
|
|
13
|
+
size: number — pixel size (default 20)
|
|
14
|
+
className: string
|
|
15
|
+
|
|
16
|
+
## Defaults
|
|
17
|
+
state defaults to "idle"
|
|
18
|
+
size defaults to 20
|
|
19
|
+
|
|
20
|
+
## Example
|
|
21
|
+
```jsx
|
|
22
|
+
// Drive the icon from the agent lifecycle:
|
|
23
|
+
<DevadootIcon state={isThinking ? 'processing' : hasReplied ? 'responded' : 'idle'} size={24} />
|
|
24
|
+
|
|
25
|
+
// Error state:
|
|
26
|
+
<DevadootIcon state="error" />
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Composability
|
|
30
|
+
- **State-as-identity** — `idle` breathes pink↔rose slowly; `processing` sweeps a pink→purple→magenta gradient + glow; `responded` flashes bright then settles + a scale pop; `error` turns red with a glow pulse. Wire `state` to your agent's status.
|
|
31
|
+
- **Pairs with `CommandBar` / `AIConversation`** — commonly placed in the CommandBar (hero/floating) or as the agent header avatar in the conversation.
|
|
32
|
+
- **Exported from the `ai` barrel** — `import { DevadootIcon } from '@devalok/shilp-sutra/ai'` (no dedicated subpath).
|
|
33
|
+
|
|
34
|
+
## Gotchas
|
|
35
|
+
- Client component — mount inside a client boundary.
|
|
36
|
+
- Requires a mounted `MotionProvider`; renders a static chakra when reduced-motion is set.
|
|
37
|
+
- Brand-locked colors (Devalok pink↔purple palette) — it's a brand asset, not a generic spinner. Use `Spinner` for neutral loading.
|
|
38
|
+
|
|
39
|
+
## Changes
|
|
40
|
+
### v0.49.0
|
|
41
|
+
- **Added** Component documentation (the AI layer is now covered by the docs gate + hosted MCP).
|
|
@@ -11,9 +11,7 @@ EmojiPicker, EmojiPickerPopover
|
|
|
11
11
|
|
|
12
12
|
### EmojiPicker
|
|
13
13
|
onSelect: (emoji: EmojiData) => void
|
|
14
|
-
|
|
15
|
-
previewPosition: "top" | "bottom" | "none"
|
|
16
|
-
skinTonePosition: "search" | "preview" | "none"
|
|
14
|
+
emojibaseUrl: string (base URL for the emoji dataset; default = frimousse's jsdelivr CDN)
|
|
17
15
|
className: string
|
|
18
16
|
|
|
19
17
|
### EmojiPickerPopover (extends EmojiPicker props)
|
|
@@ -26,7 +24,7 @@ EmojiPicker, EmojiPickerPopover
|
|
|
26
24
|
shortcodes: string
|
|
27
25
|
|
|
28
26
|
## Defaults
|
|
29
|
-
|
|
27
|
+
align="start"
|
|
30
28
|
|
|
31
29
|
## Example
|
|
32
30
|
```jsx
|
|
@@ -34,27 +32,31 @@ EmojiPicker, EmojiPickerPopover
|
|
|
34
32
|
<Button variant="ghost" size="icon-sm">😀</Button>
|
|
35
33
|
</EmojiPickerPopover>
|
|
36
34
|
|
|
37
|
-
<EmojiPicker onSelect={handleEmoji}
|
|
35
|
+
<EmojiPicker onSelect={handleEmoji} />
|
|
38
36
|
```
|
|
39
37
|
|
|
40
38
|
## Composability
|
|
41
39
|
- **Two exports:** `EmojiPicker` (inline grid, no trigger) and `EmojiPickerPopover` (trigger + popover wrapper). Use EmojiPickerPopover 95% of the time — trigger-on-click is the standard UX.
|
|
42
|
-
- **
|
|
40
|
+
- **Built on frimousse**, bundled and lazy-loaded (own `emoji` chunk, incl. the dataset). Needs no consumer peer install and no React-19 peer workaround. Don't pre-import unless you need it eagerly.
|
|
41
|
+
- **Native emoji only.** Each platform renders its own glyphs. Theme follows the surrounding `.dark` class via DS tokens automatically — no theme prop.
|
|
42
|
+
- **Built-in footer:** a live preview of the hovered/active emoji (frimousse `ActiveEmoji`) plus a skin-tone selector (`SkinToneSelector`) — no props needed.
|
|
43
|
+
- **Dataset source / CSP:** frimousse fetches the emoji dataset from jsdelivr at runtime. To remove the CDN dependency (strict CSP, offline, air-gapped), copy `node_modules/emojibase-data` into your app's `public/emojibase` and pass `emojibaseUrl="/emojibase"`.
|
|
43
44
|
- **Trigger composition:** EmojiPickerPopover's `children` is the trigger — wrap any Button/IconButton. Typical pairing is an icon-only IconButton with a 😀 label.
|
|
44
|
-
- **TipTap integration:** Use `createEmojiSuggestion(
|
|
45
|
-
- **Theme matching:** `theme="auto"` reads the `.dark` class on `<html>` — matches the DS dark mode toggle automatically. Override with explicit light/dark.
|
|
46
|
-
- **Emoji sets:** Pass `set="apple" | "google" | ...` for consistent cross-platform emoji art (defaults to native OS glyphs).
|
|
45
|
+
- **TipTap integration:** Use `createEmojiSuggestion()` (from `./extensions/emoji-suggestion`) to open a `:shortcode:` suggestion list. Works with RichChatInput and RichTextEditor.
|
|
47
46
|
|
|
48
47
|
## Gotchas
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
48
|
+
- Client-only (fetches its emoji dataset on first open) — not server-safe.
|
|
49
|
+
- EmojiPickerPopover auto-closes after selection.
|
|
50
|
+
- The `set` / `theme` / `previewPosition` / `skinTonePosition` props are deprecated no-ops kept for source compatibility; the picker is native-only.
|
|
52
51
|
|
|
53
52
|
## Changes
|
|
54
53
|
|
|
54
|
+
### v-next
|
|
55
|
+
- **Breaking** picker migrated from `@emoji-mart/react` to `frimousse` — native emoji only. Art-style sets (apple/google/twitter/facebook) removed; `set`/`theme`/`previewPosition`/`skinTonePosition` are now no-ops.
|
|
56
|
+
- **Breaking** `emojiDataLoaders` export removed; `EmojiNodeAttrs` narrowed to `{ id, native }`; `createEmojiSuggestion()` takes no argument; `EmojiSuggestionItem` no longer has `x`/`y`.
|
|
57
|
+
- **Changed** frimousse + `@emoji-mart/data` are now bundled (lazy `emoji` chunk) — the emoji feature needs zero consumer peer installs.
|
|
58
|
+
- **Added** built-in footer: active-emoji preview + skin-tone selector. New `emojibaseUrl` prop to self-host the dataset (remove the runtime CDN dependency).
|
|
59
|
+
|
|
55
60
|
### v0.33.0
|
|
56
|
-
- **Added** `
|
|
57
|
-
- **
|
|
58
|
-
- **Added** `createEmojiSuggestion(set?)` factory — replaces `EmojiSuggestion` named export
|
|
59
|
-
- **Added** `EmojiSet` type exported from barrel
|
|
60
|
-
- **Breaking** `EmojiSuggestion` named export removed — use `createEmojiSuggestion()` factory
|
|
61
|
+
- **Added** `EmojiNode` TipTap extension and `createEmojiSuggestion()` factory.
|
|
62
|
+
- **Breaking** `EmojiSuggestion` named export removed — use `createEmojiSuggestion()` factory.
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
- **Server-safe page-top header** with title + subtitle + breadcrumbs + actions slot. Renders Breadcrumb internally from the `breadcrumbs` array.
|
|
33
33
|
- **Auto-derived title:** If `title` is omitted, the last breadcrumb's `label` becomes the h1. Saves duplication when the page title matches the breadcrumb leaf.
|
|
34
34
|
- **Breadcrumb contract:** Last item should NOT have an `href` (it's the current page — renders as BreadcrumbPage, not a link). Items with `href` render as BreadcrumbLink.
|
|
35
|
-
- **actions slot:** Typically one to three buttons (Save, Cancel, More). For larger action groups, use ButtonGroup or a toolbar.
|
|
35
|
+
- **actions slot:** Typically one to three buttons (Save, Cancel, More). The slot wraps onto its own line under the title on narrow viewports instead of forcing horizontal overflow, so multiple/wide buttons are mobile-safe. For larger action groups, use ButtonGroup or a toolbar.
|
|
36
36
|
- **Not for tabs / sub-navigation** — pair with Tabs rendered below PageHeader for that.
|
|
37
37
|
|
|
38
38
|
## Gotchas
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# ResponsiveModal
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/composed/responsive-modal
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: composed
|
|
6
|
+
|
|
7
|
+
## Props
|
|
8
|
+
|
|
9
|
+
### ResponsiveModal (root)
|
|
10
|
+
open: boolean (controlled open state)
|
|
11
|
+
defaultOpen: boolean (uncontrolled initial state)
|
|
12
|
+
onOpenChange: (open: boolean) => void
|
|
13
|
+
dismissable: boolean (default: true — when false, Escape / outside-click / drag cannot close; only an explicit Close or onOpenChange does)
|
|
14
|
+
|
|
15
|
+
### ResponsiveModalContent
|
|
16
|
+
snapPoints: number[] (mobile only; ascending viewport-height fractions like [0.4, 0.9] the sheet rests at. Omit for a content-height sheet. Ignored on desktop)
|
|
17
|
+
defaultSnapPoint: number (mobile only; index into snapPoints the sheet opens at. Defaults to the last/tallest)
|
|
18
|
+
|
|
19
|
+
## Defaults
|
|
20
|
+
dismissable=true
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
```jsx
|
|
24
|
+
<ResponsiveModal>
|
|
25
|
+
<ResponsiveModalTrigger asChild><Button>Edit profile</Button></ResponsiveModalTrigger>
|
|
26
|
+
<ResponsiveModalContent snapPoints={[0.5, 0.9]}>
|
|
27
|
+
<ResponsiveModalBackground className="bg-linear-to-br from-accent-3 to-accent-6 opacity-40" />
|
|
28
|
+
<ResponsiveModalHeader>
|
|
29
|
+
<ResponsiveModalTitle>Edit profile</ResponsiveModalTitle>
|
|
30
|
+
<ResponsiveModalDescription>Changes save when you apply.</ResponsiveModalDescription>
|
|
31
|
+
</ResponsiveModalHeader>
|
|
32
|
+
<ResponsiveModalBody>{/* form fields — scrolls internally */}</ResponsiveModalBody>
|
|
33
|
+
<ResponsiveModalFooter>
|
|
34
|
+
<ResponsiveModalClose asChild><Button variant="soft">Cancel</Button></ResponsiveModalClose>
|
|
35
|
+
<Button>Apply</Button>
|
|
36
|
+
</ResponsiveModalFooter>
|
|
37
|
+
</ResponsiveModalContent>
|
|
38
|
+
</ResponsiveModal>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Composability
|
|
42
|
+
- **Part:** ResponsiveModal (required) — root; owns open/close state and the desktop/mobile switch
|
|
43
|
+
- **Part:** ResponsiveModalTrigger — opens the modal; use `asChild` to wrap your own button
|
|
44
|
+
- **Part:** ResponsiveModalContent — the switching panel: centered Dialog at md+, bottom sheet below
|
|
45
|
+
- **Part:** ResponsiveModalBackground (slot: behind) — optional full-bleed layer painted at `-z-10`, clipped to the panel radius
|
|
46
|
+
- **Part:** ResponsiveModalHeader (slot: top) — pinned; stays put while the body scrolls
|
|
47
|
+
- **Part:** ResponsiveModalTitle (required) — labels the dialog for accessibility
|
|
48
|
+
- **Part:** ResponsiveModalDescription — optional subtitle
|
|
49
|
+
- **Part:** ResponsiveModalBody — internal scroll region (min-h-0 flex-1 overflow-y-auto), capped 85dvh desktop / 90dvh mobile
|
|
50
|
+
- **Part:** ResponsiveModalFooter (slot: bottom) — pinned action row; stacks on mobile, right-aligned inline on desktop
|
|
51
|
+
- **Part:** ResponsiveModalClose — manual close (Content already renders a built-in close button when dismissable)
|
|
52
|
+
- **Composes:** dialog (alternative-to) — use Dialog directly when you always want a centered modal
|
|
53
|
+
- **Composes:** sheet (alternative-to) — use Sheet directly when you always want an edge panel
|
|
54
|
+
- **Context:** ResponsiveModal — provides open / isMobile / dismissable / onClose to its parts via context
|
|
55
|
+
|
|
56
|
+
## Gotchas
|
|
57
|
+
- Built on the same dialog primitive as Dialog and Sheet — focus trap, Escape, scroll lock, and portal are handled. Not server-safe (uses hooks); import in a client component.
|
|
58
|
+
- Put a `ResponsiveModalTitle` inside every modal — it sets the ARIA label. Omitting it triggers an accessibility warning.
|
|
59
|
+
- The built-in close button sits at `z-10` above `ResponsiveModalBackground` (`-z-10`); keep Header/Body/Footer in normal flow (no `relative z-*` wrapper) or a positioned wrapper will paint over the close button and eat the tap.
|
|
60
|
+
- `snapPoints` and `defaultSnapPoint` are mobile-only — they are ignored at md+ (always a centered Dialog).
|
|
61
|
+
- Height caps live on the panel (85dvh desktop / 90dvh mobile); do not add your own `max-h` to `ResponsiveModalBody` — it already scrolls.
|
|
62
|
+
- Prefer this over `DialogContent responsive` when the mobile form should be a partial bottom sheet; `responsive` collapses to a full-screen takeover that leaves dead space under short content.
|
|
63
|
+
|
|
64
|
+
## Changes
|
|
65
|
+
### v0.49.0
|
|
66
|
+
- Added — new component. Dialog (md+) / bottom-sheet (mobile) responsive modal with pinned header/footer, internal scroll body, optional full-bleed background slot, drag-to-dismiss, and optional mobile snap points.
|
|
@@ -58,7 +58,7 @@ MentionItem: { id: string; label: string; avatar?: string }
|
|
|
58
58
|
|
|
59
59
|
## Gotchas
|
|
60
60
|
- Tiptap is bundled — no need to install `@tiptap/*` packages separately
|
|
61
|
-
- Emoji picker
|
|
61
|
+
- Emoji picker (frimousse) + its dataset are bundled too — no emoji peers to install (native-only since the frimousse migration)
|
|
62
62
|
- Images without `onImageUpload` are stored as base64 in HTML — large images bloat content
|
|
63
63
|
- Mention rendering in viewer always works (no mention props needed, just the HTML)
|
|
64
64
|
- Features: bold, italic, underline, strikethrough, highlight, headings, blockquote, lists, task lists, code, links, images, file attachments, mentions, emoji, text alignment, horizontal rule
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
endHour: number (default: 18, exclusive)
|
|
15
15
|
slotDuration: number (minutes, default: 30)
|
|
16
16
|
|
|
17
|
-
Event colors: "
|
|
17
|
+
Event colors: "accent" | "success" | "warning" | "error" | "info" | "neutral"
|
|
18
18
|
|
|
19
19
|
## Defaults
|
|
20
20
|
startHour=8, endHour=18, slotDuration=30
|
|
@@ -33,7 +33,7 @@ Event colors: "primary" | "success" | "warning" | "error" | "info" | "neutral"
|
|
|
33
33
|
- **Day / Week calendar view** for time-block display (meetings, shifts, availability). Not a full calendar app — no month view, no drag-to-create.
|
|
34
34
|
- **Event data is consumer-owned:** You pass `events` as an array; ScheduleView doesn't fetch, doesn't cache, doesn't expand recurring events. All scheduling logic lives in your app.
|
|
35
35
|
- **Event click + slot click** — `onEventClick` for existing events; `onSlotClick` for creating new events (fires with start/end of the empty slot).
|
|
36
|
-
- **Color vocabulary matches the DS** — `
|
|
36
|
+
- **Color vocabulary matches the DS** — `accent/success/warning/error/info/neutral`. Map your event types to these at the data layer.
|
|
37
37
|
- **endHour is exclusive:** `endHour=18` means the last visible slot starts at 17:30 (with 30min slots). Match your UX expectation: 9-5 typically means `startHour=9, endHour=18`.
|
|
38
38
|
- **Pairs with date-picker/composed** — use DatePicker or DateRangePicker to choose which date to show; pass that as ScheduleView's `date`.
|
|
39
39
|
|
|
@@ -43,5 +43,9 @@ Event colors: "primary" | "success" | "warning" | "error" | "info" | "neutral"
|
|
|
43
43
|
- Events that span outside `startHour`/`endHour` may be clipped
|
|
44
44
|
|
|
45
45
|
## Changes
|
|
46
|
+
### v0.49.0
|
|
47
|
+
- **BREAKING** `ScheduleEvent.color` value `"primary"` renamed `"accent"` (DS colour vocabulary). It was the default, so untyped events are unaffected.
|
|
48
|
+
- **Added** keyboard focus rings on slot cells + event blocks; current-time indicator uses the shared `<Dot>`.
|
|
49
|
+
|
|
46
50
|
### v0.1.0
|
|
47
51
|
- **Added** Initial release
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
checked: boolean | "indeterminate"
|
|
9
9
|
onCheckedChange: (checked: boolean | "indeterminate") => void
|
|
10
10
|
size: "sm" | "md" | "lg"
|
|
11
|
-
|
|
11
|
+
state: "default" | "error" | "warning" | "success" (validation border/bg tint; error sets aria-invalid. Inherited from FormField when omitted)
|
|
12
12
|
indeterminate: boolean (overrides checked, shows dash icon)
|
|
13
13
|
disabled: boolean
|
|
14
14
|
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
- WCAG 2.5.8 minimum target size met at md (24px); sm (20px) may fail if not paired with enough label hit area
|
|
38
38
|
|
|
39
39
|
## Changes
|
|
40
|
+
### v0.49.0
|
|
41
|
+
- **BREAKING** Removed the `error: boolean` prop; use `state` (`FieldState = "default" | "error" | "warning" | "success"`). Migrate `<Checkbox error />` → `<Checkbox state="error" />`. Gains `warning`/`success` tints and now inherits state from `FormField`.
|
|
42
|
+
|
|
40
43
|
### v0.22.0
|
|
41
44
|
- **Changed** Check indicator animation from scale-bounce to path-draw (stroke draws progressively). Indeterminate dash also draws in.
|
|
42
45
|
- **Fixed** Uncontrolled checkbox never showed checkmark — `checked` from props was `undefined`, so `isActive` was always `false`. Now tracks internal state.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
## Props
|
|
8
8
|
options: ComboboxOption[] (REQUIRED) — { value: string, label: string, description?: string, icon?: ReactNode, disabled?: boolean }
|
|
9
9
|
size: "xs" | "sm" | "md" | "lg" (trigger height)
|
|
10
|
+
state: "default" | "error" | "warning" | "success" (validation border tint; error sets aria-invalid. Inherited from FormField when omitted)
|
|
10
11
|
DISCRIMINATED UNION — type depends on `multiple` flag:
|
|
11
12
|
Single (default): multiple?: false, value?: string, onValueChange: (value: string) => void
|
|
12
13
|
Multiple: multiple: true, value?: string[], onValueChange: (value: string[]) => void
|
|
@@ -50,6 +51,9 @@
|
|
|
50
51
|
- Dropdown content is portalled to document.body — parent styles like `overflow: hidden` don't clip it, and container-scoped test queries won't find it
|
|
51
52
|
|
|
52
53
|
## Changes
|
|
54
|
+
### v0.49.0
|
|
55
|
+
- **Added** `state` prop (`FieldState = "default" | "error" | "warning" | "success"`) — unified field-state API. Renders a validation border tint + sets `aria-invalid`; inherits from `FormField` when omitted. (Additive — Combobox had no prior validation prop.)
|
|
56
|
+
|
|
53
57
|
### v0.18.0
|
|
54
58
|
- **Added** `accessibleLabel` prop — custom aria-label for trigger, falls back to placeholder
|
|
55
59
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Dot
|
|
2
|
+
|
|
3
|
+
- Import: @devalok/shilp-sutra/ui/dot
|
|
4
|
+
- Server-safe: No
|
|
5
|
+
- Category: ui
|
|
6
|
+
|
|
7
|
+
A small semantic status/indicator dot — the low-level primitive behind status
|
|
8
|
+
pills, presence indicators, legend swatches, and `StatusDot`. Intent-coloured,
|
|
9
|
+
sized, optionally pulsing, filled or ring.
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
color: "accent" | "success" | "warning" | "error" | "info" | "neutral" | "current" (intent colour; `current` inherits text colour)
|
|
13
|
+
size: "xs" | "sm" | "md" | "lg"
|
|
14
|
+
variant: "filled" | "ring" | "off" (`off` = faint fill + light border, i.e. inactive)
|
|
15
|
+
withBorder: boolean (contrast ring so the dot stays visible on busy/coloured backgrounds)
|
|
16
|
+
pulse: boolean (animate a pulsing ripple; ignored for `off`)
|
|
17
|
+
pulseSpeed: "slow" | "normal" | "fast" (pulse tempo)
|
|
18
|
+
label: ReactNode (inline text; makes the dot an announced status)
|
|
19
|
+
labelPosition: "start" | "end" (which side the label sits on)
|
|
20
|
+
labelClassName: string
|
|
21
|
+
|
|
22
|
+
## Defaults
|
|
23
|
+
color: "neutral"
|
|
24
|
+
size: "md"
|
|
25
|
+
variant: "filled"
|
|
26
|
+
withBorder: false
|
|
27
|
+
pulse: false
|
|
28
|
+
pulseSpeed: "normal"
|
|
29
|
+
labelPosition: "end"
|
|
30
|
+
|
|
31
|
+
## Example
|
|
32
|
+
```jsx
|
|
33
|
+
<Dot color="success" /> {/* bare, decorative */}
|
|
34
|
+
<Dot color="error" pulse pulseSpeed="fast" label="Recording" /> {/* pulsing + announced */}
|
|
35
|
+
<Dot color="neutral" variant="off" label="Offline" /> {/* inactive: faint fill + light border */}
|
|
36
|
+
<Dot color="success" withBorder /> {/* contrast ring for busy backgrounds */}
|
|
37
|
+
<Dot color="accent" label="3 online" labelPosition="start" /> {/* label before the dot */}
|
|
38
|
+
<Dot color="neutral" variant="ring" size="lg" /> {/* hollow ring */}
|
|
39
|
+
|
|
40
|
+
// As leading content in a pill:
|
|
41
|
+
<Badge variant="soft" color="warning" startIcon={<Dot color="warning" size="sm" />}>
|
|
42
|
+
Pending
|
|
43
|
+
</Badge>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Composability
|
|
47
|
+
- **Decorative vs announced.** A bare `<Dot>` is `aria-hidden` (the surrounding element carries meaning — e.g. a Badge's text). Pass a `label` (or `aria-label`) to make it a `role="status"` announced indicator.
|
|
48
|
+
- **Intent colour, shared vocabulary.** `color` uses the same intent names as the rest of the system (`accent/success/warning/error/info/neutral`), so a Dot next to a Badge/Button of the same intent matches.
|
|
49
|
+
- **`pulse` = "live".** Off by default (static status). Turn it on for active/attention states (recording, live, connecting) — a pulsing dot reads as "happening now", so leave it off for settled statuses.
|
|
50
|
+
- **Powers the higher-level pieces.** `StatusBadge` composes `<Badge>` + `<Dot>`; use `Dot` directly anywhere you need a coloured indicator without a pill.
|
|
51
|
+
- **Three treatments:** `filled` (active), `ring` (hollow outline), `off` (faint same-tone fill + light border — present but inactive/disabled).
|
|
52
|
+
- **`withBorder`** adds a contrast ring (`ring-2 ring-surface-raised`) so the dot reads on avatars, images, or coloured fills — the pattern Avatar's status dot uses.
|
|
53
|
+
- **`pulseSpeed`** (slow/normal/fast) tunes the pulse tempo — fast for urgent (recording), slow for ambient (syncing).
|
|
54
|
+
|
|
55
|
+
## Gotchas
|
|
56
|
+
- A bare dot is decorative by design — if a dot is the *only* signal (no adjacent text), give it a `label` or `aria-label` so it's announced.
|
|
57
|
+
- `pulse` respects `prefers-reduced-motion` (the ripple is disabled).
|
|
58
|
+
|
|
59
|
+
## Changes
|
|
60
|
+
### v0.49.0
|
|
61
|
+
- **Added** Initial release. Extracted as the shared primitive behind `StatusDot`, `StatusBadge`, and `Badge`'s dot. Colours `accent/success/warning/error/info/neutral/current`; sizes `xs–lg`; variants `filled/ring/off`; `withBorder` contrast ring; `pulse` + `pulseSpeed`; `label` + `labelPosition`.
|