@fluencypassdevs/cycle 1.17.1 → 1.20.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/bin/cli.mjs +2 -0
- package/bin/mcp.mjs +95 -0
- package/bin/propose.mjs +127 -0
- package/cli/CLAUDE-section.md +4 -3
- package/dist/chunk-2Z64TSF6.js +32 -0
- package/dist/chunk-2Z64TSF6.js.map +1 -0
- package/dist/{chunk-2M73I67G.js → chunk-7I2M55D7.js} +3 -3
- package/dist/{chunk-2M73I67G.js.map → chunk-7I2M55D7.js.map} +1 -1
- package/dist/{chunk-SYD6ELLP.js → chunk-EAVNA2RW.js} +3 -3
- package/dist/{chunk-SYD6ELLP.js.map → chunk-EAVNA2RW.js.map} +1 -1
- package/dist/chunk-JNAJZM2L.js +87 -0
- package/dist/chunk-JNAJZM2L.js.map +1 -0
- package/dist/{chunk-4HNTMD2U.js → chunk-NKBTC4IF.js} +3 -3
- package/dist/{chunk-4HNTMD2U.js.map → chunk-NKBTC4IF.js.map} +1 -1
- package/dist/chunk-ONCWQO67.js +30 -0
- package/dist/chunk-ONCWQO67.js.map +1 -0
- package/dist/chunk-SKBY6OZL.js +50 -0
- package/dist/chunk-SKBY6OZL.js.map +1 -0
- package/dist/chunk-U6LYHTVH.js +109 -0
- package/dist/chunk-U6LYHTVH.js.map +1 -0
- package/dist/{chunk-A3L7MI2Y.js → chunk-VUVC2NAK.js} +3 -3
- package/dist/{chunk-A3L7MI2Y.js.map → chunk-VUVC2NAK.js.map} +1 -1
- package/dist/composites/chat-thread.js +4 -4
- package/dist/composites/selector-bar.d.ts +21 -0
- package/dist/composites/selector-bar.js +10 -0
- package/dist/composites/selector-bar.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +34 -29
- package/dist/marketing/effects/index.js +3 -3
- package/dist/marketing/motion/index.js +2 -2
- package/dist/marketing/typography/index.js +1 -1
- package/dist/ui/breadcrumb.d.ts +14 -0
- package/dist/ui/breadcrumb.js +10 -0
- package/dist/ui/breadcrumb.js.map +1 -0
- package/dist/ui/card.d.ts +12 -0
- package/dist/ui/card.js +6 -0
- package/dist/ui/card.js.map +1 -0
- package/dist/ui/chat-message.js +2 -2
- package/dist/ui/chat-panel.js +3 -3
- package/dist/ui/collapsible.d.ts +9 -0
- package/dist/ui/collapsible.js +5 -0
- package/dist/ui/collapsible.js.map +1 -0
- package/dist/ui/editor.js +2 -2
- package/dist/ui/live-waiting.js +2 -2
- package/dist/ui/separator.d.ts +7 -0
- package/dist/ui/separator.js +6 -0
- package/dist/ui/separator.js.map +1 -0
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
package/bin/mcp.mjs
CHANGED
|
@@ -94,6 +94,78 @@ const COMPONENTS = [
|
|
|
94
94
|
</Accordion>`,
|
|
95
95
|
keywords: ["accordion", "colapsavel", "expandir", "secao", "faq", "pergunta"],
|
|
96
96
|
},
|
|
97
|
+
{
|
|
98
|
+
name: "Collapsible",
|
|
99
|
+
import: `import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@fluencypassdevs/cycle"`,
|
|
100
|
+
description: "Regiao expansivel/recolhivel sem estilo imposto (Radix). Primitivo para secoes opcionais de formulario — para listas estilizadas use Accordion.",
|
|
101
|
+
props: [
|
|
102
|
+
{ name: "defaultOpen", type: "boolean", default: "false" },
|
|
103
|
+
{ name: "open", type: "boolean", default: "-" },
|
|
104
|
+
{ name: "onOpenChange", type: "(open: boolean) => void", default: "-" },
|
|
105
|
+
{ name: "disabled", type: "boolean", default: "false" },
|
|
106
|
+
],
|
|
107
|
+
example: `<Collapsible>
|
|
108
|
+
<CollapsibleTrigger asChild>
|
|
109
|
+
<Button variant="ghost" size="sm">Opcoes avancadas</Button>
|
|
110
|
+
</CollapsibleTrigger>
|
|
111
|
+
<CollapsibleContent>Campos opcionais aqui</CollapsibleContent>
|
|
112
|
+
</Collapsible>`,
|
|
113
|
+
keywords: ["collapsible", "colapsavel", "expandir", "recolher", "toggle", "secao opcional", "avancado"],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "Separator",
|
|
117
|
+
import: `import { Separator } from "@fluencypassdevs/cycle"`,
|
|
118
|
+
description: "Linha divisoria horizontal ou vertical acessivel (Radix), usando o token bg-border. Decorative por padrao.",
|
|
119
|
+
props: [
|
|
120
|
+
{ name: "orientation", type: '"horizontal" | "vertical"', default: '"horizontal"' },
|
|
121
|
+
{ name: "decorative", type: "boolean", default: "true" },
|
|
122
|
+
],
|
|
123
|
+
example: `<Separator />
|
|
124
|
+
<div className="flex h-5 items-center gap-4">
|
|
125
|
+
<span>Docs</span>
|
|
126
|
+
<Separator orientation="vertical" />
|
|
127
|
+
<span>Componentes</span>
|
|
128
|
+
</div>`,
|
|
129
|
+
keywords: ["separator", "divisor", "divider", "linha", "hr", "secao"],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "Card",
|
|
133
|
+
import: `import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter } from "@fluencypassdevs/cycle"`,
|
|
134
|
+
description: "Container de superficie com header, titulo, descricao, acao, conteudo e rodape. bg-card, border, rounded-xl, shadow-sm.",
|
|
135
|
+
props: [
|
|
136
|
+
{ name: "className", type: "string", default: "-" },
|
|
137
|
+
],
|
|
138
|
+
example: `<Card>
|
|
139
|
+
<CardHeader>
|
|
140
|
+
<CardTitle>Titulo</CardTitle>
|
|
141
|
+
<CardDescription>Descricao do card</CardDescription>
|
|
142
|
+
<CardAction><Badge variant="secondary">Novo</Badge></CardAction>
|
|
143
|
+
</CardHeader>
|
|
144
|
+
<CardContent>Conteudo</CardContent>
|
|
145
|
+
<CardFooter><Button size="sm">Salvar</Button></CardFooter>
|
|
146
|
+
</Card>`,
|
|
147
|
+
keywords: ["card", "cartao", "container", "superficie", "painel", "bloco", "agrupamento"],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "Breadcrumb",
|
|
151
|
+
import: `import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@fluencypassdevs/cycle"`,
|
|
152
|
+
description: "Trilha de navegacao hierarquica com links, item atual (aria-current), separador ChevronRight (via CycleIcon) e ellipsis para caminhos longos. BreadcrumbLink aceita asChild para Next Link.",
|
|
153
|
+
props: [
|
|
154
|
+
{ name: "asChild (BreadcrumbLink)", type: "boolean", default: "false" },
|
|
155
|
+
],
|
|
156
|
+
example: `<Breadcrumb>
|
|
157
|
+
<BreadcrumbList>
|
|
158
|
+
<BreadcrumbItem>
|
|
159
|
+
<BreadcrumbLink href="/trilhas">Trilhas</BreadcrumbLink>
|
|
160
|
+
</BreadcrumbItem>
|
|
161
|
+
<BreadcrumbSeparator />
|
|
162
|
+
<BreadcrumbItem>
|
|
163
|
+
<BreadcrumbPage>Modulo 3</BreadcrumbPage>
|
|
164
|
+
</BreadcrumbItem>
|
|
165
|
+
</BreadcrumbList>
|
|
166
|
+
</Breadcrumb>`,
|
|
167
|
+
keywords: ["breadcrumb", "navegacao", "trilha", "hierarquia", "caminho", "migalha"],
|
|
168
|
+
},
|
|
97
169
|
{
|
|
98
170
|
name: "Tabs",
|
|
99
171
|
import: `import { Tabs, TabsList, TabsTrigger, TabsContent } from "@fluencypassdevs/cycle"`,
|
|
@@ -487,6 +559,29 @@ const COMPONENTS = [
|
|
|
487
559
|
<MessageBar state="recording" recordingDuration={duration} recordingStream={stream} maxRecordingDuration={30} warnAtSecondsLeft={5} onMaxDurationReached={() => pause()} onPauseRecording={pause} onSendAudio={send} />`,
|
|
488
560
|
keywords: ["chat", "input", "message", "bar", "audio", "recording", "microphone", "mensagem", "envio", "press-to-record", "waveform", "playback", "whatsapp"],
|
|
489
561
|
},
|
|
562
|
+
{
|
|
563
|
+
name: "SelectorBar",
|
|
564
|
+
import: `import { SelectorBar } from "@fluencypassdevs/cycle"`,
|
|
565
|
+
description: "Barra seletora de contexto (mobile-first): overline (rotulo pequeno) + titulo + slot badge a direita + affordance de seletor (ChevronsUpDown via CycleIcon). Trigger tipico de um Drawer (bottom sheet) com a lista de opcoes — compoe com <DrawerTrigger asChild> (forwardRef). aria-haspopup=dialog + aria-expanded via prop open. Nao usar para disclosure inline (Collapsible) nem navegacao hierarquica (Breadcrumb). Origem: barra de unidades do Class mobile.",
|
|
566
|
+
props: [
|
|
567
|
+
{ name: "title", type: "string", default: "-", note: "obrigatoria — titulo principal (ex: unidade atual)" },
|
|
568
|
+
{ name: "overline", type: "string", default: "-", note: "rotulo pequeno acima do titulo (ex: nome do curso)" },
|
|
569
|
+
{ name: "badge", type: "React.ReactNode", default: "-", note: "slot a direita, antes do icone (ex: <Badge variant='progress'>0/7</Badge>)" },
|
|
570
|
+
{ name: "open", type: "boolean", default: "-", note: "estado da superficie controlada (reflete em aria-expanded)" },
|
|
571
|
+
],
|
|
572
|
+
example: `<Drawer open={open} onOpenChange={setOpen}>
|
|
573
|
+
<DrawerTrigger asChild>
|
|
574
|
+
<SelectorBar
|
|
575
|
+
overline="Beginner 1"
|
|
576
|
+
title="Family and Jobs"
|
|
577
|
+
badge={<Badge variant="progress" size="sm">0/7</Badge>}
|
|
578
|
+
open={open}
|
|
579
|
+
/>
|
|
580
|
+
</DrawerTrigger>
|
|
581
|
+
<DrawerContent>{/* lista de opcoes */}</DrawerContent>
|
|
582
|
+
</Drawer>`,
|
|
583
|
+
keywords: ["selector", "bar", "seletor", "contexto", "unidade", "curso", "bottom sheet", "drawer", "trigger", "mobile", "trocar"],
|
|
584
|
+
},
|
|
490
585
|
{
|
|
491
586
|
name: "ChatThread",
|
|
492
587
|
import: `import { ChatThread, type ChatThreadMessage } from "@fluencypassdevs/cycle"`,
|
package/bin/propose.mjs
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `npx cycle propose <arquivo>` — propõe um componente ao Design System a partir
|
|
5
|
+
* do PRÓPRIO projeto do dev, sem abrir o GitHub na mão.
|
|
6
|
+
*
|
|
7
|
+
* Lê o componente (idealmente de `src/components/ds-proposals/`), pergunta o
|
|
8
|
+
* contexto, e abre a issue "Proposta de Componente" no repo do DS via `gh`
|
|
9
|
+
* (login do próprio dev — todos são da org). Se o `gh` não existir, cai num
|
|
10
|
+
* fallback: salva o corpo num arquivo + imprime um link pré-preenchido.
|
|
11
|
+
*
|
|
12
|
+
* Flags: --name, --what, --where, --preview (não-interativo) · --dry-run (só imprime).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from "fs"
|
|
16
|
+
import path from "path"
|
|
17
|
+
import { execFileSync, spawnSync } from "child_process"
|
|
18
|
+
import { createInterface } from "readline"
|
|
19
|
+
|
|
20
|
+
// ── Config específica do DS (o pulse tem a sua) ──
|
|
21
|
+
const DS = {
|
|
22
|
+
label: "Cycle",
|
|
23
|
+
repo: "Fluencypass/Cycle",
|
|
24
|
+
surfaceHint: "Tela de produto (app logado, painel, aula)",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const argv = process.argv.slice(2)
|
|
28
|
+
const flags = {}
|
|
29
|
+
const positionals = []
|
|
30
|
+
for (let i = 0; i < argv.length; i++) {
|
|
31
|
+
const a = argv[i]
|
|
32
|
+
if (a === "--dry-run") flags.dryRun = true
|
|
33
|
+
else if (a.startsWith("--")) flags[a.slice(2)] = argv[++i]
|
|
34
|
+
else positionals.push(a)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fail(msg) {
|
|
38
|
+
console.error(`\n ✗ ${msg}\n`)
|
|
39
|
+
process.exit(1)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 1. Arquivo do componente
|
|
43
|
+
let file = positionals[0]
|
|
44
|
+
if (!file) {
|
|
45
|
+
// tenta achar candidatos em ds-proposals/
|
|
46
|
+
const dir = path.join(process.cwd(), "src/components/ds-proposals")
|
|
47
|
+
const found = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => /\.(tsx|jsx|ts)$/.test(f)) : []
|
|
48
|
+
if (found.length === 1) file = path.join(dir, found[0])
|
|
49
|
+
else if (found.length > 1) fail(`Vários candidatos em ds-proposals/. Especifique: npx cycle propose <arquivo>\n ${found.join("\n ")}`)
|
|
50
|
+
else fail("Uso: npx cycle propose <caminho-do-componente> (ex.: src/components/ds-proposals/meu-card.tsx)")
|
|
51
|
+
}
|
|
52
|
+
if (!fs.existsSync(file)) fail(`Arquivo não encontrado: ${file}`)
|
|
53
|
+
const code = fs.readFileSync(file, "utf-8")
|
|
54
|
+
|
|
55
|
+
// 2. Metadados (flags ou prompt)
|
|
56
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
57
|
+
const ask = (q, def) =>
|
|
58
|
+
new Promise((res) => rl.question(` ${q}${def ? ` (${def})` : ""}: `, (a) => res(a.trim() || def || "")))
|
|
59
|
+
|
|
60
|
+
const name = flags.name || path.basename(file).replace(/\.(tsx|jsx|ts)$/, "")
|
|
61
|
+
|
|
62
|
+
console.log(`\n Propor componente ao ${DS.label} Design System\n ─────────────────────────────────────────────\n`)
|
|
63
|
+
|
|
64
|
+
const what = flags.what || (await ask("O que ele faz? (1 frase)"))
|
|
65
|
+
const where = flags.where || (await ask("Onde ele aparece?", DS.surfaceHint))
|
|
66
|
+
const preview = flags.preview || (await ask("Link de preview ou screenshot (opcional)"))
|
|
67
|
+
rl.close()
|
|
68
|
+
|
|
69
|
+
// 3. Corpo da issue
|
|
70
|
+
const title = `[Proposta] ${name}`
|
|
71
|
+
const body = `## Componente
|
|
72
|
+
**${name}**
|
|
73
|
+
|
|
74
|
+
## O que faz
|
|
75
|
+
${what || "(não informado)"}
|
|
76
|
+
|
|
77
|
+
## Onde aparece
|
|
78
|
+
${where}
|
|
79
|
+
|
|
80
|
+
## Preview
|
|
81
|
+
${preview || "(anexar print/link)"}
|
|
82
|
+
|
|
83
|
+
## Código
|
|
84
|
+
\`\`\`tsx
|
|
85
|
+
${code}
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
_Proposto via \`npx cycle propose\` a partir de um projeto consumidor._`
|
|
90
|
+
|
|
91
|
+
// 4. dry-run
|
|
92
|
+
if (flags.dryRun) {
|
|
93
|
+
console.log(`\n [dry-run] Issue que seria criada em ${DS.repo}:\n`)
|
|
94
|
+
console.log(` Título: ${title}`)
|
|
95
|
+
console.log(` Label: ds-proposal\n`)
|
|
96
|
+
console.log(body.split("\n").map((l) => " │ " + l).join("\n"))
|
|
97
|
+
console.log("")
|
|
98
|
+
process.exit(0)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 5. Cria a issue via gh (ou fallback)
|
|
102
|
+
const hasGh = spawnSync("gh", ["--version"], { stdio: "ignore" }).status === 0
|
|
103
|
+
if (hasGh) {
|
|
104
|
+
const tmp = path.join(process.cwd(), `.ds-proposal-${name}.md`)
|
|
105
|
+
fs.writeFileSync(tmp, body)
|
|
106
|
+
try {
|
|
107
|
+
const url = execFileSync(
|
|
108
|
+
"gh",
|
|
109
|
+
["issue", "create", "--repo", DS.repo, "--title", title, "--label", "ds-proposal", "--body-file", tmp],
|
|
110
|
+
{ encoding: "utf-8" }
|
|
111
|
+
).trim()
|
|
112
|
+
fs.unlinkSync(tmp)
|
|
113
|
+
console.log(`\n ✓ Proposta enviada! ${url}\n Você será notificado quando for revisada.\n`)
|
|
114
|
+
} catch (err) {
|
|
115
|
+
fs.unlinkSync(tmp)
|
|
116
|
+
fail(`Falha ao criar a issue via gh (${err.message}). Rode \`gh auth login\` e tente de novo, ou use o fallback abaixo.`)
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
const out = path.join(process.cwd(), `ds-proposal-${name}.md`)
|
|
120
|
+
fs.writeFileSync(out, body)
|
|
121
|
+
const url = `https://github.com/${DS.repo}/issues/new?labels=ds-proposal&title=${encodeURIComponent(title)}`
|
|
122
|
+
console.log(`\n 'gh' não encontrado. Fallback:\n`)
|
|
123
|
+
console.log(` 1. Corpo salvo em: ${out}`)
|
|
124
|
+
console.log(` 2. Abra: ${url}`)
|
|
125
|
+
console.log(` 3. Cole o conteúdo do arquivo no corpo da issue.\n`)
|
|
126
|
+
console.log(` (Instale o GitHub CLI — https://cli.github.com — pra automatizar isso.)\n`)
|
|
127
|
+
}
|
package/cli/CLAUDE-section.md
CHANGED
|
@@ -166,9 +166,10 @@ O DS **nao trava** o projeto. Se um componente REALMENTE nao existe (confirme an
|
|
|
166
166
|
`components/ui/`. O componente ja funciona e pode ser usado no projeto.
|
|
167
167
|
2. Componha com os atomos que ja existem (Button, Input...) e use SO tokens semanticos
|
|
168
168
|
(`bg-brand`, `text-muted-foreground`) — nunca cor/spacing hardcoded.
|
|
169
|
-
3. Se for reutilizavel por outros projetos, **proponha a entrada no DS
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
3. Se for reutilizavel por outros projetos, **proponha a entrada no DS** rodando no projeto:
|
|
170
|
+
\`npx cycle propose src/components/ds-proposals/<arquivo>\` (abre a issue automaticamente via
|
|
171
|
+
gh). Fallback manual: https://github.com/Fluencypass/Cycle/issues/new/choose. Guia:
|
|
172
|
+
https://design.fluencypass.com/cycle/contribuir
|
|
172
173
|
|
|
173
174
|
**Regra de superficie**: o DS certo depende de ONDE o componente renderiza — tela de produto
|
|
174
175
|
(app logado) = **Cycle**; landing/campanha de marketing = **Pulse**. Nunca misture os dois no
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { cn } from './chunk-W4KJCBMW.js';
|
|
2
|
+
import { __objRest, __spreadValues } from './chunk-2NMEKWO5.js';
|
|
3
|
+
import { Separator as Separator$1 } from 'radix-ui';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function Separator(_a) {
|
|
7
|
+
var _b = _a, {
|
|
8
|
+
className,
|
|
9
|
+
orientation = "horizontal",
|
|
10
|
+
decorative = true
|
|
11
|
+
} = _b, props = __objRest(_b, [
|
|
12
|
+
"className",
|
|
13
|
+
"orientation",
|
|
14
|
+
"decorative"
|
|
15
|
+
]);
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
Separator$1.Root,
|
|
18
|
+
__spreadValues({
|
|
19
|
+
"data-slot": "separator",
|
|
20
|
+
decorative,
|
|
21
|
+
orientation,
|
|
22
|
+
className: cn(
|
|
23
|
+
"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
24
|
+
className
|
|
25
|
+
)
|
|
26
|
+
}, props)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Separator };
|
|
31
|
+
//# sourceMappingURL=chunk-2Z64TSF6.js.map
|
|
32
|
+
//# sourceMappingURL=chunk-2Z64TSF6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../core/src/components/ui/separator.tsx"],"names":["SeparatorPrimitive"],"mappings":";;;;;AAOA,SAAS,UAAU,EAAA,EAKsC;AALtC,EAAA,IAAA,EAAA,GAAA,EAAA,EACjB;AAAA,IAAA,SAAA;AAAA,IACA,WAAA,GAAc,YAAA;AAAA,IACd,UAAA,GAAa;AAAA,GAVf,GAOmB,EAAA,EAId,KAAA,GAAA,SAAA,CAJc,EAAA,EAId;AAAA,IAHH,WAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAGA,EAAA,uBACE,GAAA;AAAA,IAACA,WAAA,CAAmB,IAAA;AAAA,IAAnB,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,WAAA;AAAA,MACV,UAAA;AAAA,MACA,WAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,gKAAA;AAAA,QACA;AAAA;AACF,KAAA,EACI,KAAA;AAAA,GACN;AAEJ","file":"chunk-2Z64TSF6.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Separator as SeparatorPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@core/lib/utils\"\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n decorative = true,\n ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n return (\n <SeparatorPrimitive.Root\n data-slot=\"separator\"\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Separator }\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from './chunk-6AZLLBX6.js';
|
|
2
|
-
import { CycleIcon } from './chunk-US5LXJTO.js';
|
|
3
2
|
import { toggleVariants } from './chunk-FVWCKX3O.js';
|
|
3
|
+
import { CycleIcon } from './chunk-US5LXJTO.js';
|
|
4
4
|
import { cn } from './chunk-W4KJCBMW.js';
|
|
5
5
|
import { __spreadValues } from './chunk-2NMEKWO5.js';
|
|
6
6
|
import * as React from 'react';
|
|
@@ -299,5 +299,5 @@ var Editor = React.forwardRef(
|
|
|
299
299
|
);
|
|
300
300
|
|
|
301
301
|
export { Editor };
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
303
|
-
//# sourceMappingURL=chunk-
|
|
302
|
+
//# sourceMappingURL=chunk-7I2M55D7.js.map
|
|
303
|
+
//# sourceMappingURL=chunk-7I2M55D7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/ui/editor.tsx"],"names":["UnderlineIcon","Editor","_a"],"mappings":"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","file":"chunk-2M73I67G.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useEditor, EditorContent, type Editor as TiptapEditor } from \"@tiptap/react\"\nimport StarterKit from \"@tiptap/starter-kit\"\nimport Underline from \"@tiptap/extension-underline\"\nimport Placeholder from \"@tiptap/extension-placeholder\"\nimport {\n Bold,\n Italic,\n Underline as UnderlineIcon,\n Strikethrough,\n Heading2,\n List,\n ListOrdered,\n Undo2,\n Redo2,\n type LucideIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { toggleVariants } from \"@/components/ui/toggle\"\nimport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from \"@/components/ui/tooltip\"\nimport { CycleIcon } from \"@/components/icons\"\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface EditorRef {\n /** The Tiptap editor instance */\n editor: TiptapEditor | null\n /** Get current HTML content */\n getHTML: () => string\n /** Get current plain-text content */\n getText: () => string\n /** Clear the editor */\n clear: () => void\n /** Focus the editor */\n focus: () => void\n}\n\nexport interface EditorProps {\n /** HTML content (controlled) */\n content?: string\n /** Default HTML content (uncontrolled) */\n defaultContent?: string\n /** Called when content changes with the HTML string */\n onChange?: (html: string) => void\n /** Placeholder text shown when the editor is empty */\n placeholder?: string\n /** Whether the editor is editable */\n editable?: boolean\n /** Additional class for the outer wrapper */\n className?: string\n /** Auto-focus the editor on mount */\n autoFocus?: boolean\n /** Max height of the content area — scrolls when exceeded (e.g. \"300px\", \"50vh\") */\n maxHeight?: string\n /** Min height of the content area (default: \"120px\") */\n minHeight?: string\n /** Allow the user to resize the editor vertically */\n resizable?: boolean\n /** Theme class for the toolbar pressed state (e.g. \"theme-class\", \"theme-brand\") */\n toolbarTheme?: string\n}\n\n// ── Toolbar internals ────────────────────────────────────────────────\n\nfunction ToolbarDivider() {\n return <div className=\"mx-1 h-5 w-px bg-neutral-border\" aria-hidden=\"true\" />\n}\n\ninterface ToolbarToggleProps {\n pressed: boolean\n onToggle: () => void\n icon: LucideIcon\n label: string\n disabled?: boolean\n}\n\nfunction ToolbarToggle({ pressed, onToggle, icon, label, disabled }: ToolbarToggleProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className={cn(\n toggleVariants({ variant: \"default\", size: \"icon-sm\" }),\n pressed && \"bg-accent text-accent-foreground\"\n )}\n onClick={onToggle}\n aria-pressed={pressed}\n aria-label={label}\n disabled={disabled}\n data-state={pressed ? \"on\" : \"off\"}\n >\n <CycleIcon icon={icon} size=\"xs\" decorative />\n </button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n )\n}\n\ninterface ToolbarState {\n isBold: boolean\n isItalic: boolean\n isUnderline: boolean\n isStrike: boolean\n isH2: boolean\n isBulletList: boolean\n isOrderedList: boolean\n canUndo: boolean\n canRedo: boolean\n}\n\nfunction getToolbarState(editor: TiptapEditor): ToolbarState {\n return {\n isBold: editor.isActive(\"bold\"),\n isItalic: editor.isActive(\"italic\"),\n isUnderline: editor.isActive(\"underline\"),\n isStrike: editor.isActive(\"strike\"),\n isH2: editor.isActive(\"heading\", { level: 2 }),\n isBulletList: editor.isActive(\"bulletList\"),\n isOrderedList: editor.isActive(\"orderedList\"),\n canUndo: editor.can().undo(),\n canRedo: editor.can().redo(),\n }\n}\n\nfunction EditorToolbar({ editor, state, theme }: { editor: TiptapEditor; state: ToolbarState; theme?: string }) {\n const {\n isBold, isItalic, isUnderline, isStrike, isH2,\n isBulletList, isOrderedList, canUndo, canRedo,\n } = state\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"flex items-center gap-0.5 overflow-x-auto border-b border-neutral-border px-2 py-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n theme\n )}\n role=\"toolbar\"\n aria-label=\"Formatting options\"\n >\n {/* History */}\n <ToolbarToggle\n pressed={false}\n onToggle={() => editor.chain().focus().undo().run()}\n icon={Undo2}\n label=\"Undo\"\n disabled={!canUndo}\n />\n <ToolbarToggle\n pressed={false}\n onToggle={() => editor.chain().focus().redo().run()}\n icon={Redo2}\n label=\"Redo\"\n disabled={!canRedo}\n />\n\n <ToolbarDivider />\n\n {/* Text formatting */}\n <ToolbarToggle\n pressed={isBold}\n onToggle={() => editor.chain().focus().toggleBold().run()}\n icon={Bold}\n label=\"Bold\"\n />\n <ToolbarToggle\n pressed={isItalic}\n onToggle={() => editor.chain().focus().toggleItalic().run()}\n icon={Italic}\n label=\"Italic\"\n />\n <ToolbarToggle\n pressed={isUnderline}\n onToggle={() => editor.chain().focus().toggleUnderline().run()}\n icon={UnderlineIcon}\n label=\"Underline\"\n />\n <ToolbarToggle\n pressed={isStrike}\n onToggle={() => editor.chain().focus().toggleStrike().run()}\n icon={Strikethrough}\n label=\"Strikethrough\"\n />\n\n <ToolbarDivider />\n\n {/* Heading */}\n <ToolbarToggle\n pressed={isH2}\n onToggle={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n icon={Heading2}\n label=\"Heading 2\"\n />\n\n <ToolbarDivider />\n\n {/* Lists */}\n <ToolbarToggle\n pressed={isBulletList}\n onToggle={() => editor.chain().focus().toggleBulletList().run()}\n icon={List}\n label=\"Bullet list\"\n />\n <ToolbarToggle\n pressed={isOrderedList}\n onToggle={() => editor.chain().focus().toggleOrderedList().run()}\n icon={ListOrdered}\n label=\"Ordered list\"\n />\n </div>\n </TooltipProvider>\n )\n}\n\n// ── Editor ───────────────────────────────────────────────────────────\n\nconst Editor = React.forwardRef<EditorRef, EditorProps>(\n function Editor(\n {\n content,\n defaultContent,\n onChange,\n placeholder = \"Type something...\",\n editable = true,\n className,\n autoFocus = false,\n maxHeight,\n minHeight = \"120px\",\n resizable = false,\n toolbarTheme,\n },\n ref\n ) {\n const [toolbarState, setToolbarState] = React.useState<ToolbarState>({\n isBold: false, isItalic: false, isUnderline: false, isStrike: false,\n isH2: false, isBulletList: false, isOrderedList: false,\n canUndo: false, canRedo: false,\n })\n\n const onChangeRef = React.useRef(onChange)\n onChangeRef.current = onChange\n\n const editor = useEditor({\n immediatelyRender: false,\n extensions: [\n StarterKit.configure({\n heading: { levels: [2] },\n }),\n Underline,\n Placeholder.configure({ placeholder }),\n ],\n content: defaultContent ?? content ?? \"\",\n editable,\n autofocus: autoFocus,\n onUpdate: ({ editor: e }) => {\n onChangeRef.current?.(e.getHTML())\n },\n })\n\n // Subscribe to editor events directly — avoids stale closure issues\n // with useEditor callbacks on React 19\n React.useEffect(() => {\n if (!editor) return\n const sync = () => setToolbarState(getToolbarState(editor))\n sync()\n editor.on(\"transaction\", sync)\n return () => { editor.off(\"transaction\", sync) }\n }, [editor])\n\n // Sync controlled content\n React.useEffect(() => {\n if (content !== undefined && editor && editor.getHTML() !== content) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [content, editor])\n\n // Sync editable\n React.useEffect(() => {\n editor?.setEditable(editable)\n }, [editable, editor])\n\n // Expose ref\n React.useImperativeHandle(ref, () => ({\n editor,\n getHTML: () => editor?.getHTML() ?? \"\",\n getText: () => editor?.getText() ?? \"\",\n clear: () => { editor?.commands.clearContent() },\n focus: () => { editor?.commands.focus() },\n }), [editor])\n\n if (!editor) return null\n\n return (\n <div\n data-editor-wrapper\n className={cn(\n // Base — same tokens as Textarea (outline variant)\n \"w-full rounded-lg border border-neutral-border bg-transparent text-neutral-foreground shadow-xs dark:shadow-none\",\n \"transition-[color,border-color,box-shadow,background-color] outline-none\",\n // Hover\n \"hover:border-ring hover:bg-ring/5\",\n // Focus-within (focus is on nested contenteditable, not the wrapper)\n \"focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 focus-within:bg-ring/5\",\n // Error\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:hover:border-destructive dark:aria-invalid:border-destructive dark:aria-invalid:ring-destructive/40\",\n // Disabled\n !editable && \"opacity-50 cursor-not-allowed pointer-events-none\",\n className\n )}\n >\n {/* Scoped styles: placeholder + Tiptap scroll/sizing */}\n <style>{`\n [data-editor-wrapper] .tiptap {\n min-height: var(--editor-min-h, 120px);\n max-height: var(--editor-max-h, none);\n }\n [data-editor-wrapper] .tiptap p.is-editor-empty:first-child::before {\n color: var(--color-neutral-muted-foreground);\n content: attr(data-placeholder);\n float: left;\n height: 0;\n pointer-events: none;\n }\n `}</style>\n\n <EditorToolbar editor={editor} state={toolbarState} theme={toolbarTheme} />\n\n <EditorContent\n editor={editor}\n className={cn(\n \"text-sm text-foreground\",\n \"selection:bg-primary selection:text-primary-foreground\",\n resizable && \"resize-y\",\n // Scroll on .tiptap directly — Tiptap auto-scrolls to keep cursor in view\n \"[&_.tiptap]:outline-none [&_.tiptap]:overflow-y-auto [&_.tiptap]:px-4 [&_.tiptap]:py-2\",\n \"[&_.tiptap_p]:my-1.5 [&_.tiptap_p]:leading-relaxed\",\n \"[&_.tiptap_h2]:my-2 [&_.tiptap_h2]:text-lg [&_.tiptap_h2]:font-semibold\",\n \"[&_.tiptap_ul]:my-1.5 [&_.tiptap_ul]:list-disc [&_.tiptap_ul]:pl-6\",\n \"[&_.tiptap_ol]:my-1.5 [&_.tiptap_ol]:list-decimal [&_.tiptap_ol]:pl-6\",\n \"[&_.tiptap_li]:my-0.5\",\n )}\n style={{\n ...(minHeight && { [\"--editor-min-h\" as string]: minHeight }),\n ...(maxHeight && { [\"--editor-max-h\" as string]: maxHeight }),\n }}\n />\n </div>\n )\n }\n)\n\nexport { Editor }\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/editor.tsx"],"names":["UnderlineIcon","Editor","_a"],"mappings":"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","file":"chunk-7I2M55D7.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useEditor, EditorContent, type Editor as TiptapEditor } from \"@tiptap/react\"\nimport StarterKit from \"@tiptap/starter-kit\"\nimport Underline from \"@tiptap/extension-underline\"\nimport Placeholder from \"@tiptap/extension-placeholder\"\nimport {\n Bold,\n Italic,\n Underline as UnderlineIcon,\n Strikethrough,\n Heading2,\n List,\n ListOrdered,\n Undo2,\n Redo2,\n type LucideIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { toggleVariants } from \"@/components/ui/toggle\"\nimport {\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n TooltipProvider,\n} from \"@/components/ui/tooltip\"\nimport { CycleIcon } from \"@/components/icons\"\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface EditorRef {\n /** The Tiptap editor instance */\n editor: TiptapEditor | null\n /** Get current HTML content */\n getHTML: () => string\n /** Get current plain-text content */\n getText: () => string\n /** Clear the editor */\n clear: () => void\n /** Focus the editor */\n focus: () => void\n}\n\nexport interface EditorProps {\n /** HTML content (controlled) */\n content?: string\n /** Default HTML content (uncontrolled) */\n defaultContent?: string\n /** Called when content changes with the HTML string */\n onChange?: (html: string) => void\n /** Placeholder text shown when the editor is empty */\n placeholder?: string\n /** Whether the editor is editable */\n editable?: boolean\n /** Additional class for the outer wrapper */\n className?: string\n /** Auto-focus the editor on mount */\n autoFocus?: boolean\n /** Max height of the content area — scrolls when exceeded (e.g. \"300px\", \"50vh\") */\n maxHeight?: string\n /** Min height of the content area (default: \"120px\") */\n minHeight?: string\n /** Allow the user to resize the editor vertically */\n resizable?: boolean\n /** Theme class for the toolbar pressed state (e.g. \"theme-class\", \"theme-brand\") */\n toolbarTheme?: string\n}\n\n// ── Toolbar internals ────────────────────────────────────────────────\n\nfunction ToolbarDivider() {\n return <div className=\"mx-1 h-5 w-px bg-neutral-border\" aria-hidden=\"true\" />\n}\n\ninterface ToolbarToggleProps {\n pressed: boolean\n onToggle: () => void\n icon: LucideIcon\n label: string\n disabled?: boolean\n}\n\nfunction ToolbarToggle({ pressed, onToggle, icon, label, disabled }: ToolbarToggleProps) {\n return (\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n className={cn(\n toggleVariants({ variant: \"default\", size: \"icon-sm\" }),\n pressed && \"bg-accent text-accent-foreground\"\n )}\n onClick={onToggle}\n aria-pressed={pressed}\n aria-label={label}\n disabled={disabled}\n data-state={pressed ? \"on\" : \"off\"}\n >\n <CycleIcon icon={icon} size=\"xs\" decorative />\n </button>\n </TooltipTrigger>\n <TooltipContent>{label}</TooltipContent>\n </Tooltip>\n )\n}\n\ninterface ToolbarState {\n isBold: boolean\n isItalic: boolean\n isUnderline: boolean\n isStrike: boolean\n isH2: boolean\n isBulletList: boolean\n isOrderedList: boolean\n canUndo: boolean\n canRedo: boolean\n}\n\nfunction getToolbarState(editor: TiptapEditor): ToolbarState {\n return {\n isBold: editor.isActive(\"bold\"),\n isItalic: editor.isActive(\"italic\"),\n isUnderline: editor.isActive(\"underline\"),\n isStrike: editor.isActive(\"strike\"),\n isH2: editor.isActive(\"heading\", { level: 2 }),\n isBulletList: editor.isActive(\"bulletList\"),\n isOrderedList: editor.isActive(\"orderedList\"),\n canUndo: editor.can().undo(),\n canRedo: editor.can().redo(),\n }\n}\n\nfunction EditorToolbar({ editor, state, theme }: { editor: TiptapEditor; state: ToolbarState; theme?: string }) {\n const {\n isBold, isItalic, isUnderline, isStrike, isH2,\n isBulletList, isOrderedList, canUndo, canRedo,\n } = state\n\n return (\n <TooltipProvider>\n <div\n className={cn(\n \"flex items-center gap-0.5 overflow-x-auto border-b border-neutral-border px-2 py-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\",\n theme\n )}\n role=\"toolbar\"\n aria-label=\"Formatting options\"\n >\n {/* History */}\n <ToolbarToggle\n pressed={false}\n onToggle={() => editor.chain().focus().undo().run()}\n icon={Undo2}\n label=\"Undo\"\n disabled={!canUndo}\n />\n <ToolbarToggle\n pressed={false}\n onToggle={() => editor.chain().focus().redo().run()}\n icon={Redo2}\n label=\"Redo\"\n disabled={!canRedo}\n />\n\n <ToolbarDivider />\n\n {/* Text formatting */}\n <ToolbarToggle\n pressed={isBold}\n onToggle={() => editor.chain().focus().toggleBold().run()}\n icon={Bold}\n label=\"Bold\"\n />\n <ToolbarToggle\n pressed={isItalic}\n onToggle={() => editor.chain().focus().toggleItalic().run()}\n icon={Italic}\n label=\"Italic\"\n />\n <ToolbarToggle\n pressed={isUnderline}\n onToggle={() => editor.chain().focus().toggleUnderline().run()}\n icon={UnderlineIcon}\n label=\"Underline\"\n />\n <ToolbarToggle\n pressed={isStrike}\n onToggle={() => editor.chain().focus().toggleStrike().run()}\n icon={Strikethrough}\n label=\"Strikethrough\"\n />\n\n <ToolbarDivider />\n\n {/* Heading */}\n <ToolbarToggle\n pressed={isH2}\n onToggle={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n icon={Heading2}\n label=\"Heading 2\"\n />\n\n <ToolbarDivider />\n\n {/* Lists */}\n <ToolbarToggle\n pressed={isBulletList}\n onToggle={() => editor.chain().focus().toggleBulletList().run()}\n icon={List}\n label=\"Bullet list\"\n />\n <ToolbarToggle\n pressed={isOrderedList}\n onToggle={() => editor.chain().focus().toggleOrderedList().run()}\n icon={ListOrdered}\n label=\"Ordered list\"\n />\n </div>\n </TooltipProvider>\n )\n}\n\n// ── Editor ───────────────────────────────────────────────────────────\n\nconst Editor = React.forwardRef<EditorRef, EditorProps>(\n function Editor(\n {\n content,\n defaultContent,\n onChange,\n placeholder = \"Type something...\",\n editable = true,\n className,\n autoFocus = false,\n maxHeight,\n minHeight = \"120px\",\n resizable = false,\n toolbarTheme,\n },\n ref\n ) {\n const [toolbarState, setToolbarState] = React.useState<ToolbarState>({\n isBold: false, isItalic: false, isUnderline: false, isStrike: false,\n isH2: false, isBulletList: false, isOrderedList: false,\n canUndo: false, canRedo: false,\n })\n\n const onChangeRef = React.useRef(onChange)\n onChangeRef.current = onChange\n\n const editor = useEditor({\n immediatelyRender: false,\n extensions: [\n StarterKit.configure({\n heading: { levels: [2] },\n }),\n Underline,\n Placeholder.configure({ placeholder }),\n ],\n content: defaultContent ?? content ?? \"\",\n editable,\n autofocus: autoFocus,\n onUpdate: ({ editor: e }) => {\n onChangeRef.current?.(e.getHTML())\n },\n })\n\n // Subscribe to editor events directly — avoids stale closure issues\n // with useEditor callbacks on React 19\n React.useEffect(() => {\n if (!editor) return\n const sync = () => setToolbarState(getToolbarState(editor))\n sync()\n editor.on(\"transaction\", sync)\n return () => { editor.off(\"transaction\", sync) }\n }, [editor])\n\n // Sync controlled content\n React.useEffect(() => {\n if (content !== undefined && editor && editor.getHTML() !== content) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [content, editor])\n\n // Sync editable\n React.useEffect(() => {\n editor?.setEditable(editable)\n }, [editable, editor])\n\n // Expose ref\n React.useImperativeHandle(ref, () => ({\n editor,\n getHTML: () => editor?.getHTML() ?? \"\",\n getText: () => editor?.getText() ?? \"\",\n clear: () => { editor?.commands.clearContent() },\n focus: () => { editor?.commands.focus() },\n }), [editor])\n\n if (!editor) return null\n\n return (\n <div\n data-editor-wrapper\n className={cn(\n // Base — same tokens as Textarea (outline variant)\n \"w-full rounded-lg border border-neutral-border bg-transparent text-neutral-foreground shadow-xs dark:shadow-none\",\n \"transition-[color,border-color,box-shadow,background-color] outline-none\",\n // Hover\n \"hover:border-ring hover:bg-ring/5\",\n // Focus-within (focus is on nested contenteditable, not the wrapper)\n \"focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 focus-within:bg-ring/5\",\n // Error\n \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:hover:border-destructive dark:aria-invalid:border-destructive dark:aria-invalid:ring-destructive/40\",\n // Disabled\n !editable && \"opacity-50 cursor-not-allowed pointer-events-none\",\n className\n )}\n >\n {/* Scoped styles: placeholder + Tiptap scroll/sizing */}\n <style>{`\n [data-editor-wrapper] .tiptap {\n min-height: var(--editor-min-h, 120px);\n max-height: var(--editor-max-h, none);\n }\n [data-editor-wrapper] .tiptap p.is-editor-empty:first-child::before {\n color: var(--color-neutral-muted-foreground);\n content: attr(data-placeholder);\n float: left;\n height: 0;\n pointer-events: none;\n }\n `}</style>\n\n <EditorToolbar editor={editor} state={toolbarState} theme={toolbarTheme} />\n\n <EditorContent\n editor={editor}\n className={cn(\n \"text-sm text-foreground\",\n \"selection:bg-primary selection:text-primary-foreground\",\n resizable && \"resize-y\",\n // Scroll on .tiptap directly — Tiptap auto-scrolls to keep cursor in view\n \"[&_.tiptap]:outline-none [&_.tiptap]:overflow-y-auto [&_.tiptap]:px-4 [&_.tiptap]:py-2\",\n \"[&_.tiptap_p]:my-1.5 [&_.tiptap_p]:leading-relaxed\",\n \"[&_.tiptap_h2]:my-2 [&_.tiptap_h2]:text-lg [&_.tiptap_h2]:font-semibold\",\n \"[&_.tiptap_ul]:my-1.5 [&_.tiptap_ul]:list-disc [&_.tiptap_ul]:pl-6\",\n \"[&_.tiptap_ol]:my-1.5 [&_.tiptap_ol]:list-decimal [&_.tiptap_ol]:pl-6\",\n \"[&_.tiptap_li]:my-0.5\",\n )}\n style={{\n ...(minHeight && { [\"--editor-min-h\" as string]: minHeight }),\n ...(maxHeight && { [\"--editor-max-h\" as string]: maxHeight }),\n }}\n />\n </div>\n )\n }\n)\n\nexport { Editor }\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from './chunk-2KFWBA76.js';
|
|
2
|
-
import { ChatMessage } from './chunk-
|
|
2
|
+
import { ChatMessage } from './chunk-VUVC2NAK.js';
|
|
3
3
|
import { DEFAULT_MESSAGE_RATING_LABELS, MessageRating } from './chunk-YIOXPV2N.js';
|
|
4
4
|
import { MessageBar } from './chunk-TK67WF35.js';
|
|
5
5
|
import { cn } from './chunk-W4KJCBMW.js';
|
|
@@ -482,5 +482,5 @@ function ChatThread(_a) {
|
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
export { ChatThread };
|
|
485
|
-
//# sourceMappingURL=chunk-
|
|
486
|
-
//# sourceMappingURL=chunk-
|
|
485
|
+
//# sourceMappingURL=chunk-EAVNA2RW.js.map
|
|
486
|
+
//# sourceMappingURL=chunk-EAVNA2RW.js.map
|