create-newt-app 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { existsSync, promises, readdirSync } from "fs";
11
11
  import path from "path";
12
12
  import https from "node:https";
13
13
  //#region package.json
14
- var version = "0.37.0";
14
+ var version = "0.38.0";
15
15
  var engines = { "node": ">=24.0.0" };
16
16
  //#endregion
17
17
  //#region src/templates/root/index.ts
@@ -803,7 +803,7 @@ var manifest_default = {
803
803
  const versions = {
804
804
  "@babel/plugin-proposal-decorators": "^8.0.2",
805
805
  "@babel/plugin-transform-class-properties": "^8.0.1",
806
- "@base-ui/react": "^1.6.0",
806
+ "@base-ui/react": "^1.9.0",
807
807
  "@eslint/js": "^9.39.1",
808
808
  "@nestjs/cli": "^12.0.7",
809
809
  "@nestjs/common": "^12.1.0",
@@ -815,7 +815,7 @@ const versions = {
815
815
  "@next/eslint-plugin-next": "^16.4.0",
816
816
  "@oxlint/plugins": "^1.74.0",
817
817
  "@shadcn/lint": "^0.1.0",
818
- "@shadcn/react": "^0.2.1",
818
+ "@shadcn/react": "^0.3.1",
819
819
  "@stylexjs/babel-plugin": "0.19.0",
820
820
  "@stylexjs/postcss-plugin": "0.19.0",
821
821
  "@stylexjs/stylex": "0.19.0",
@@ -840,7 +840,7 @@ const versions = {
840
840
  "class-variance-authority": "^0.7.1",
841
841
  clsx: "^2.1.1",
842
842
  cmdk: "^1.1.1",
843
- cn: "^0.3.0",
843
+ cn: "^0.4.0",
844
844
  "date-fns": "^4.2.1",
845
845
  dotenv: "^17.3.1",
846
846
  "dotenv-cli": "^11.0.0",
@@ -870,6 +870,7 @@ const versions = {
870
870
  "reflect-metadata": "^0.2.2",
871
871
  rxjs: "^7.8.1",
872
872
  shadcn: "^4.7.0",
873
+ shiki: "^4.5.0",
873
874
  sonner: "^2.0.7",
874
875
  supertest: "^7.0.0",
875
876
  "tailwind-merge": "^3.6.0",
@@ -1673,6 +1674,7 @@ const shadcnUi = {
1673
1674
  "exports": {
1674
1675
  "./globals.css": "./src/styles/globals.css",
1675
1676
  "./postcss.config": "./postcss.config.mjs",
1677
+ "./lib/highlighter": "./src/lib/highlighter.tsx",
1676
1678
  "./lib/*": "./src/lib/*.ts",
1677
1679
  "./components/*": "./src/components/*.tsx",
1678
1680
  "./hooks/*": "./src/hooks/*.ts",
@@ -1696,6 +1698,7 @@ const shadcnUi = {
1696
1698
  "react-day-picker": "<%= versions["react-day-picker"] %>",
1697
1699
  "react-resizable-panels": "<%= versions["react-resizable-panels"] %>",
1698
1700
  "recharts": "<%= versions.recharts %>",
1701
+ "shiki": "<%= versions.shiki %>",
1699
1702
  "sonner": "<%= versions.sonner %>",
1700
1703
  "tw-animate-css": "<%= versions["tw-animate-css"] %>"
1701
1704
  },
@@ -9892,6 +9895,1198 @@ export {
9892
9895
  useMessageScrollerScrollable,
9893
9896
  useMessageScrollerVisibility,
9894
9897
  }
9898
+ `
9899
+ },
9900
+ {
9901
+ filename: "packages/ui/src/components/code-block.tsx",
9902
+ template: `"use client"
9903
+
9904
+ import * as React from "react"
9905
+ import { cva, type VariantProps } from "class-variance-authority"
9906
+ import { cn } from "cn"
9907
+
9908
+ import { Button } from "@<%= projectName %>/ui/components/button"
9909
+ import { CheckIcon, CopyIcon } from "lucide-react"
9910
+
9911
+ const codeBlockVariants = cva(
9912
+ "group/code-block relative flex w-full min-w-0 flex-col overflow-hidden rounded-lg text-sm has-[>[data-slot=code-block-content]:focus-visible]:ring-3 has-[>[data-slot=code-block-content]:focus-visible]:ring-ring/50 data-[size=sm]:text-xs",
9913
+ {
9914
+ variants: {
9915
+ variant: {
9916
+ default:
9917
+ "border border-input text-foreground has-[>[data-slot=code-block-content]:focus-visible]:border-ring dark:bg-input/30",
9918
+ destructive:
9919
+ "border border-destructive/30 text-destructive dark:border-destructive/40",
9920
+ },
9921
+ },
9922
+ defaultVariants: {
9923
+ variant: "default",
9924
+ },
9925
+ }
9926
+ )
9927
+
9928
+ function CodeBlock({
9929
+ className,
9930
+ variant = "default",
9931
+ size = "default",
9932
+ ...props
9933
+ }: React.ComponentProps<"div"> &
9934
+ VariantProps<typeof codeBlockVariants> & { size?: "default" | "sm" }) {
9935
+ return (
9936
+ <div
9937
+ data-slot="code-block"
9938
+ data-variant={variant}
9939
+ data-size={size}
9940
+ className={cn(codeBlockVariants({ variant }), className)}
9941
+ {...props}
9942
+ />
9943
+ )
9944
+ }
9945
+
9946
+ function CodeBlockHeader({ className, ...props }: React.ComponentProps<"div">) {
9947
+ return (
9948
+ <div
9949
+ data-slot="code-block-header"
9950
+ className={cn(
9951
+ "flex min-w-0 shrink-0 items-center gap-2 px-3 pt-3 text-muted-foreground group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:pt-2.5 group-data-[variant=destructive]/code-block:text-destructive *:has-[>[data-slot=code-block-title]]:min-w-0",
9952
+ className
9953
+ )}
9954
+ {...props}
9955
+ />
9956
+ )
9957
+ }
9958
+
9959
+ function CodeBlockTitle({ className, ...props }: React.ComponentProps<"div">) {
9960
+ return (
9961
+ <div
9962
+ data-slot="code-block-title"
9963
+ className={cn(
9964
+ "min-w-0 truncate font-sans text-sm font-medium text-foreground group-data-[size=sm]/code-block:text-xs group-data-[variant=destructive]/code-block:text-destructive [&_svg]:pointer-events-none [&_svg]:inline-block [&_svg]:align-middle [&_svg:not([class*='size-'])]:size-3.5 *:[svg]:me-1.5",
9965
+ className
9966
+ )}
9967
+ {...props}
9968
+ />
9969
+ )
9970
+ }
9971
+
9972
+ function CodeBlockActions({
9973
+ className,
9974
+ ...props
9975
+ }: React.ComponentProps<"div">) {
9976
+ return (
9977
+ <div
9978
+ data-slot="code-block-actions"
9979
+ className={cn(
9980
+ "-my-1 ms-auto -me-1.5 flex shrink-0 items-center gap-1 [[data-slot=code-block]>&]:absolute [[data-slot=code-block]>&]:end-2 [[data-slot=code-block]>&]:top-2 [[data-slot=code-block]>&]:z-10 [[data-slot=code-block]>&]:m-0 [[data-slot=code-block]>&]:opacity-0 [[data-slot=code-block]>&]:transition-opacity [[data-slot=code-block]>&]:group-focus-within/code-block:opacity-100 [[data-slot=code-block]>&]:group-hover/code-block:opacity-100 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:end-1.5 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:top-1.5 [[data-slot=code-block]>&]:pointer-coarse:opacity-100 [[data-slot=code-block]>&]:*:[button]:bg-background [[data-slot=code-block]>&]:*:[button]:ring-1 [[data-slot=code-block]>&]:*:[button]:ring-foreground/10",
9981
+ className
9982
+ )}
9983
+ {...props}
9984
+ />
9985
+ )
9986
+ }
9987
+
9988
+ function CodeBlockCopyButton({
9989
+ value,
9990
+ variant = "ghost",
9991
+ size = "icon-xs",
9992
+ className,
9993
+ onClick,
9994
+ ...props
9995
+ }: React.ComponentProps<typeof Button> & { value: string }) {
9996
+ const [copied, setCopied] = React.useState(false)
9997
+
9998
+ React.useEffect(() => {
9999
+ if (!copied) {
10000
+ return
10001
+ }
10002
+
10003
+ const timeout = setTimeout(() => setCopied(false), 2000)
10004
+ return () => clearTimeout(timeout)
10005
+ }, [copied])
10006
+
10007
+ return (
10008
+ <>
10009
+ <Button
10010
+ data-slot="code-block-copy-button"
10011
+ data-copied={copied || undefined}
10012
+ variant={variant}
10013
+ size={size}
10014
+ aria-label={copied ? "Copied" : "Copy"}
10015
+ className={cn(className)}
10016
+ onClick={(event) => {
10017
+ onClick?.(event)
10018
+ navigator.clipboard
10019
+ .writeText(value)
10020
+ .then(() => setCopied(true))
10021
+ .catch(() => {})
10022
+ }}
10023
+ {...props}
10024
+ >
10025
+ {copied ? (
10026
+ <CheckIcon
10027
+ />
10028
+ ) : (
10029
+ <CopyIcon
10030
+ />
10031
+ )}
10032
+ </Button>
10033
+ <span aria-live="polite" className="sr-only">
10034
+ {copied ? "Copied" : ""}
10035
+ </span>
10036
+ </>
10037
+ )
10038
+ }
10039
+
10040
+ function CodeBlockContent({
10041
+ className,
10042
+ children,
10043
+ style,
10044
+ wrap = false,
10045
+ lineNumbers = false,
10046
+ startLine = 1,
10047
+ ...props
10048
+ }: React.ComponentProps<"pre"> & {
10049
+ wrap?: boolean
10050
+ lineNumbers?: boolean
10051
+ startLine?: number
10052
+ }) {
10053
+ return (
10054
+ <pre
10055
+ data-slot="code-block-content"
10056
+ data-wrap={wrap || undefined}
10057
+ data-numbered={lineNumbers || undefined}
10058
+ dir="ltr"
10059
+ tabIndex={0}
10060
+ style={{ counterReset: \`line \${startLine - 1}\`, ...style }}
10061
+ className={cn(
10062
+ "min-h-0 min-w-0 scroll-fade-x scrollbar-thin overflow-auto px-3 py-3 text-start font-mono leading-relaxed outline-none group-has-data-[slot=code-block-footer]/code-block:pb-2 group-has-data-[slot=code-block-header]/code-block:pt-2 group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:py-2.5 group-data-[size=sm]/code-block:group-has-data-[slot=code-block-header]/code-block:pt-1.5 data-wrap:scroll-fade-y data-wrap:wrap-break-word data-wrap:whitespace-pre-wrap",
10063
+ className
10064
+ )}
10065
+ {...props}
10066
+ >
10067
+ <code
10068
+ data-slot="code-block-code"
10069
+ className="grid min-h-lh min-w-max content-start in-data-numbered:grid-cols-[auto_1fr] in-data-wrap:min-w-0"
10070
+ >
10071
+ {lineNumbers && typeof children === "string"
10072
+ ? children
10073
+ .replace(/\\n$/, "")
10074
+ .split("\\n")
10075
+ .map((line, index) => (
10076
+ <CodeBlockLine key={index}>{line}</CodeBlockLine>
10077
+ ))
10078
+ : children}
10079
+ </code>
10080
+ </pre>
10081
+ )
10082
+ }
10083
+
10084
+ const codeBlockLineVariants = cva(
10085
+ "relative -mx-3 block min-h-lh px-3 group-data-[size=sm]/code-block:-mx-2.5 group-data-[size=sm]/code-block:px-2.5 before:min-w-6 before:text-end before:text-muted-foreground after:absolute after:start-1 after:top-0 after:text-muted-foreground after:select-none in-data-numbered:col-span-2 in-data-numbered:grid in-data-numbered:grid-cols-subgrid in-data-numbered:gap-x-3 in-data-numbered:[counter-increment:line] in-data-numbered:before:content-[counter(line)] in-data-numbered:before:select-none in-data-numbered:supports-[content:counter(line)_/_'']:before:content-[counter(line)_/_'']",
10086
+ {
10087
+ variants: {
10088
+ variant: {
10089
+ default: "",
10090
+ added: "bg-muted after:content-['+']",
10091
+ removed: "bg-destructive/10 after:text-destructive after:content-['−']",
10092
+ highlighted: "bg-muted",
10093
+ },
10094
+ },
10095
+ defaultVariants: {
10096
+ variant: "default",
10097
+ },
10098
+ }
10099
+ )
10100
+
10101
+ function CodeBlockLine({
10102
+ className,
10103
+ variant = "default",
10104
+ children,
10105
+ ...props
10106
+ }: React.ComponentProps<"span"> & VariantProps<typeof codeBlockLineVariants>) {
10107
+ // An empty line holds a newline so copying a selection keeps blank lines.
10108
+ const empty =
10109
+ children === undefined ||
10110
+ children === null ||
10111
+ children === "" ||
10112
+ (Array.isArray(children) && children.length === 0)
10113
+
10114
+ return (
10115
+ <span
10116
+ data-slot="code-block-line"
10117
+ data-variant={variant}
10118
+ className={cn(codeBlockLineVariants({ variant }), className)}
10119
+ {...props}
10120
+ >
10121
+ <span data-slot="code-block-line-content" className="min-w-0">
10122
+ {empty ? "\\n" : children}
10123
+ </span>
10124
+ </span>
10125
+ )
10126
+ }
10127
+
10128
+ function CodeBlockFooter({ className, ...props }: React.ComponentProps<"div">) {
10129
+ return (
10130
+ <div
10131
+ data-slot="code-block-footer"
10132
+ className={cn(
10133
+ "flex min-w-0 shrink-0 items-center gap-2 px-3 pb-3 text-muted-foreground group-data-[size=sm]/code-block:px-2.5 group-data-[size=sm]/code-block:pb-2.5",
10134
+ className
10135
+ )}
10136
+ {...props}
10137
+ />
10138
+ )
10139
+ }
10140
+
10141
+ export {
10142
+ CodeBlock,
10143
+ CodeBlockHeader,
10144
+ CodeBlockTitle,
10145
+ CodeBlockActions,
10146
+ CodeBlockCopyButton,
10147
+ CodeBlockContent,
10148
+ CodeBlockLine,
10149
+ CodeBlockFooter,
10150
+ }
10151
+ `
10152
+ },
10153
+ {
10154
+ filename: "packages/ui/src/components/questionnaire.tsx",
10155
+ template: `"use client"
10156
+
10157
+ import * as React from "react"
10158
+ import { Questionnaire as QuestionnairePrimitive } from "@shadcn/react/questionnaire"
10159
+ import { cn } from "cn"
10160
+
10161
+ import { buttonVariants, type Button } from "@<%= projectName %>/ui/components/button"
10162
+ import { CheckIcon } from "lucide-react"
10163
+
10164
+ function Questionnaire({
10165
+ className,
10166
+ ...props
10167
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Root>) {
10168
+ return (
10169
+ <QuestionnairePrimitive.Root
10170
+ data-slot="questionnaire"
10171
+ className={cn("flex w-full min-w-0 flex-col gap-4", className)}
10172
+ {...props}
10173
+ />
10174
+ )
10175
+ }
10176
+
10177
+ function QuestionnaireProgress({
10178
+ className,
10179
+ ...props
10180
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Progress>) {
10181
+ return (
10182
+ <QuestionnairePrimitive.Progress
10183
+ data-slot="questionnaire-progress"
10184
+ className={cn(
10185
+ "min-h-[1lh] w-fit min-w-[14ch] text-xs font-medium text-muted-foreground tabular-nums",
10186
+ className
10187
+ )}
10188
+ {...props}
10189
+ />
10190
+ )
10191
+ }
10192
+
10193
+ function QuestionnaireItem({
10194
+ className,
10195
+ ...props
10196
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Item>) {
10197
+ return (
10198
+ <QuestionnairePrimitive.Item
10199
+ data-slot="questionnaire-item"
10200
+ className={cn(
10201
+ "flex min-w-0 flex-col gap-4 border-0 p-0 outline-none",
10202
+ className
10203
+ )}
10204
+ {...props}
10205
+ />
10206
+ )
10207
+ }
10208
+
10209
+ function QuestionnaireTitle({
10210
+ className,
10211
+ ...props
10212
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Title>) {
10213
+ return (
10214
+ <QuestionnairePrimitive.Title
10215
+ data-slot="questionnaire-title"
10216
+ className={cn(
10217
+ "text-base leading-snug font-medium text-pretty [&:not(:has(~[data-slot=questionnaire-description]))]:mb-4",
10218
+ className
10219
+ )}
10220
+ {...props}
10221
+ />
10222
+ )
10223
+ }
10224
+
10225
+ function QuestionnaireDescription({
10226
+ className,
10227
+ ...props
10228
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Description>) {
10229
+ return (
10230
+ <QuestionnairePrimitive.Description
10231
+ data-slot="questionnaire-description"
10232
+ className={cn("text-sm text-pretty text-muted-foreground", className)}
10233
+ {...props}
10234
+ />
10235
+ )
10236
+ }
10237
+
10238
+ function QuestionnaireChoices({
10239
+ className,
10240
+ ...props
10241
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Choices>) {
10242
+ return (
10243
+ <QuestionnairePrimitive.Choices
10244
+ data-slot="questionnaire-choices"
10245
+ className={cn(
10246
+ "group/questionnaire-choices grid min-w-0 gap-2",
10247
+ className
10248
+ )}
10249
+ {...props}
10250
+ />
10251
+ )
10252
+ }
10253
+
10254
+ function QuestionnaireChoice({
10255
+ children,
10256
+ className,
10257
+ ...props
10258
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Choice>) {
10259
+ return (
10260
+ <QuestionnairePrimitive.Choice
10261
+ data-slot="questionnaire-choice"
10262
+ className={cn(
10263
+ "group/questionnaire-choice relative flex min-h-11 cursor-pointer items-start gap-2.5 rounded-lg border border-input bg-transparent px-3 py-2.5 text-start text-sm transition-colors outline-none select-none hover:bg-muted/50 has-[>input:focus-visible]:border-ring has-[>input:focus-visible]:ring-3 has-[>input:focus-visible]:ring-ring/50 data-invalid:border-destructive dark:bg-input/20 data-checked:border-primary/40 data-checked:bg-muted dark:data-checked:bg-muted",
10264
+ "data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50",
10265
+ className
10266
+ )}
10267
+ {...props}
10268
+ >
10269
+ <QuestionnairePrimitive.ChoiceInput
10270
+ data-slot="questionnaire-choice-input"
10271
+ className="absolute inset-0 z-10 size-full cursor-pointer opacity-0"
10272
+ />
10273
+ <span
10274
+ aria-hidden="true"
10275
+ data-slot="questionnaire-choice-indicator"
10276
+ className="pointer-events-none relative flex size-4 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-[4px] border border-input group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[type=radio]/questionnaire-choice:rounded-full group-data-checked/questionnaire-choice:border-primary group-data-checked/questionnaire-choice:bg-primary group-data-checked/questionnaire-choice:text-primary-foreground dark:bg-input/30 dark:group-data-checked/questionnaire-choice:bg-primary"
10277
+ >
10278
+ <span
10279
+ data-slot="questionnaire-choice-indicator-dot"
10280
+ className="hidden size-2 rounded-full bg-primary-foreground group-data-[type=checkbox]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block"
10281
+ />
10282
+ <CheckIcon data-slot="questionnaire-choice-indicator-check" className="hidden size-3.5 group-data-[type=radio]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block" />
10283
+ </span>
10284
+ <QuestionnairePrimitive.ChoiceLabel
10285
+ data-slot="questionnaire-choice-label"
10286
+ className="flex min-w-0 flex-1 flex-col gap-0.5 leading-snug"
10287
+ >
10288
+ {children}
10289
+ </QuestionnairePrimitive.ChoiceLabel>
10290
+ <QuestionnairePrimitive.ChoiceShortcut
10291
+ data-slot="questionnaire-choice-shortcut"
10292
+ className="pointer-events-none ms-auto hidden size-5 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-md border border-input bg-background font-mono text-[0.625rem] leading-none font-medium text-muted-foreground group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[shortcut]/questionnaire-choice:inline-flex"
10293
+ />
10294
+ </QuestionnairePrimitive.Choice>
10295
+ )
10296
+ }
10297
+
10298
+ function QuestionnaireChoiceDescription({
10299
+ className,
10300
+ ...props
10301
+ }: React.ComponentProps<"span">) {
10302
+ return (
10303
+ <span
10304
+ data-slot="questionnaire-choice-description"
10305
+ className={cn("text-muted-foreground", className)}
10306
+ {...props}
10307
+ />
10308
+ )
10309
+ }
10310
+
10311
+ function QuestionnaireInput({
10312
+ className,
10313
+ ...props
10314
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Input>) {
10315
+ return (
10316
+ <div
10317
+ data-slot="questionnaire-input-wrapper"
10318
+ className="group/questionnaire-input relative w-full min-w-0"
10319
+ >
10320
+ <QuestionnairePrimitive.Input
10321
+ data-slot="questionnaire-input"
10322
+ className={cn(
10323
+ "h-8 min-h-11 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-[color,box-shadow,background-color] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 sm:min-h-0 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
10324
+ "selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground",
10325
+ className
10326
+ )}
10327
+ {...props}
10328
+ />
10329
+ </div>
10330
+ )
10331
+ }
10332
+
10333
+ function QuestionnaireError({
10334
+ className,
10335
+ ...props
10336
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Error>) {
10337
+ return (
10338
+ <QuestionnairePrimitive.Error
10339
+ data-slot="questionnaire-error"
10340
+ className={cn("mt-2 text-sm text-destructive", className)}
10341
+ {...props}
10342
+ />
10343
+ )
10344
+ }
10345
+
10346
+ function QuestionnaireActions({
10347
+ className,
10348
+ ...props
10349
+ }: React.ComponentProps<"div">) {
10350
+ return (
10351
+ <div
10352
+ data-slot="questionnaire-actions"
10353
+ className={cn(
10354
+ "grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto_auto] items-center gap-2 sm:min-h-8",
10355
+ className
10356
+ )}
10357
+ {...props}
10358
+ />
10359
+ )
10360
+ }
10361
+
10362
+ function QuestionnairePrevious({
10363
+ children,
10364
+ className,
10365
+ size = "default",
10366
+ variant = "outline",
10367
+ ...props
10368
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Previous> &
10369
+ Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
10370
+ return (
10371
+ <QuestionnairePrimitive.Previous
10372
+ data-slot="questionnaire-previous"
10373
+ data-size={size}
10374
+ data-variant={variant}
10375
+ className={cn(
10376
+ buttonVariants({ size, variant }),
10377
+ "col-start-1 row-start-1 min-h-11 justify-self-start sm:min-h-0",
10378
+ className
10379
+ )}
10380
+ {...props}
10381
+ >
10382
+ {children ?? "Previous"}
10383
+ </QuestionnairePrimitive.Previous>
10384
+ )
10385
+ }
10386
+
10387
+ function QuestionnaireSkip({
10388
+ children,
10389
+ className,
10390
+ size = "default",
10391
+ variant = "outline",
10392
+ ...props
10393
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Skip> &
10394
+ Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
10395
+ return (
10396
+ <QuestionnairePrimitive.Skip
10397
+ data-slot="questionnaire-skip"
10398
+ data-size={size}
10399
+ data-variant={variant}
10400
+ className={cn(
10401
+ buttonVariants({ size, variant }),
10402
+ "col-start-2 row-start-1 min-h-11 justify-self-end sm:min-h-0",
10403
+ className
10404
+ )}
10405
+ {...props}
10406
+ >
10407
+ {children ?? "Skip"}
10408
+ </QuestionnairePrimitive.Skip>
10409
+ )
10410
+ }
10411
+
10412
+ function QuestionnaireNext({
10413
+ children,
10414
+ className,
10415
+ size = "default",
10416
+ variant = "default",
10417
+ ...props
10418
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Next> &
10419
+ Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
10420
+ return (
10421
+ <QuestionnairePrimitive.Next
10422
+ data-slot="questionnaire-next"
10423
+ data-size={size}
10424
+ data-variant={variant}
10425
+ className={cn(
10426
+ buttonVariants({ size, variant }),
10427
+ "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",
10428
+ className
10429
+ )}
10430
+ {...props}
10431
+ >
10432
+ {children ?? "Next"}
10433
+ </QuestionnairePrimitive.Next>
10434
+ )
10435
+ }
10436
+
10437
+ function QuestionnaireSubmit({
10438
+ children,
10439
+ className,
10440
+ size = "default",
10441
+ variant = "default",
10442
+ ...props
10443
+ }: React.ComponentProps<typeof QuestionnairePrimitive.Submit> &
10444
+ Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
10445
+ return (
10446
+ <QuestionnairePrimitive.Submit
10447
+ data-slot="questionnaire-submit"
10448
+ data-size={size}
10449
+ data-variant={variant}
10450
+ className={cn(
10451
+ buttonVariants({ size, variant }),
10452
+ "col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0",
10453
+ className
10454
+ )}
10455
+ {...props}
10456
+ >
10457
+ {children ?? "Submit"}
10458
+ </QuestionnairePrimitive.Submit>
10459
+ )
10460
+ }
10461
+
10462
+ export {
10463
+ Questionnaire,
10464
+ QuestionnaireActions,
10465
+ QuestionnaireChoice,
10466
+ QuestionnaireChoiceDescription,
10467
+ QuestionnaireChoices,
10468
+ QuestionnaireDescription,
10469
+ QuestionnaireError,
10470
+ QuestionnaireInput,
10471
+ QuestionnaireItem,
10472
+ QuestionnaireNext,
10473
+ QuestionnairePrevious,
10474
+ QuestionnaireProgress,
10475
+ QuestionnaireSkip,
10476
+ QuestionnaireSubmit,
10477
+ QuestionnaireTitle,
10478
+ }
10479
+ `
10480
+ },
10481
+ {
10482
+ filename: "packages/ui/src/components/trace.tsx",
10483
+ template: `import * as React from "react"
10484
+ import { cn } from "cn"
10485
+
10486
+ import {
10487
+ Collapsible,
10488
+ CollapsibleContent,
10489
+ CollapsibleTrigger,
10490
+ } from "@<%= projectName %>/ui/components/collapsible"
10491
+ import { ChevronRightIcon } from "lucide-react"
10492
+
10493
+ function Trace({ className, ...props }: React.ComponentProps<"div">) {
10494
+ return (
10495
+ <div
10496
+ data-slot="trace"
10497
+ className={cn(
10498
+ "flex w-full min-w-0 flex-col gap-2 *:has-[>[data-slot=trace-line]]:flex *:has-[>[data-slot=trace-line]]:w-fit *:has-[>[data-slot=trace-line]]:max-w-full *:has-[>[data-slot=trace-line]]:min-w-0 *:has-[>[data-slot=trace-line]]:rounded-md *:has-[>[data-slot=trace-line]]:text-left *:has-[>[data-slot=trace-line]]:outline-none *:has-[>[data-slot=trace-line]]:focus-visible:ring-3 *:has-[>[data-slot=trace-line]]:focus-visible:ring-ring/50",
10499
+ className
10500
+ )}
10501
+ {...props}
10502
+ />
10503
+ )
10504
+ }
10505
+
10506
+ function TraceLine({
10507
+ className,
10508
+ status,
10509
+ ...props
10510
+ }: React.ComponentProps<"span"> & {
10511
+ status?: "running" | "error" | "pending"
10512
+ }) {
10513
+ return (
10514
+ <span
10515
+ data-slot="trace-line"
10516
+ data-status={status}
10517
+ className={cn(
10518
+ "flex w-fit max-w-full min-w-0 items-center gap-2 text-sm text-muted-foreground transition-colors in-[a,button]:cursor-pointer in-[a:hover,button:hover]:text-foreground data-[status=error]:text-destructive data-[status=error]:in-[a:hover,button:hover]:text-destructive data-[status=pending]:opacity-60 data-[status=running]:*:data-[slot=trace-title]:shimmer",
10519
+ className
10520
+ )}
10521
+ {...props}
10522
+ />
10523
+ )
10524
+ }
10525
+
10526
+ function TraceIcon({ className, ...props }: React.ComponentProps<"span">) {
10527
+ return (
10528
+ <span
10529
+ data-slot="trace-icon"
10530
+ aria-hidden="true"
10531
+ className={cn(
10532
+ "flex size-4 shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [[data-slot=trace-title]~&]:-ml-1",
10533
+ className
10534
+ )}
10535
+ {...props}
10536
+ />
10537
+ )
10538
+ }
10539
+
10540
+ function TraceTitle({ className, ...props }: React.ComponentProps<"span">) {
10541
+ return (
10542
+ <span
10543
+ data-slot="trace-title"
10544
+ className={cn("min-w-0 truncate motion-reduce:shimmer-none", className)}
10545
+ {...props}
10546
+ />
10547
+ )
10548
+ }
10549
+
10550
+ function TraceDescription({
10551
+ className,
10552
+ ...props
10553
+ }: React.ComponentProps<"span">) {
10554
+ return (
10555
+ <span
10556
+ data-slot="trace-description"
10557
+ className={cn(
10558
+ "shrink-0 text-muted-foreground/70 tabular-nums",
10559
+ className
10560
+ )}
10561
+ {...props}
10562
+ />
10563
+ )
10564
+ }
10565
+
10566
+ function TraceCollapsible({
10567
+ className,
10568
+ ...props
10569
+ }: Omit<React.ComponentProps<typeof Collapsible>, "open"> &
10570
+ (
10571
+ | { open?: undefined }
10572
+ | {
10573
+ open: boolean
10574
+ onOpenChange: NonNullable<
10575
+ React.ComponentProps<typeof Collapsible>["onOpenChange"]
10576
+ >
10577
+ }
10578
+ )) {
10579
+ return (
10580
+ <Collapsible
10581
+ data-slot="trace-collapsible"
10582
+ className={cn("flex w-full min-w-0 flex-col", className)}
10583
+ {...props}
10584
+ />
10585
+ )
10586
+ }
10587
+
10588
+ function TraceCollapsibleTrigger({
10589
+ className,
10590
+ children,
10591
+ ...props
10592
+ }: React.ComponentProps<typeof CollapsibleTrigger>) {
10593
+ return (
10594
+ <CollapsibleTrigger
10595
+ data-slot="trace-collapsible-trigger"
10596
+ className={cn(
10597
+ "group/trace-collapsible-trigger flex w-fit max-w-full min-w-0 cursor-pointer items-center gap-1 rounded-md text-left text-muted-foreground transition-colors outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50 has-[[data-status=error]]:text-destructive",
10598
+ className
10599
+ )}
10600
+ {...props}
10601
+ >
10602
+ {children}
10603
+ <TraceIcon className="transition-transform group-aria-expanded/trace-collapsible-trigger:rotate-90">
10604
+ <ChevronRightIcon
10605
+ />
10606
+ </TraceIcon>
10607
+ </CollapsibleTrigger>
10608
+ )
10609
+ }
10610
+
10611
+ function TraceCollapsibleContent({
10612
+ className,
10613
+ children,
10614
+ ...props
10615
+ }: React.ComponentProps<typeof CollapsibleContent>) {
10616
+ return (
10617
+ <CollapsibleContent
10618
+ data-slot="trace-collapsible-content"
10619
+ className="h-(--collapsible-panel-height) min-w-0 overflow-hidden transition-[height] duration-200 data-ending-style:h-0 data-starting-style:h-0"
10620
+ {...props}
10621
+ >
10622
+ <div
10623
+ data-slot="trace-collapsible-content-inner"
10624
+ className={cn(
10625
+ "mt-2 mb-1 ml-[calc(--spacing(2)-1px)] min-w-0 border-l pr-1 pl-4 leading-relaxed wrap-break-word text-muted-foreground",
10626
+ className
10627
+ )}
10628
+ >
10629
+ {children}
10630
+ </div>
10631
+ </CollapsibleContent>
10632
+ )
10633
+ }
10634
+
10635
+ export {
10636
+ Trace,
10637
+ TraceLine,
10638
+ TraceIcon,
10639
+ TraceTitle,
10640
+ TraceDescription,
10641
+ TraceCollapsible,
10642
+ TraceCollapsibleTrigger,
10643
+ TraceCollapsibleContent,
10644
+ }
10645
+ `
10646
+ },
10647
+ {
10648
+ filename: "packages/ui/src/components/toast.tsx",
10649
+ template: `"use client"
10650
+
10651
+ import * as React from "react"
10652
+ import { Toast as ToastPrimitive } from "@base-ui/react/toast"
10653
+ import { cn } from "cn"
10654
+
10655
+ import { Button } from "@<%= projectName %>/ui/components/button"
10656
+ import { XIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
10657
+
10658
+ const toast = ToastPrimitive.createToastManager()
10659
+
10660
+ function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
10661
+ return <ToastPrimitive.Provider {...props} />
10662
+ }
10663
+
10664
+ function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
10665
+ return <ToastPrimitive.Portal data-slot="toast-portal" {...props} />
10666
+ }
10667
+
10668
+ function ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {
10669
+ return (
10670
+ <ToastPrimitive.Viewport
10671
+ data-slot="toast-viewport"
10672
+ className={cn(
10673
+ "pointer-events-none fixed inset-x-4 bottom-4 z-50 mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full",
10674
+ className
10675
+ )}
10676
+ {...props}
10677
+ />
10678
+ )
10679
+ }
10680
+
10681
+ function Toast({ className, ...props }: ToastPrimitive.Root.Props) {
10682
+ return (
10683
+ <ToastPrimitive.Root
10684
+ data-slot="toast"
10685
+ className={cn(
10686
+ "group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-2xl border bg-popover text-popover-foreground shadow-lg will-change-transform outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
10687
+ "[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]",
10688
+ "h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms]",
10689
+ "after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']",
10690
+ "data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]",
10691
+ "data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]",
10692
+ "[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]",
10693
+ "data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
10694
+ "data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
10695
+ "data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
10696
+ "data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
10697
+ "data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
10698
+ "data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
10699
+ "data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
10700
+ "data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
10701
+ className
10702
+ )}
10703
+ {...props}
10704
+ />
10705
+ )
10706
+ }
10707
+
10708
+ function ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {
10709
+ return (
10710
+ <ToastPrimitive.Content
10711
+ data-slot="toast-content"
10712
+ className={cn(
10713
+ "flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-250 ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100",
10714
+ className
10715
+ )}
10716
+ {...props}
10717
+ />
10718
+ )
10719
+ }
10720
+
10721
+ function ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {
10722
+ return (
10723
+ <ToastPrimitive.Title
10724
+ data-slot="toast-title"
10725
+ className={cn("text-sm font-medium", className)}
10726
+ {...props}
10727
+ />
10728
+ )
10729
+ }
10730
+
10731
+ function ToastDescription({
10732
+ className,
10733
+ ...props
10734
+ }: ToastPrimitive.Description.Props) {
10735
+ return (
10736
+ <ToastPrimitive.Description
10737
+ data-slot="toast-description"
10738
+ className={cn("text-sm text-muted-foreground", className)}
10739
+ {...props}
10740
+ />
10741
+ )
10742
+ }
10743
+
10744
+ function ToastAction({
10745
+ className,
10746
+ render = <Button variant="outline" size="sm" />,
10747
+ ...props
10748
+ }: ToastPrimitive.Action.Props) {
10749
+ return (
10750
+ <ToastPrimitive.Action
10751
+ data-slot="toast-action"
10752
+ render={render}
10753
+ className={cn("shrink-0", className)}
10754
+ {...props}
10755
+ />
10756
+ )
10757
+ }
10758
+
10759
+ function ToastClose({
10760
+ className,
10761
+ children,
10762
+ render = <Button variant="ghost" size="icon-sm" />,
10763
+ ...props
10764
+ }: ToastPrimitive.Close.Props) {
10765
+ return (
10766
+ <ToastPrimitive.Close
10767
+ data-slot="toast-close"
10768
+ aria-label="Close toast"
10769
+ render={render}
10770
+ className={cn(
10771
+ "relative shrink-0 text-muted-foreground after:absolute after:-inset-2 after:content-[''] hover:text-foreground",
10772
+ className
10773
+ )}
10774
+ {...props}
10775
+ >
10776
+ {children ?? (
10777
+ <XIcon aria-hidden="true" />
10778
+ )}
10779
+ </ToastPrimitive.Close>
10780
+ )
10781
+ }
10782
+
10783
+ function ToastIcon({ type }: { type: string | undefined }) {
10784
+ let icon: React.ReactNode = null
10785
+
10786
+ if (type === "success") {
10787
+ icon = (
10788
+ <CircleCheckIcon aria-hidden="true" />
10789
+ )
10790
+ }
10791
+
10792
+ if (type === "info") {
10793
+ icon = (
10794
+ <InfoIcon aria-hidden="true" />
10795
+ )
10796
+ }
10797
+
10798
+ if (type === "warning") {
10799
+ icon = (
10800
+ <TriangleAlertIcon aria-hidden="true" />
10801
+ )
10802
+ }
10803
+
10804
+ if (type === "error") {
10805
+ icon = (
10806
+ <OctagonXIcon className="text-destructive" aria-hidden="true" />
10807
+ )
10808
+ }
10809
+
10810
+ if (type === "loading") {
10811
+ icon = (
10812
+ <Loader2Icon className="animate-spin" aria-hidden="true" />
10813
+ )
10814
+ }
10815
+
10816
+ if (!icon) {
10817
+ return null
10818
+ }
10819
+
10820
+ return (
10821
+ <span
10822
+ data-slot="toast-icon"
10823
+ className="shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"
10824
+ >
10825
+ {icon}
10826
+ </span>
10827
+ )
10828
+ }
10829
+
10830
+ function ToastList() {
10831
+ const { toasts } = ToastPrimitive.useToastManager()
10832
+
10833
+ return toasts.map((toastItem) => (
10834
+ <Toast key={toastItem.id} toast={toastItem}>
10835
+ <ToastContent>
10836
+ <ToastIcon type={toastItem.type} />
10837
+ <div className="flex min-w-0 flex-1 flex-col gap-1">
10838
+ <ToastTitle />
10839
+ <ToastDescription />
10840
+ </div>
10841
+ <ToastAction />
10842
+ <ToastClose />
10843
+ </ToastContent>
10844
+ </Toast>
10845
+ ))
10846
+ }
10847
+
10848
+ function Toaster({
10849
+ children,
10850
+ toastManager = toast,
10851
+ ...props
10852
+ }: ToastPrimitive.Provider.Props) {
10853
+ return (
10854
+ <ToastProvider toastManager={toastManager} {...props}>
10855
+ {children}
10856
+ <ToastPortal>
10857
+ <ToastViewport>
10858
+ <ToastList />
10859
+ </ToastViewport>
10860
+ </ToastPortal>
10861
+ </ToastProvider>
10862
+ )
10863
+ }
10864
+
10865
+ const createToastManager = ToastPrimitive.createToastManager
10866
+ const useToastManager = ToastPrimitive.useToastManager
10867
+
10868
+ export {
10869
+ Toaster,
10870
+ Toast,
10871
+ ToastAction,
10872
+ ToastClose,
10873
+ ToastContent,
10874
+ ToastDescription,
10875
+ ToastPortal,
10876
+ ToastProvider,
10877
+ ToastTitle,
10878
+ ToastViewport,
10879
+ createToastManager,
10880
+ toast,
10881
+ useToastManager,
10882
+ }
10883
+ `
10884
+ },
10885
+ {
10886
+ filename: "packages/ui/src/hooks/use-highlighted-code.ts",
10887
+ template: `import * as React from "react"
10888
+
10889
+ import { loadHighlighter } from "@<%= projectName %>/ui/lib/highlighter"
10890
+
10891
+ export function useHighlightedCode(value: string, language: string) {
10892
+ // A trailing newline would render as an extra empty line.
10893
+ const code = value.endsWith("\\n") ? value.slice(0, -1) : value
10894
+ const [loaded, setLoaded] = React.useState<{
10895
+ language: string
10896
+ highlighter: Awaited<ReturnType<typeof loadHighlighter>>
10897
+ } | null>(null)
10898
+ const highlighter = loaded?.language === language ? loaded.highlighter : null
10899
+
10900
+ React.useEffect(() => {
10901
+ let cancelled = false
10902
+
10903
+ loadHighlighter(language)
10904
+ .then((next) => {
10905
+ if (!cancelled) {
10906
+ setLoaded({ language, highlighter: next })
10907
+ }
10908
+ })
10909
+ .catch(() => {})
10910
+
10911
+ return () => {
10912
+ cancelled = true
10913
+ }
10914
+ }, [language])
10915
+
10916
+ // The complete lines, highlighted. While the code streams, only the new
10917
+ // lines are tokenized, starting from the grammar state of the last one.
10918
+ const [done, setDone] = React.useState<{
10919
+ highlighter: typeof highlighter
10920
+ text: string
10921
+ lines: React.ReactNode[]
10922
+ state?: Parameters<NonNullable<typeof highlighter>["highlight"]>[1]
10923
+ }>({ highlighter: null, text: "", lines: [] })
10924
+
10925
+ let next = done
10926
+
10927
+ if (next.highlighter !== highlighter || !code.startsWith(next.text)) {
10928
+ next = { highlighter, text: "", lines: [] }
10929
+ }
10930
+
10931
+ const end = code.lastIndexOf("\\n") + 1
10932
+
10933
+ if (highlighter && end > next.text.length) {
10934
+ const result = highlighter.highlight(
10935
+ code.slice(next.text.length, end - 1),
10936
+ next.state
10937
+ )
10938
+
10939
+ next = {
10940
+ highlighter,
10941
+ text: code.slice(0, end),
10942
+ lines: [...next.lines, ...result.lines],
10943
+ state: result.state,
10944
+ }
10945
+ }
10946
+
10947
+ if (next !== done) {
10948
+ setDone(next)
10949
+ }
10950
+
10951
+ // The last line is still being written, so it is tokenized on every render.
10952
+ const tail = code.slice(end)
10953
+
10954
+ return React.useMemo(() => {
10955
+ if (!highlighter) {
10956
+ return code.split("\\n")
10957
+ }
10958
+
10959
+ return [...next.lines, highlighter.highlight(tail, next.state).lines[0]]
10960
+ }, [code, highlighter, next, tail])
10961
+ }
10962
+ `
10963
+ },
10964
+ {
10965
+ filename: "packages/ui/src/lib/highlighter.tsx",
10966
+ template: `import * as React from "react"
10967
+ import {
10968
+ createHighlighterCore,
10969
+ type GrammarState,
10970
+ type HighlighterCore,
10971
+ } from "shiki/core"
10972
+ import { createJavaScriptRegexEngine } from "shiki/engine/javascript"
10973
+
10974
+ // Add the languages you need. Each one loads the first time it is used.
10975
+ const languages = {
10976
+ bash: () => import("shiki/langs/shellscript.mjs"),
10977
+ css: () => import("shiki/langs/css.mjs"),
10978
+ diff: () => import("shiki/langs/diff.mjs"),
10979
+ html: () => import("shiki/langs/html.mjs"),
10980
+ javascript: () => import("shiki/langs/javascript.mjs"),
10981
+ json: () => import("shiki/langs/json.mjs"),
10982
+ jsx: () => import("shiki/langs/jsx.mjs"),
10983
+ markdown: () => import("shiki/langs/markdown.mjs"),
10984
+ python: () => import("shiki/langs/python.mjs"),
10985
+ sql: () => import("shiki/langs/sql.mjs"),
10986
+ tsx: () => import("shiki/langs/tsx.mjs"),
10987
+ typescript: () => import("shiki/langs/typescript.mjs"),
10988
+ yaml: () => import("shiki/langs/yaml.mjs"),
10989
+ }
10990
+
10991
+ const aliases: Record<string, keyof typeof languages> = {
10992
+ js: "javascript",
10993
+ md: "markdown",
10994
+ py: "python",
10995
+ sh: "bash",
10996
+ shell: "bash",
10997
+ shellscript: "bash",
10998
+ ts: "typescript",
10999
+ yml: "yaml",
11000
+ zsh: "bash",
11001
+ }
11002
+
11003
+ let highlighter: Promise<HighlighterCore> | undefined
11004
+
11005
+ function getHighlighter() {
11006
+ highlighter ??= createHighlighterCore({
11007
+ themes: [
11008
+ import("shiki/themes/github-light.mjs"),
11009
+ import("shiki/themes/github-dark.mjs"),
11010
+ ],
11011
+ langs: [],
11012
+ engine: createJavaScriptRegexEngine({ forgiving: true }),
11013
+ })
11014
+
11015
+ return highlighter
11016
+ }
11017
+
11018
+ function getLanguage(language: string) {
11019
+ const name = language.toLowerCase()
11020
+
11021
+ if (name === "ansi") {
11022
+ return name
11023
+ }
11024
+
11025
+ if (name in languages) {
11026
+ return name as keyof typeof languages
11027
+ }
11028
+
11029
+ return aliases[name] ?? null
11030
+ }
11031
+
11032
+ // Loads a language and returns a highlighter for it, or null for a language
11033
+ // that is not listed.
11034
+ export async function loadHighlighter(language: string) {
11035
+ const lang = getLanguage(language)
11036
+
11037
+ if (!lang) {
11038
+ return null
11039
+ }
11040
+
11041
+ const shiki = await getHighlighter()
11042
+
11043
+ if (lang !== "ansi" && !shiki.getLoadedLanguages().includes(lang)) {
11044
+ await shiki.loadLanguage(languages[lang])
11045
+ }
11046
+
11047
+ return {
11048
+ // Returns one React node per line, and the grammar state after the last
11049
+ // line. Pass that state back to continue where the code left off.
11050
+ highlight(code: string, state?: GrammarState) {
11051
+ const tokenize = (text: string) =>
11052
+ shiki.codeToTokens(text, {
11053
+ lang,
11054
+ themes: { light: "github-light", dark: "github-dark" },
11055
+ defaultColor: false,
11056
+ grammarState: state,
11057
+ }).tokens
11058
+
11059
+ // ANSI has no grammar state, so each line is tokenized on its own and
11060
+ // gets the same colors whether it arrives in one piece or streams.
11061
+ const tokens =
11062
+ lang === "ansi"
11063
+ ? code.split("\\n").flatMap((line) => tokenize(line))
11064
+ : tokenize(code)
11065
+
11066
+ return {
11067
+ lines: tokens.map((line) =>
11068
+ line.map((token, index) => (
11069
+ <span
11070
+ key={index}
11071
+ style={token.htmlStyle as React.CSSProperties}
11072
+ className="text-(--shiki-light) dark:text-(--shiki-dark)"
11073
+ >
11074
+ {token.content}
11075
+ </span>
11076
+ ))
11077
+ ),
11078
+ state: shiki.getLastGrammarState(tokens),
11079
+ }
11080
+ },
11081
+ }
11082
+ }
11083
+
11084
+ // Returns one React node per line, or null for a language that is not listed.
11085
+ export async function highlightCode(code: string, language: string) {
11086
+ const highlighter = await loadHighlighter(language)
11087
+
11088
+ return highlighter ? highlighter.highlight(code).lines : null
11089
+ }
9895
11090
  `
9896
11091
  },
9897
11092
  {
@@ -11222,7 +12417,7 @@ var oxlintrc_default = {
11222
12417
  }<% } %><% if (shadcn) { %>,
11223
12418
  "overrides": [
11224
12419
  {
11225
- "files": ["packages/ui/src/components/**"],
12420
+ "files": ["packages/ui/src/components/**", "packages/ui/src/lib/highlighter.tsx"],
11226
12421
  "rules": {<% if (antiSlop) { %>
11227
12422
  "anti-slop/no-chained-type-assertions": "off",
11228
12423
  "anti-slop/no-conditional-empty-object-spread": "off",