prompt-area 0.1.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/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/action-bar/index.d.ts +60 -0
- package/dist/action-bar/index.js +5 -0
- package/dist/action-bar/index.js.map +1 -0
- package/dist/chat-prompt-layout/index.d.ts +53 -0
- package/dist/chat-prompt-layout/index.js +5 -0
- package/dist/chat-prompt-layout/index.js.map +1 -0
- package/dist/chunk-ANZZEZP2.js +38 -0
- package/dist/chunk-ANZZEZP2.js.map +1 -0
- package/dist/chunk-BPJO4DGM.js +198 -0
- package/dist/chunk-BPJO4DGM.js.map +1 -0
- package/dist/chunk-BWVBDP7C.js +38 -0
- package/dist/chunk-BWVBDP7C.js.map +1 -0
- package/dist/chunk-E7HUXORB.js +2692 -0
- package/dist/chunk-E7HUXORB.js.map +1 -0
- package/dist/chunk-NF2LHZIE.js +12 -0
- package/dist/chunk-NF2LHZIE.js.map +1 -0
- package/dist/chunk-UBBCAMJA.js +116 -0
- package/dist/chunk-UBBCAMJA.js.map +1 -0
- package/dist/chunk-XDKRP7UE.js +125 -0
- package/dist/chunk-XDKRP7UE.js.map +1 -0
- package/dist/compact-prompt-area/index.d.ts +86 -0
- package/dist/compact-prompt-area/index.js +6 -0
- package/dist/compact-prompt-area/index.js.map +1 -0
- package/dist/helpers/index.d.ts +374 -0
- package/dist/helpers/index.js +291 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt-area/index.d.ts +327 -0
- package/dist/prompt-area/index.js +6 -0
- package/dist/prompt-area/index.js.map +1 -0
- package/dist/status-bar/index.d.ts +50 -0
- package/dist/status-bar/index.js +5 -0
- package/dist/status-bar/index.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/tailwind.css +181 -0
- package/dist/types-C4BgDEpe.d.ts +271 -0
- package/package.json +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ilko Kacharov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# prompt-area
|
|
2
|
+
|
|
3
|
+
An opinionated, dependency-light React rich-text **prompt input** — trigger-based chips (`@mentions`, `/commands`, `#tags`), inline markdown, undo/redo, file & image attachments, and a complete chat-input layout.
|
|
4
|
+
|
|
5
|
+
Ships **two ways** from the same source:
|
|
6
|
+
|
|
7
|
+
- **npm package** (this package) — `npm install prompt-area`, import the component and a stylesheet. Versioned, opinionated, batteries-included.
|
|
8
|
+
- **[shadcn registry](https://prompt-area.com)** — `npx shadcn@latest add https://prompt-area.com/r/prompt-area.json` to copy the source into your project and own it.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install prompt-area
|
|
14
|
+
# peer deps (most React apps already have these)
|
|
15
|
+
npm install react react-dom
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
'use client'
|
|
22
|
+
import { useState } from 'react'
|
|
23
|
+
import { PromptArea, mentionTrigger, type Segment } from 'prompt-area'
|
|
24
|
+
import 'prompt-area/styles.css'
|
|
25
|
+
|
|
26
|
+
export function Composer() {
|
|
27
|
+
const [value, setValue] = useState<Segment[]>([])
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<PromptArea
|
|
31
|
+
value={value}
|
|
32
|
+
onChange={setValue}
|
|
33
|
+
placeholder="Type a message…"
|
|
34
|
+
autoGrow
|
|
35
|
+
triggers={[
|
|
36
|
+
mentionTrigger({
|
|
37
|
+
onSearch: async (q) =>
|
|
38
|
+
[
|
|
39
|
+
{ id: '1', label: 'Ada Lovelace', value: 'ada' },
|
|
40
|
+
{ id: '2', label: 'Alan Turing', value: 'alan' },
|
|
41
|
+
].filter((u) => u.label.toLowerCase().includes(q.toLowerCase())),
|
|
42
|
+
}),
|
|
43
|
+
]}
|
|
44
|
+
onSubmit={(segments) => console.log(segments)}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Styling
|
|
51
|
+
|
|
52
|
+
The components are styled with Tailwind utility classes plus a small set of design tokens. You can use them **with or without** Tailwind.
|
|
53
|
+
|
|
54
|
+
### Option A — prebuilt CSS (zero config)
|
|
55
|
+
|
|
56
|
+
Import the compiled stylesheet once. It is self-contained, ships **no global reset** (it won't restyle the rest of your app), and works in any React app.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import 'prompt-area/styles.css'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Theme it by overriding the CSS variables (same names as shadcn/ui):
|
|
63
|
+
|
|
64
|
+
```css
|
|
65
|
+
:root {
|
|
66
|
+
--primary: oklch(0.62 0.19 260);
|
|
67
|
+
--radius: 0.5rem;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Dark mode follows a `.dark` ancestor (add `class="dark"` to `<html>` or a wrapper).
|
|
72
|
+
|
|
73
|
+
### Option B — Tailwind v4 preset (themeable)
|
|
74
|
+
|
|
75
|
+
If you run Tailwind yourself, import the preset from your CSS entry instead. It registers the tokens and component styles and points Tailwind at the built files so the utilities are generated in your pipeline.
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
@import 'tailwindcss';
|
|
79
|
+
@import 'prompt-area/tailwind.css';
|
|
80
|
+
/* for the entrance/spinner animations: */
|
|
81
|
+
@import 'tw-animate-css';
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
In a shadcn project the token names already exist, so the component automatically inherits your theme — you usually don't need to do anything.
|
|
85
|
+
|
|
86
|
+
## Exports
|
|
87
|
+
|
|
88
|
+
| Import | Description |
|
|
89
|
+
| --------------------------------- | --------------------------------------------------------- |
|
|
90
|
+
| `prompt-area` | Everything: components, hooks, helpers, and types |
|
|
91
|
+
| `prompt-area/prompt-area` | `PromptArea` + its types/hooks |
|
|
92
|
+
| `prompt-area/action-bar` | `ActionBar` |
|
|
93
|
+
| `prompt-area/status-bar` | `StatusBar` |
|
|
94
|
+
| `prompt-area/compact-prompt-area` | `CompactPromptArea` |
|
|
95
|
+
| `prompt-area/chat-prompt-layout` | `ChatPromptLayout` |
|
|
96
|
+
| `prompt-area/helpers` | Framework-agnostic helpers, **safe in Server Components** |
|
|
97
|
+
| `prompt-area/styles.css` | Prebuilt stylesheet |
|
|
98
|
+
| `prompt-area/tailwind.css` | Tailwind v4 preset |
|
|
99
|
+
|
|
100
|
+
All blocks are tree-shakeable, so deep-importing a single block keeps your bundle small.
|
|
101
|
+
|
|
102
|
+
### React Server Components
|
|
103
|
+
|
|
104
|
+
The components are client components and carry a `'use client'` boundary, so you can render them directly from a Server Component. The pure helpers (e.g. `segmentsToPlainText`, `parseInlineMarkdown`, trigger presets) are also re-exported from `prompt-area/helpers` **without** the client boundary, so they can run on the server too.
|
|
105
|
+
|
|
106
|
+
## Dependencies
|
|
107
|
+
|
|
108
|
+
- **Peer:** `react`, `react-dom` (>= 18)
|
|
109
|
+
- **Runtime:** `clsx`, `tailwind-merge` — the two `cn` helpers, both already
|
|
110
|
+
present in any shadcn/Tailwind project.
|
|
111
|
+
|
|
112
|
+
No animation library and no icon library: animations use CSS (`tw-animate-css`
|
|
113
|
+
utilities) and icons are inline SVGs. No editor framework (ProseMirror, Slate,
|
|
114
|
+
Lexical) either.
|
|
115
|
+
|
|
116
|
+
ESM-only. Requires Node 18+ / a modern bundler (Next.js, Vite, etc.).
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ActionBar component types
|
|
5
|
+
*
|
|
6
|
+
* A horizontal toolbar with left and right slots, designed to sit
|
|
7
|
+
* below a text input (e.g., PromptArea) and stay anchored via
|
|
8
|
+
* normal document flow.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Props for the ActionBar component.
|
|
12
|
+
*/
|
|
13
|
+
type ActionBarProps = {
|
|
14
|
+
/** Content rendered on the left side of the bar */
|
|
15
|
+
left?: React.ReactNode;
|
|
16
|
+
/** Content rendered on the right side of the bar */
|
|
17
|
+
right?: React.ReactNode;
|
|
18
|
+
/** Additional CSS class for the root element */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** Whether the action bar is disabled (visually dims and disables pointer events) */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Accessible label for the toolbar */
|
|
23
|
+
'aria-label'?: string;
|
|
24
|
+
/** data-test-id for e2e testing */
|
|
25
|
+
'data-test-id'?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* ActionBar - A horizontal toolbar with left and right slots.
|
|
30
|
+
*
|
|
31
|
+
* Designed to sit below a text input (e.g., PromptArea) and stay
|
|
32
|
+
* anchored via normal document flow. Place it as a sibling after
|
|
33
|
+
* the input inside a shared wrapper.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* <div className="rounded-lg border p-4">
|
|
38
|
+
* <PromptArea value={segments} onChange={setSegments} ... />
|
|
39
|
+
* <ActionBar
|
|
40
|
+
* left={
|
|
41
|
+
* <>
|
|
42
|
+
* <button><PlusCircle /></button>
|
|
43
|
+
* <button><AtSign /></button>
|
|
44
|
+
* </>
|
|
45
|
+
* }
|
|
46
|
+
* right={
|
|
47
|
+
* <>
|
|
48
|
+
* <button><Mic /></button>
|
|
49
|
+
* <button onClick={handleSubmit}><ArrowUp /></button>
|
|
50
|
+
* </>
|
|
51
|
+
* }
|
|
52
|
+
* />
|
|
53
|
+
* </div>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare function ActionBar({ left, right, className, disabled, 'aria-label': ariaLabel, 'data-test-id': dataTestId, ref, }: ActionBarProps & {
|
|
57
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
58
|
+
}): react.JSX.Element;
|
|
59
|
+
|
|
60
|
+
export { ActionBar, type ActionBarProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ChatPromptLayout component types
|
|
5
|
+
*
|
|
6
|
+
* A full-height chat layout with a scrollable messages area and
|
|
7
|
+
* a bottom-anchored prompt slot. Includes contextual scroll
|
|
8
|
+
* navigation buttons.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Props for the ChatPromptLayout component.
|
|
12
|
+
*/
|
|
13
|
+
type ChatPromptLayoutProps = {
|
|
14
|
+
/** Chat messages rendered in the scrollable area */
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
/** Prompt area rendered at the bottom of the layout (slot) */
|
|
17
|
+
prompt: React.ReactNode;
|
|
18
|
+
/** Additional CSS class for the root container */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** Accessible label for the layout region */
|
|
21
|
+
'aria-label'?: string;
|
|
22
|
+
/** data-test-id for e2e testing */
|
|
23
|
+
'data-test-id'?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* ChatPromptLayout - A full-height chat layout with scrollable messages
|
|
28
|
+
* and a bottom-anchored prompt slot.
|
|
29
|
+
*
|
|
30
|
+
* Pass chat messages as `children` and the prompt area via the `prompt`
|
|
31
|
+
* prop. Contextual scroll buttons appear when the user scrolls away
|
|
32
|
+
* from the top or bottom of the messages area.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <ChatPromptLayout
|
|
37
|
+
* className="h-[600px]"
|
|
38
|
+
* prompt={
|
|
39
|
+
* <div className="border-t p-4">
|
|
40
|
+
* <PromptArea ... />
|
|
41
|
+
* <ActionBar ... />
|
|
42
|
+
* </div>
|
|
43
|
+
* }
|
|
44
|
+
* >
|
|
45
|
+
* {messages.map(msg => <ChatBubble key={msg.id} {...msg} />)}
|
|
46
|
+
* </ChatPromptLayout>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare function ChatPromptLayout({ children, prompt, className, 'aria-label': ariaLabel, 'data-test-id': dataTestId, ref, }: ChatPromptLayoutProps & {
|
|
50
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
51
|
+
}): react.JSX.Element;
|
|
52
|
+
|
|
53
|
+
export { ChatPromptLayout, type ChatPromptLayoutProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { cn } from './chunk-NF2LHZIE.js';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function StatusBar({
|
|
6
|
+
left,
|
|
7
|
+
right,
|
|
8
|
+
className,
|
|
9
|
+
disabled = false,
|
|
10
|
+
"aria-label": ariaLabel,
|
|
11
|
+
"data-test-id": dataTestId,
|
|
12
|
+
ref
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ jsxs(
|
|
15
|
+
"div",
|
|
16
|
+
{
|
|
17
|
+
ref,
|
|
18
|
+
role: "group",
|
|
19
|
+
"aria-label": ariaLabel ?? "Status bar",
|
|
20
|
+
"aria-disabled": disabled || void 0,
|
|
21
|
+
"data-test-id": dataTestId,
|
|
22
|
+
className: cn(
|
|
23
|
+
"status-bar",
|
|
24
|
+
"flex items-center justify-between gap-2 px-3 py-1.5 text-xs",
|
|
25
|
+
disabled && "pointer-events-none opacity-50",
|
|
26
|
+
className
|
|
27
|
+
),
|
|
28
|
+
children: [
|
|
29
|
+
left && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: left }),
|
|
30
|
+
right && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1.5", children: right })
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { StatusBar };
|
|
37
|
+
//# sourceMappingURL=chunk-ANZZEZP2.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-ANZZEZP2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/status-bar/status-bar.tsx"],"names":[],"mappings":";;;AAuBO,SAAS,SAAA,CAAU;AAAA,EACxB,IAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,YAAA,EAAc,SAAA;AAAA,EACd,cAAA,EAAgB,UAAA;AAAA,EAChB;AACF,CAAA,EAAyD;AACvD,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,IAAA,EAAK,OAAA;AAAA,MACL,cAAY,SAAA,IAAa,YAAA;AAAA,MACzB,iBAAe,QAAA,IAAY,MAAA;AAAA,MAC3B,cAAA,EAAc,UAAA;AAAA,MACd,SAAA,EAAW,EAAA;AAAA,QACT,YAAA;AAAA,QACA,6DAAA;AAAA,QACA,QAAA,IAAY,gCAAA;AAAA,QACZ;AAAA,OACF;AAAA,MACC,QAAA,EAAA;AAAA,QAAA,IAAA,oBAAQ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EAA6B,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,QACzD,KAAA,oBAAS,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAqC,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,GACtE;AAEJ","file":"chunk-ANZZEZP2.js","sourcesContent":["'use client'\n\nimport { cn } from '@/lib/utils'\nimport type { StatusBarProps } from './types'\n\n/**\n * StatusBar - A horizontal bar with left and right slots.\n *\n * Designed to sit above or below a text input (e.g., PromptArea) to\n * display contextual information. Place it as a sibling before or\n * after the input inside a shared wrapper.\n *\n * @example\n * ```tsx\n * <div className=\"rounded-lg border\">\n * <StatusBar\n * left={<span>prompt-area</span>}\n * right={<span>Default</span>}\n * />\n * <PromptArea value={segments} onChange={setSegments} ... />\n * </div>\n * ```\n */\nexport function StatusBar({\n left,\n right,\n className,\n disabled = false,\n 'aria-label': ariaLabel,\n 'data-test-id': dataTestId,\n ref,\n}: StatusBarProps & { ref?: React.Ref<HTMLDivElement> }) {\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel ?? 'Status bar'}\n aria-disabled={disabled || undefined}\n data-test-id={dataTestId}\n className={cn(\n 'status-bar',\n 'flex items-center justify-between gap-2 px-3 py-1.5 text-xs',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}>\n {left && <div className=\"flex items-center gap-1.5\">{left}</div>}\n {right && <div className=\"ml-auto flex items-center gap-1.5\">{right}</div>}\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { BLUR_DELAY_MS, PromptArea } from './chunk-E7HUXORB.js';
|
|
3
|
+
import { cn } from './chunk-NF2LHZIE.js';
|
|
4
|
+
import { useRef, useState, useImperativeHandle, useCallback } from 'react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function Svg({ className, children }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(
|
|
9
|
+
"svg",
|
|
10
|
+
{
|
|
11
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
12
|
+
width: "24",
|
|
13
|
+
height: "24",
|
|
14
|
+
viewBox: "0 0 24 24",
|
|
15
|
+
fill: "none",
|
|
16
|
+
stroke: "currentColor",
|
|
17
|
+
strokeWidth: "2",
|
|
18
|
+
strokeLinecap: "round",
|
|
19
|
+
strokeLinejoin: "round",
|
|
20
|
+
"aria-hidden": "true",
|
|
21
|
+
className,
|
|
22
|
+
children
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
var Plus = ({ className }) => /* @__PURE__ */ jsxs(Svg, { className, children: [
|
|
27
|
+
/* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
|
|
28
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5v14" })
|
|
29
|
+
] });
|
|
30
|
+
var ArrowUp = ({ className }) => /* @__PURE__ */ jsxs(Svg, { className, children: [
|
|
31
|
+
/* @__PURE__ */ jsx("path", { d: "m5 12 7-7 7 7" }),
|
|
32
|
+
/* @__PURE__ */ jsx("path", { d: "M12 19V5" })
|
|
33
|
+
] });
|
|
34
|
+
function CompactPromptArea({
|
|
35
|
+
value,
|
|
36
|
+
onChange,
|
|
37
|
+
triggers,
|
|
38
|
+
placeholder,
|
|
39
|
+
disabled = false,
|
|
40
|
+
markdown,
|
|
41
|
+
onSubmit,
|
|
42
|
+
onEscape,
|
|
43
|
+
onChipClick,
|
|
44
|
+
onChipAdd,
|
|
45
|
+
onChipDelete,
|
|
46
|
+
onPaste,
|
|
47
|
+
images,
|
|
48
|
+
onImagePaste,
|
|
49
|
+
onImageRemove,
|
|
50
|
+
files,
|
|
51
|
+
onFileRemove,
|
|
52
|
+
plusButtonIcon,
|
|
53
|
+
onPlusClick,
|
|
54
|
+
submitButtonIcon,
|
|
55
|
+
beforeSubmitSlot,
|
|
56
|
+
maxHeight = 320,
|
|
57
|
+
className,
|
|
58
|
+
"aria-label": ariaLabel,
|
|
59
|
+
"data-test-id": dataTestId,
|
|
60
|
+
ref
|
|
61
|
+
}) {
|
|
62
|
+
const promptRef = useRef(null);
|
|
63
|
+
const containerRef = useRef(null);
|
|
64
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
65
|
+
useImperativeHandle(ref, () => promptRef.current, []);
|
|
66
|
+
const isEmpty = value.length === 0 || value.length === 1 && value[0].type === "text" && value[0].text === "";
|
|
67
|
+
const isExpanded = isFocused || !isEmpty;
|
|
68
|
+
const handleContainerFocus = useCallback(() => {
|
|
69
|
+
setIsFocused(true);
|
|
70
|
+
}, []);
|
|
71
|
+
const handleContainerBlur = useCallback(() => {
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
if (!containerRef.current?.contains(document.activeElement)) {
|
|
74
|
+
setIsFocused(false);
|
|
75
|
+
}
|
|
76
|
+
}, BLUR_DELAY_MS);
|
|
77
|
+
}, []);
|
|
78
|
+
const handleSubmit = useCallback(() => {
|
|
79
|
+
onSubmit?.(value);
|
|
80
|
+
}, [onSubmit, value]);
|
|
81
|
+
return /* @__PURE__ */ jsx(
|
|
82
|
+
"div",
|
|
83
|
+
{
|
|
84
|
+
ref: containerRef,
|
|
85
|
+
onFocus: handleContainerFocus,
|
|
86
|
+
onBlur: handleContainerBlur,
|
|
87
|
+
"aria-label": ariaLabel,
|
|
88
|
+
"data-test-id": dataTestId,
|
|
89
|
+
className: cn(
|
|
90
|
+
"compact-prompt-area",
|
|
91
|
+
"bg-background border transition-all duration-200 ease-out",
|
|
92
|
+
isExpanded ? "rounded-2xl" : "rounded-full",
|
|
93
|
+
className
|
|
94
|
+
),
|
|
95
|
+
children: /* @__PURE__ */ jsxs("div", { className: cn("flex", isExpanded ? "flex-col" : "items-center p-1.5"), children: [
|
|
96
|
+
!isExpanded && /* @__PURE__ */ jsx(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
onClick: onPlusClick,
|
|
101
|
+
disabled,
|
|
102
|
+
className: cn(
|
|
103
|
+
"flex shrink-0 items-center justify-center rounded-xl transition-colors",
|
|
104
|
+
"bg-muted text-muted-foreground size-9",
|
|
105
|
+
"hover:bg-accent hover:text-foreground",
|
|
106
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
107
|
+
),
|
|
108
|
+
"aria-label": "Add attachment",
|
|
109
|
+
children: plusButtonIcon ?? /* @__PURE__ */ jsx(Plus, { className: "size-4" })
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
/* @__PURE__ */ jsx(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
className: cn("min-w-0 flex-1", isExpanded ? "px-5 pt-4 pb-2" : "overflow-hidden px-3"),
|
|
116
|
+
onClick: () => promptRef.current?.focus(),
|
|
117
|
+
children: /* @__PURE__ */ jsx(
|
|
118
|
+
PromptArea,
|
|
119
|
+
{
|
|
120
|
+
ref: promptRef,
|
|
121
|
+
value,
|
|
122
|
+
onChange,
|
|
123
|
+
triggers,
|
|
124
|
+
placeholder,
|
|
125
|
+
disabled,
|
|
126
|
+
markdown,
|
|
127
|
+
onSubmit: handleSubmit,
|
|
128
|
+
onEscape,
|
|
129
|
+
onChipClick,
|
|
130
|
+
onChipAdd,
|
|
131
|
+
onChipDelete,
|
|
132
|
+
onPaste,
|
|
133
|
+
images,
|
|
134
|
+
onImagePaste,
|
|
135
|
+
onImageRemove,
|
|
136
|
+
files,
|
|
137
|
+
onFileRemove,
|
|
138
|
+
autoGrow: true,
|
|
139
|
+
minHeight: isExpanded ? 48 : 24,
|
|
140
|
+
maxHeight
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
/* @__PURE__ */ jsxs(
|
|
146
|
+
"div",
|
|
147
|
+
{
|
|
148
|
+
className: cn(
|
|
149
|
+
"flex shrink-0 items-center",
|
|
150
|
+
isExpanded ? "justify-between px-3 pt-1 pb-3" : "gap-1.5"
|
|
151
|
+
),
|
|
152
|
+
children: [
|
|
153
|
+
isExpanded && /* @__PURE__ */ jsx(
|
|
154
|
+
"button",
|
|
155
|
+
{
|
|
156
|
+
type: "button",
|
|
157
|
+
onClick: onPlusClick,
|
|
158
|
+
disabled,
|
|
159
|
+
className: cn(
|
|
160
|
+
"flex shrink-0 items-center justify-center rounded-xl transition-colors",
|
|
161
|
+
"bg-muted text-muted-foreground size-9",
|
|
162
|
+
"hover:bg-accent hover:text-foreground",
|
|
163
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
164
|
+
),
|
|
165
|
+
"aria-label": "Add attachment",
|
|
166
|
+
children: plusButtonIcon ?? /* @__PURE__ */ jsx(Plus, { className: "size-4" })
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
170
|
+
beforeSubmitSlot,
|
|
171
|
+
/* @__PURE__ */ jsx(
|
|
172
|
+
"button",
|
|
173
|
+
{
|
|
174
|
+
type: "button",
|
|
175
|
+
onClick: handleSubmit,
|
|
176
|
+
disabled: disabled || isEmpty,
|
|
177
|
+
className: cn(
|
|
178
|
+
"flex shrink-0 items-center justify-center rounded-xl transition-colors",
|
|
179
|
+
"bg-primary text-primary-foreground size-9",
|
|
180
|
+
"hover:bg-primary/90",
|
|
181
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
182
|
+
),
|
|
183
|
+
"aria-label": "Send message",
|
|
184
|
+
children: submitButtonIcon ?? /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" })
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
] })
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
] })
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export { CompactPromptArea };
|
|
197
|
+
//# sourceMappingURL=chunk-BPJO4DGM.js.map
|
|
198
|
+
//# sourceMappingURL=chunk-BPJO4DGM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/compact-prompt-area/compact-prompt-area.tsx"],"names":[],"mappings":"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","file":"chunk-BPJO4DGM.js","sourcesContent":["'use client'\n\nimport { useCallback, useImperativeHandle, useRef, useState } from 'react'\nimport { cn } from '@/lib/utils'\nimport { PromptArea } from '@/registry/new-york/blocks/prompt-area/prompt-area'\nimport { BLUR_DELAY_MS } from '@/registry/new-york/blocks/prompt-area/use-prompt-area-events'\nimport type { PromptAreaHandle } from '@/registry/new-york/blocks/prompt-area/types'\nimport type { CompactPromptAreaProps } from './types'\n\ntype IconProps = { className?: string }\n\n/** Shared SVG wrapper matching the lucide icon defaults (no dependency). */\nfunction Svg({ className, children }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className={className}>\n {children}\n </svg>\n )\n}\n\nconst Plus = ({ className }: IconProps) => (\n <Svg className={className}>\n <path d=\"M5 12h14\" />\n <path d=\"M12 5v14\" />\n </Svg>\n)\nconst ArrowUp = ({ className }: IconProps) => (\n <Svg className={className}>\n <path d=\"m5 12 7-7 7 7\" />\n <path d=\"M12 19V5\" />\n </Svg>\n)\n\n/**\n * CompactPromptArea – A pill-shaped prompt input that sits on a single row\n * and expands downward on focus.\n *\n * - Left: circular plus button\n * - Middle: PromptArea text input (expands down when focused)\n * - Right: optional slot + circular submit button\n *\n * Reuses PromptArea internally with autoGrow enabled.\n *\n * @example\n * ```tsx\n * <CompactPromptArea\n * value={segments}\n * onChange={setSegments}\n * placeholder=\"Ask anything...\"\n * onSubmit={handleSubmit}\n * onPlusClick={() => setMenuOpen(true)}\n * beforeSubmitSlot={<button aria-label=\"Voice\"><Mic className=\"size-4\" /></button>}\n * />\n * ```\n */\nexport function CompactPromptArea({\n value,\n onChange,\n triggers,\n placeholder,\n disabled = false,\n markdown,\n onSubmit,\n onEscape,\n onChipClick,\n onChipAdd,\n onChipDelete,\n onPaste,\n images,\n onImagePaste,\n onImageRemove,\n files,\n onFileRemove,\n plusButtonIcon,\n onPlusClick,\n submitButtonIcon,\n beforeSubmitSlot,\n maxHeight = 320,\n className,\n 'aria-label': ariaLabel,\n 'data-test-id': dataTestId,\n ref,\n}: CompactPromptAreaProps & { ref?: React.Ref<PromptAreaHandle> }) {\n const promptRef = useRef<PromptAreaHandle>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const [isFocused, setIsFocused] = useState(false)\n\n useImperativeHandle(ref, () => promptRef.current!, [])\n\n const isEmpty =\n value.length === 0 || (value.length === 1 && value[0].type === 'text' && value[0].text === '')\n\n const isExpanded = isFocused || !isEmpty\n\n const handleContainerFocus = useCallback(() => {\n setIsFocused(true)\n }, [])\n\n const handleContainerBlur = useCallback(() => {\n setTimeout(() => {\n if (!containerRef.current?.contains(document.activeElement)) {\n setIsFocused(false)\n }\n }, BLUR_DELAY_MS)\n }, [])\n\n const handleSubmit = useCallback(() => {\n onSubmit?.(value)\n }, [onSubmit, value])\n\n return (\n <div\n ref={containerRef}\n onFocus={handleContainerFocus}\n onBlur={handleContainerBlur}\n aria-label={ariaLabel}\n data-test-id={dataTestId}\n className={cn(\n 'compact-prompt-area',\n 'bg-background border transition-all duration-200 ease-out',\n isExpanded ? 'rounded-2xl' : 'rounded-full',\n className,\n )}>\n <div className={cn('flex', isExpanded ? 'flex-col' : 'items-center p-1.5')}>\n {/* Plus button – left side in collapsed mode */}\n {!isExpanded && (\n <button\n type=\"button\"\n onClick={onPlusClick}\n disabled={disabled}\n className={cn(\n 'flex shrink-0 items-center justify-center rounded-xl transition-colors',\n 'bg-muted text-muted-foreground size-9',\n 'hover:bg-accent hover:text-foreground',\n 'disabled:pointer-events-none disabled:opacity-50',\n )}\n aria-label=\"Add attachment\">\n {plusButtonIcon ?? <Plus className=\"size-4\" />}\n </button>\n )}\n\n {/* Prompt area region */}\n <div\n className={cn('min-w-0 flex-1', isExpanded ? 'px-5 pt-4 pb-2' : 'overflow-hidden px-3')}\n onClick={() => promptRef.current?.focus()}>\n <PromptArea\n ref={promptRef}\n value={value}\n onChange={onChange}\n triggers={triggers}\n placeholder={placeholder}\n disabled={disabled}\n markdown={markdown}\n onSubmit={handleSubmit}\n onEscape={onEscape}\n onChipClick={onChipClick}\n onChipAdd={onChipAdd}\n onChipDelete={onChipDelete}\n onPaste={onPaste}\n images={images}\n onImagePaste={onImagePaste}\n onImageRemove={onImageRemove}\n files={files}\n onFileRemove={onFileRemove}\n autoGrow\n minHeight={isExpanded ? 48 : 24}\n maxHeight={maxHeight}\n />\n </div>\n\n {/* Button bar */}\n <div\n className={cn(\n 'flex shrink-0 items-center',\n isExpanded ? 'justify-between px-3 pt-1 pb-3' : 'gap-1.5',\n )}>\n {/* Plus button – bottom-left in expanded mode */}\n {isExpanded && (\n <button\n type=\"button\"\n onClick={onPlusClick}\n disabled={disabled}\n className={cn(\n 'flex shrink-0 items-center justify-center rounded-xl transition-colors',\n 'bg-muted text-muted-foreground size-9',\n 'hover:bg-accent hover:text-foreground',\n 'disabled:pointer-events-none disabled:opacity-50',\n )}\n aria-label=\"Add attachment\">\n {plusButtonIcon ?? <Plus className=\"size-4\" />}\n </button>\n )}\n\n {/* Right side: slot + submit */}\n <div className=\"flex items-center gap-1.5\">\n {beforeSubmitSlot}\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={disabled || isEmpty}\n className={cn(\n 'flex shrink-0 items-center justify-center rounded-xl transition-colors',\n 'bg-primary text-primary-foreground size-9',\n 'hover:bg-primary/90',\n 'disabled:pointer-events-none disabled:opacity-50',\n )}\n aria-label=\"Send message\">\n {submitButtonIcon ?? <ArrowUp className=\"size-4\" />}\n </button>\n </div>\n </div>\n </div>\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { cn } from './chunk-NF2LHZIE.js';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function ActionBar({
|
|
6
|
+
left,
|
|
7
|
+
right,
|
|
8
|
+
className,
|
|
9
|
+
disabled = false,
|
|
10
|
+
"aria-label": ariaLabel,
|
|
11
|
+
"data-test-id": dataTestId,
|
|
12
|
+
ref
|
|
13
|
+
}) {
|
|
14
|
+
return /* @__PURE__ */ jsxs(
|
|
15
|
+
"div",
|
|
16
|
+
{
|
|
17
|
+
ref,
|
|
18
|
+
role: "toolbar",
|
|
19
|
+
"aria-label": ariaLabel ?? "Action bar",
|
|
20
|
+
"aria-disabled": disabled || void 0,
|
|
21
|
+
"data-test-id": dataTestId,
|
|
22
|
+
className: cn(
|
|
23
|
+
"action-bar",
|
|
24
|
+
"flex items-center justify-between gap-2 pt-2",
|
|
25
|
+
disabled && "pointer-events-none opacity-50",
|
|
26
|
+
className
|
|
27
|
+
),
|
|
28
|
+
children: [
|
|
29
|
+
left && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: left }),
|
|
30
|
+
right && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1", children: right })
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { ActionBar };
|
|
37
|
+
//# sourceMappingURL=chunk-BWVBDP7C.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-BWVBDP7C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/action-bar/action-bar.tsx"],"names":[],"mappings":";;;AAiCO,SAAS,SAAA,CAAU;AAAA,EACxB,IAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,YAAA,EAAc,SAAA;AAAA,EACd,cAAA,EAAgB,UAAA;AAAA,EAChB;AACF,CAAA,EAAyD;AACvD,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA;AAAA,MACA,IAAA,EAAK,SAAA;AAAA,MACL,cAAY,SAAA,IAAa,YAAA;AAAA,MACzB,iBAAe,QAAA,IAAY,MAAA;AAAA,MAC3B,cAAA,EAAc,UAAA;AAAA,MACd,SAAA,EAAW,EAAA;AAAA,QACT,YAAA;AAAA,QACA,8CAAA;AAAA,QACA,QAAA,IAAY,gCAAA;AAAA,QACZ;AAAA,OACF;AAAA,MACC,QAAA,EAAA;AAAA,QAAA,IAAA,oBAAQ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EAA2B,QAAA,EAAA,IAAA,EAAK,CAAA;AAAA,QACvD,KAAA,oBAAS,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mCAAmC,QAAA,EAAA,KAAA,EAAM;AAAA;AAAA;AAAA,GACpE;AAEJ","file":"chunk-BWVBDP7C.js","sourcesContent":["'use client'\n\nimport { cn } from '@/lib/utils'\nimport type { ActionBarProps } from './types'\n\n/**\n * ActionBar - A horizontal toolbar with left and right slots.\n *\n * Designed to sit below a text input (e.g., PromptArea) and stay\n * anchored via normal document flow. Place it as a sibling after\n * the input inside a shared wrapper.\n *\n * @example\n * ```tsx\n * <div className=\"rounded-lg border p-4\">\n * <PromptArea value={segments} onChange={setSegments} ... />\n * <ActionBar\n * left={\n * <>\n * <button><PlusCircle /></button>\n * <button><AtSign /></button>\n * </>\n * }\n * right={\n * <>\n * <button><Mic /></button>\n * <button onClick={handleSubmit}><ArrowUp /></button>\n * </>\n * }\n * />\n * </div>\n * ```\n */\nexport function ActionBar({\n left,\n right,\n className,\n disabled = false,\n 'aria-label': ariaLabel,\n 'data-test-id': dataTestId,\n ref,\n}: ActionBarProps & { ref?: React.Ref<HTMLDivElement> }) {\n return (\n <div\n ref={ref}\n role=\"toolbar\"\n aria-label={ariaLabel ?? 'Action bar'}\n aria-disabled={disabled || undefined}\n data-test-id={dataTestId}\n className={cn(\n 'action-bar',\n 'flex items-center justify-between gap-2 pt-2',\n disabled && 'pointer-events-none opacity-50',\n className,\n )}>\n {left && <div className=\"flex items-center gap-1\">{left}</div>}\n {right && <div className=\"ml-auto flex items-center gap-1\">{right}</div>}\n </div>\n )\n}\n"]}
|