@tanstack/cta-ui 0.10.0-alpha.18 → 0.10.0-alpha.20

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.
Files changed (71) hide show
  1. package/lib/index.ts +16 -7
  2. package/lib-dist/index.d.ts +6 -1
  3. package/lib-dist/index.js +6 -3
  4. package/package.json +20 -7
  5. package/public/tailwind.svg +1 -0
  6. package/public/tanstack.png +0 -0
  7. package/public/typescript.svg +1 -0
  8. package/src/components/StatusList.tsx +22 -0
  9. package/src/components/add-on-info-dialog.tsx +39 -0
  10. package/src/components/cta-sidebar.tsx +55 -0
  11. package/src/components/custom-add-on-dialog.tsx +79 -0
  12. package/src/components/file-navigator.tsx +205 -0
  13. package/src/components/file-tree.tsx +18 -60
  14. package/src/components/file-viewer.tsx +11 -3
  15. package/src/components/sidebar-items/add-ons.tsx +91 -0
  16. package/src/components/sidebar-items/mode-selector.tsx +55 -0
  17. package/src/components/sidebar-items/project-name.tsx +29 -0
  18. package/src/components/sidebar-items/run-add-ons.tsx +71 -0
  19. package/src/components/sidebar-items/run-create-app.tsx +82 -0
  20. package/src/components/sidebar-items/starter.tsx +115 -0
  21. package/src/components/sidebar-items/typescript-switch.tsx +52 -0
  22. package/src/components/toaster.tsx +29 -0
  23. package/src/components/ui/button.tsx +21 -19
  24. package/src/components/ui/dialog.tsx +25 -20
  25. package/src/components/ui/dropdown-menu.tsx +255 -0
  26. package/src/components/ui/input.tsx +21 -0
  27. package/src/components/ui/label.tsx +22 -0
  28. package/src/components/ui/popover.tsx +46 -0
  29. package/src/components/ui/separator.tsx +28 -0
  30. package/src/components/ui/sheet.tsx +137 -0
  31. package/src/components/ui/sidebar.tsx +726 -0
  32. package/src/components/ui/skeleton.tsx +13 -0
  33. package/src/components/ui/sonner.tsx +23 -0
  34. package/src/components/ui/switch.tsx +29 -0
  35. package/src/components/ui/toggle-group.tsx +11 -11
  36. package/src/components/ui/toggle.tsx +15 -13
  37. package/src/components/ui/tooltip.tsx +61 -0
  38. package/src/components/ui/tree-view.tsx +17 -12
  39. package/src/engine-handling/add-to-app-wrapper.ts +114 -0
  40. package/src/engine-handling/create-app-wrapper.ts +107 -0
  41. package/src/engine-handling/file-helpers.ts +25 -0
  42. package/src/engine-handling/framework-registration.ts +11 -0
  43. package/src/engine-handling/generate-initial-payload.ts +93 -0
  44. package/src/engine-handling/server-environment.ts +13 -0
  45. package/src/file-classes.ts +54 -0
  46. package/src/hooks/use-mobile.ts +19 -0
  47. package/src/hooks/use-streaming-status.ts +70 -0
  48. package/src/lib/api.ts +90 -0
  49. package/src/routeTree.gen.ts +4 -27
  50. package/src/routes/__root.tsx +36 -7
  51. package/src/routes/api/add-to-app.ts +21 -0
  52. package/src/routes/api/create-app.ts +21 -0
  53. package/src/routes/api/dry-run-add-to-app.ts +16 -0
  54. package/src/routes/api/dry-run-create-app.ts +16 -0
  55. package/src/routes/api/initial-payload.ts +10 -0
  56. package/src/routes/api/load-remote-add-on.ts +42 -0
  57. package/src/routes/api/load-starter.ts +47 -0
  58. package/src/routes/api/shutdown.ts +11 -0
  59. package/src/routes/index.tsx +3 -210
  60. package/src/store/add-ons.ts +81 -0
  61. package/src/store/project.ts +268 -0
  62. package/src/styles.css +47 -0
  63. package/src/types.d.ts +87 -0
  64. package/tests/store/add-ons.test.ts +222 -0
  65. package/vitest.config.ts +6 -0
  66. package/.cursorrules +0 -7
  67. package/src/components/Header.tsx +0 -13
  68. package/src/components/applied-add-on.tsx +0 -149
  69. package/src/lib/server-fns.ts +0 -78
  70. package/src/routes/api.demo-names.ts +0 -11
  71. package/src/routes/demo.tanstack-query.tsx +0 -28
@@ -1,222 +1,15 @@
1
- import { useState, Fragment } from 'react'
2
1
  import { createFileRoute } from '@tanstack/react-router'
3
- import { Info } from 'lucide-react'
4
2
 
5
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
6
- import {
7
- Table,
8
- TableBody,
9
- TableCell,
10
- TableHead,
11
- TableHeader,
12
- TableRow,
13
- } from '@/components/ui/table'
14
-
15
- import AppliedAddOn from '@/components/applied-add-on'
16
- import FileTree from '@/components/file-tree'
17
- import FileViewer from '@/components/file-viewer'
18
-
19
- import {
20
- getAddons,
21
- getAddonInfo,
22
- getOriginalOptions,
23
- runCreateApp,
24
- } from '@/lib/server-fns'
25
- import type { AddOn } from '@tanstack/cta-engine'
3
+ import FileNavigator from '@/components/file-navigator'
26
4
 
27
5
  export const Route = createFileRoute('/')({
28
6
  component: App,
29
- loader: async () => {
30
- const originalOptions = await getOriginalOptions()
31
- const [codeRouterAddons, fileRouterAddons] = await Promise.all([
32
- getAddons({
33
- data: { platform: 'react', mode: 'code-router' },
34
- }),
35
- getAddons({
36
- data: { platform: 'react', mode: 'file-router' },
37
- }),
38
- ])
39
- return {
40
- addOns: {
41
- 'code-router': codeRouterAddons,
42
- 'file-router': fileRouterAddons,
43
- },
44
- projectPath: process.env.PROJECT_PATH!,
45
- output: await runCreateApp({
46
- data: { withAddOn: true, options: originalOptions },
47
- }),
48
- outputWithoutAddon: await runCreateApp({
49
- data: { withAddOn: false, options: originalOptions },
50
- }),
51
- addOnInfo: (await getAddonInfo()) as AddOn,
52
- originalOptions,
53
- }
54
- },
55
7
  })
56
8
 
57
- const CAPTIONS = {
58
- dependencies: 'Dependencies',
59
- devDependencies: 'Dev Dependencies',
60
- scripts: 'Scripts',
61
- }
62
-
63
- function InfoViewer({ info }: { info: AddOn }) {
64
- return (
65
- <div className="text-lg">
66
- <div className="grid grid-cols-[200px_1fr] gap-4 mb-4">
67
- <div className="font-medium">Name</div>
68
- <div className="font-bold">{info.name}</div>
69
- <div className="font-medium">Description</div>
70
- <div className="font-bold">{info.description}</div>
71
- <div className="font-medium">Version</div>
72
- <div className="font-bold">{info.version}</div>
73
- <div className="font-medium">Author</div>
74
- <div className="font-bold">{info.author}</div>
75
- <div className="font-medium">License</div>
76
- <div className="font-bold">{info.license}</div>
77
- {(
78
- [
79
- 'dependencies',
80
- 'devDependencies',
81
- 'scripts',
82
- ] as (keyof AddOn['packageAdditions'])[]
83
- )
84
- .filter(
85
- (key) =>
86
- info.packageAdditions[key] &&
87
- Object.entries(info.packageAdditions[key]).length > 0,
88
- )
89
- .map((key) => (
90
- <Fragment key={key}>
91
- <div className="font-medium">{CAPTIONS[key]}</div>
92
- <Table>
93
- {key !== 'scripts' && (
94
- <TableHeader>
95
- <TableRow>
96
- <TableHead className="w-1/2">Package</TableHead>
97
- <TableHead className="w-1/2">Version</TableHead>
98
- </TableRow>
99
- </TableHeader>
100
- )}
101
- <TableBody>
102
- {Object.entries(info.packageAdditions[key]).map(
103
- ([pkg, version]) => (
104
- <TableRow key={`${key}-${pkg}`}>
105
- <TableCell className="w-1/2">{pkg}</TableCell>
106
- <TableCell className="w-1/2">{version}</TableCell>
107
- </TableRow>
108
- ),
109
- )}
110
- </TableBody>
111
- </Table>
112
- </Fragment>
113
- ))}
114
- {info.routes && (
115
- <>
116
- <div className="font-medium">Routes</div>
117
- <div>
118
- <Table>
119
- <TableHeader>
120
- <TableRow>
121
- <TableHead className="w-1/2">URL</TableHead>
122
- <TableHead className="w-1/2">Name</TableHead>
123
- </TableRow>
124
- </TableHeader>
125
- <TableBody>
126
- {info.routes.map(({ url, name }) => (
127
- <TableRow key={`${url}-${name}`}>
128
- <TableCell className="w-1/2">{url}</TableCell>
129
- <TableCell className="w-1/2">{name}</TableCell>
130
- </TableRow>
131
- ))}
132
- </TableBody>
133
- </Table>
134
- </div>
135
- </>
136
- )}
137
- {info.commmand && (
138
- <>
139
- <div className="font-medium">Setup Command</div>
140
- <div className="font-bold font-mono">{`${info.command.command} ${info.command.args.join(' ')}`}</div>
141
- </>
142
- )}
143
- </div>
144
- </div>
145
- )
146
- }
147
-
148
- function AddOnViewer({ addOnInfo }: { addOnInfo: AddOn }) {
149
- const [selectedFile, setSelectedFile] = useState<string | null>(null)
150
- const [showInfo, setShowInfo] = useState(false)
151
-
152
- return (
153
- <div className="flex flex-row">
154
- <FileTree
155
- prefix="./"
156
- tree={addOnInfo.files || {}}
157
- originalTree={{}}
158
- onFileSelected={(file) => {
159
- setSelectedFile(file)
160
- setShowInfo(false)
161
- }}
162
- extraTreeItems={[
163
- {
164
- id: 'info',
165
- name: 'Add-On Info',
166
- icon: () => <Info className="w-4 h-4 mr-2" />,
167
- onClick: () => {
168
- setSelectedFile(null)
169
- setShowInfo(true)
170
- },
171
- },
172
- ]}
173
- />
174
- <div className="max-w-3/4 w-3/4 pl-2">
175
- {showInfo && <InfoViewer info={addOnInfo as AddOn} />}
176
- {selectedFile ? (
177
- <FileViewer
178
- filePath={selectedFile}
179
- modifiedFile={addOnInfo?.files?.[selectedFile] || ''}
180
- />
181
- ) : null}
182
- </div>
183
- </div>
184
- )
185
- }
186
-
187
9
  function App() {
188
- const {
189
- projectPath,
190
- output,
191
- addOnInfo,
192
- outputWithoutAddon,
193
- originalOptions,
194
- addOns,
195
- } = Route.useLoaderData()
196
-
197
10
  return (
198
- <div className="p-5">
199
- <Tabs defaultValue="add-on">
200
- <TabsList className="grid grid-cols-2 w-[400px]">
201
- <TabsTrigger value="add-on">Add-On</TabsTrigger>
202
- <TabsTrigger value="applied">Applied</TabsTrigger>
203
- </TabsList>
204
- <TabsContent value="add-on">
205
- <AddOnViewer addOnInfo={addOnInfo} />
206
- </TabsContent>
207
- <TabsContent value="applied">
208
- <AppliedAddOn
209
- projectPath={projectPath}
210
- output={output}
211
- addOnInfo={addOnInfo}
212
- outputWithoutAddon={outputWithoutAddon}
213
- originalOptions={
214
- originalOptions as Required<typeof originalOptions>
215
- }
216
- addOns={addOns}
217
- />
218
- </TabsContent>
219
- </Tabs>
11
+ <div className="pl-3">
12
+ <FileNavigator />
220
13
  </div>
221
14
  )
222
15
  }
@@ -0,0 +1,81 @@
1
+ import type { AddOnInfo } from '@/types'
2
+
3
+ export function getAddOnStatus(
4
+ availableAddOns: Array<AddOnInfo>,
5
+ chosenAddOns: Array<string>,
6
+ originalAddOns: Array<string>,
7
+ ) {
8
+ const addOnMap = new Map<
9
+ string,
10
+ {
11
+ enabled: boolean
12
+ selected: boolean
13
+ dependedUpon: boolean
14
+ }
15
+ >()
16
+
17
+ for (const addOn of availableAddOns) {
18
+ addOnMap.set(addOn.id, {
19
+ selected: false,
20
+ enabled: true,
21
+ dependedUpon: false,
22
+ })
23
+ }
24
+
25
+ // Guard against cycles in the dependency graph. The results won't be great. But it won't crash.
26
+ function cycleGuardedSelectAndDisableDependsOn(startingAddOnId: string) {
27
+ const visited = new Set<string>()
28
+ function selectAndDisableDependsOn(addOnId: string) {
29
+ if (visited.has(addOnId)) {
30
+ return
31
+ }
32
+ visited.add(addOnId)
33
+ const selectedAddOn = availableAddOns.find(
34
+ (addOn) => addOn.id === addOnId,
35
+ )
36
+ if (selectedAddOn) {
37
+ for (const dependsOnId of selectedAddOn.dependsOn || []) {
38
+ const dependsOnAddOn = addOnMap.get(dependsOnId)
39
+ if (dependsOnAddOn) {
40
+ dependsOnAddOn.selected = true
41
+ dependsOnAddOn.enabled = false
42
+ dependsOnAddOn.dependedUpon = true
43
+ selectAndDisableDependsOn(dependsOnId)
44
+ }
45
+ }
46
+ const addOn = addOnMap.get(addOnId)
47
+ if (addOn) {
48
+ addOn.selected = true
49
+ if (!addOn.dependedUpon) {
50
+ addOn.enabled = true
51
+ }
52
+ }
53
+ }
54
+ }
55
+ selectAndDisableDependsOn(startingAddOnId)
56
+ }
57
+
58
+ for (const addOn of originalAddOns) {
59
+ const addOnInfo = addOnMap.get(addOn)
60
+ if (addOnInfo) {
61
+ addOnInfo.selected = true
62
+ addOnInfo.enabled = false
63
+ addOnInfo.dependedUpon = true
64
+ }
65
+ cycleGuardedSelectAndDisableDependsOn(addOn)
66
+ }
67
+
68
+ for (const addOnId of chosenAddOns) {
69
+ cycleGuardedSelectAndDisableDependsOn(addOnId)
70
+ }
71
+
72
+ return Object.fromEntries(
73
+ Array.from(addOnMap.entries()).map(([v, addOn]) => [
74
+ v,
75
+ {
76
+ enabled: addOn.enabled,
77
+ selected: addOn.selected,
78
+ },
79
+ ]),
80
+ )
81
+ }
@@ -0,0 +1,268 @@
1
+ import { useCallback, useMemo } from 'react'
2
+ import { create } from 'zustand'
3
+ import { useQuery } from '@tanstack/react-query'
4
+
5
+ import { getAddOnStatus } from './add-ons'
6
+
7
+ import type { Mode, SerializedOptions } from '@tanstack/cta-engine'
8
+
9
+ import type { AddOnInfo, DryRunOutput, StarterInfo } from '@/types.js'
10
+ import { dryRunAddToApp, dryRunCreateApp, loadInitialData } from '@/lib/api'
11
+
12
+ const useInitialData = () =>
13
+ useQuery({
14
+ queryKey: ['initial-data'],
15
+ queryFn: async () => loadInitialData(),
16
+ initialData: {
17
+ options: {
18
+ framework: 'react-cra',
19
+ mode: 'file-router',
20
+ projectName: 'my-application',
21
+ targetDir: 'my-application',
22
+ typescript: true,
23
+ tailwind: true,
24
+ git: true,
25
+ chosenAddOns: [],
26
+ packageManager: 'pnpm',
27
+ },
28
+ localFiles: {},
29
+ output: {
30
+ files: {},
31
+ commands: [],
32
+ deletedFiles: [],
33
+ },
34
+ addOns: {
35
+ 'code-router': [],
36
+ 'file-router': [],
37
+ },
38
+ applicationMode: 'none',
39
+ },
40
+ })
41
+
42
+ export const useProjectLocalFiles = () => useInitialData().data.localFiles
43
+ export const useOriginalOutput = () => useInitialData().data.output
44
+ export const useOriginalSelectedAddOns = () =>
45
+ useInitialData().data.options.chosenAddOns
46
+ export const useApplicationMode = () => useInitialData().data.applicationMode
47
+ export const useReady = () => useInitialData().isFetched
48
+ export const useCodeRouterAddOns = () =>
49
+ useInitialData().data.addOns['code-router']
50
+ export const useFileRouterAddOns = () =>
51
+ useInitialData().data.addOns['file-router']
52
+
53
+ export const useProjectOptions = create<SerializedOptions>(() => ({
54
+ framework: 'react-cra',
55
+ mode: 'file-router',
56
+ projectName: 'my-app',
57
+ targetDir: 'my-app',
58
+ typescript: true,
59
+ tailwind: true,
60
+ git: true,
61
+ chosenAddOns: [],
62
+ packageManager: 'pnpm',
63
+ }))
64
+
65
+ const useApplicationSettings = create<{
66
+ includeFiles: Array<string>
67
+ }>(() => ({
68
+ includeFiles: ['unchanged', 'added', 'modified', 'deleted', 'overwritten'],
69
+ }))
70
+
71
+ const useMutableAddOns = create<{
72
+ userSelectedAddOns: Array<string>
73
+ customAddOns: Array<AddOnInfo>
74
+ }>(() => ({
75
+ userSelectedAddOns: [],
76
+ customAddOns: [],
77
+ }))
78
+
79
+ export const useProjectStarter = create<{
80
+ projectStarter: StarterInfo | undefined
81
+ }>(() => ({
82
+ projectStarter: undefined,
83
+ }))
84
+
85
+ export function addCustomAddOn(addOn: AddOnInfo) {
86
+ useMutableAddOns.setState((state) => ({
87
+ customAddOns: [...state.customAddOns, addOn],
88
+ }))
89
+ if (addOn.modes.includes(useProjectOptions.getState().mode)) {
90
+ useMutableAddOns.setState((state) => ({
91
+ userSelectedAddOns: [...state.userSelectedAddOns, addOn.id],
92
+ }))
93
+ }
94
+ }
95
+
96
+ export function useAddOns() {
97
+ const routerMode = useRouterMode()
98
+ const originalSelectedAddOns = useOriginalSelectedAddOns()
99
+ const codeRouterAddOns = useCodeRouterAddOns()
100
+ const fileRouterAddOns = useFileRouterAddOns()
101
+ const { userSelectedAddOns, customAddOns } = useMutableAddOns()
102
+ const projectStarter = useProjectStarter().projectStarter
103
+
104
+ const availableAddOns = useMemo(() => {
105
+ const baseAddOns =
106
+ routerMode === 'code-router' ? codeRouterAddOns : fileRouterAddOns
107
+ return [
108
+ ...baseAddOns,
109
+ ...customAddOns.filter((addOn) => addOn.modes.includes(routerMode)),
110
+ ]
111
+ }, [routerMode, codeRouterAddOns, fileRouterAddOns, customAddOns])
112
+
113
+ const addOnState = useMemo(() => {
114
+ const originalAddOns: Set<string> = new Set()
115
+ for (const addOn of projectStarter?.dependsOn || []) {
116
+ originalAddOns.add(addOn)
117
+ }
118
+ for (const addOn of originalSelectedAddOns) {
119
+ originalAddOns.add(addOn)
120
+ }
121
+ return getAddOnStatus(
122
+ availableAddOns,
123
+ userSelectedAddOns,
124
+ Array.from(originalAddOns),
125
+ )
126
+ }, [
127
+ availableAddOns,
128
+ userSelectedAddOns,
129
+ originalSelectedAddOns,
130
+ projectStarter?.dependsOn,
131
+ ])
132
+
133
+ const chosenAddOns = useMemo(() => {
134
+ return Object.keys(addOnState).filter((addOn) => addOnState[addOn].selected)
135
+ }, [addOnState])
136
+
137
+ const toggleAddOn = useCallback(
138
+ (addOnId: string) => {
139
+ if (addOnState[addOnId] && addOnState[addOnId].enabled) {
140
+ if (addOnState[addOnId].selected) {
141
+ useMutableAddOns.setState((state) => ({
142
+ userSelectedAddOns: state.userSelectedAddOns.filter(
143
+ (addOn) => addOn !== addOnId,
144
+ ),
145
+ }))
146
+ } else {
147
+ useMutableAddOns.setState((state) => ({
148
+ userSelectedAddOns: [...state.userSelectedAddOns, addOnId],
149
+ }))
150
+ }
151
+ }
152
+ },
153
+ [addOnState],
154
+ )
155
+
156
+ return {
157
+ toggleAddOn,
158
+ chosenAddOns,
159
+ availableAddOns,
160
+ userSelectedAddOns,
161
+ originalSelectedAddOns,
162
+ addOnState,
163
+ }
164
+ }
165
+
166
+ const useHasProjectStarter = () =>
167
+ useProjectStarter((state) => state.projectStarter === undefined)
168
+
169
+ export const useModeEditable = () => useHasProjectStarter()
170
+
171
+ export const useTypeScriptEditable = () => {
172
+ const hasProjectStarter = useHasProjectStarter()
173
+ const routerMode = useRouterMode()
174
+ return hasProjectStarter && routerMode === 'code-router'
175
+ }
176
+
177
+ export const useTailwindEditable = () => {
178
+ const hasProjectStarter = useHasProjectStarter()
179
+ const routerMode = useRouterMode()
180
+ return hasProjectStarter && routerMode === 'code-router'
181
+ }
182
+
183
+ export const useProjectName = () =>
184
+ useProjectOptions((state) => state.projectName)
185
+
186
+ export const useRouterMode = () => useProjectOptions((state) => state.mode)
187
+
188
+ export function useFilters() {
189
+ const includedFiles = useApplicationSettings((state) => state.includeFiles)
190
+
191
+ const toggleFilter = useCallback((filter: string) => {
192
+ useApplicationSettings.setState((state) => ({
193
+ includeFiles: state.includeFiles.includes(filter)
194
+ ? state.includeFiles.filter((f) => f !== filter)
195
+ : [...state.includeFiles, filter],
196
+ }))
197
+ }, [])
198
+
199
+ return {
200
+ includedFiles,
201
+ toggleFilter,
202
+ }
203
+ }
204
+
205
+ export function useDryRun() {
206
+ const applicationMode = useApplicationMode()
207
+ const projectOptions = useProjectOptions()
208
+ const { userSelectedAddOns, chosenAddOns } = useAddOns()
209
+ const projectStarter = useProjectStarter().projectStarter
210
+
211
+ const { data: dryRunOutput } = useQuery<DryRunOutput>({
212
+ queryKey: [
213
+ 'dry-run',
214
+ applicationMode,
215
+ JSON.stringify(projectOptions),
216
+ JSON.stringify(userSelectedAddOns),
217
+ projectStarter?.url,
218
+ ],
219
+ queryFn: async () => {
220
+ if (applicationMode === 'none') {
221
+ return {
222
+ files: {},
223
+ commands: [],
224
+ deletedFiles: [],
225
+ }
226
+ } else if (applicationMode === 'setup') {
227
+ return dryRunCreateApp(projectOptions, chosenAddOns, projectStarter)
228
+ } else {
229
+ return dryRunAddToApp(userSelectedAddOns)
230
+ }
231
+ },
232
+ initialData: {
233
+ files: {},
234
+ commands: [],
235
+ deletedFiles: [],
236
+ },
237
+ })
238
+
239
+ return dryRunOutput
240
+ }
241
+
242
+ export const setProjectName = (projectName: string) =>
243
+ useProjectOptions.setState({
244
+ projectName,
245
+ })
246
+
247
+ export const setRouterMode = (mode: Mode) =>
248
+ useProjectOptions.setState({
249
+ mode,
250
+ })
251
+
252
+ export function setTypeScript(typescript: boolean) {
253
+ useProjectOptions.setState({
254
+ typescript,
255
+ })
256
+ }
257
+
258
+ export function setTailwind(tailwind: boolean) {
259
+ useProjectOptions.setState({
260
+ tailwind,
261
+ })
262
+ }
263
+
264
+ export function setProjectStarter(starter: StarterInfo | undefined) {
265
+ useProjectStarter.setState(() => ({
266
+ projectStarter: starter,
267
+ }))
268
+ }
package/src/styles.css CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  @plugin "tailwindcss-animate";
4
4
 
5
+ @source inline("bg-green-500");
6
+
5
7
  @custom-variant dark (&:is(.dark *));
6
8
 
7
9
  body {
@@ -128,6 +130,30 @@ code {
128
130
  --color-sidebar-ring: var(--sidebar-ring);
129
131
  }
130
132
 
133
+ @layer base {
134
+ :root {
135
+ --sidebar-background: 0 0% 98%;
136
+ --sidebar-foreground: 240 5.3% 26.1%;
137
+ --sidebar-primary: 240 5.9% 10%;
138
+ --sidebar-primary-foreground: 0 0% 98%;
139
+ --sidebar-accent: 240 4.8% 95.9%;
140
+ --sidebar-accent-foreground: 240 5.9% 10%;
141
+ --sidebar-border: 220 13% 91%;
142
+ --sidebar-ring: 217.2 91.2% 59.8%;
143
+ }
144
+
145
+ .dark {
146
+ --sidebar-background: 240 5.9% 10%;
147
+ --sidebar-foreground: 240 4.8% 95.9%;
148
+ --sidebar-primary: 224.3 76.3% 48%;
149
+ --sidebar-primary-foreground: 0 0% 100%;
150
+ --sidebar-accent: 240 3.7% 15.9%;
151
+ --sidebar-accent-foreground: 240 4.8% 95.9%;
152
+ --sidebar-border: 240 3.7% 15.9%;
153
+ --sidebar-ring: 217.2 91.2% 59.8%;
154
+ }
155
+ }
156
+
131
157
  @layer base {
132
158
  * {
133
159
  @apply border-border outline-ring/50;
@@ -136,3 +162,24 @@ code {
136
162
  @apply bg-background text-foreground;
137
163
  }
138
164
  }
165
+
166
+ [data-sidebar='group'] {
167
+ background-color: hsl(240 5.9% 6%) !important;
168
+ @apply mb-2 rounded-lg inset-shadow-gray-600 inset-shadow-sm;
169
+ }
170
+
171
+ [data-sidebar='content'] {
172
+ @apply p-2 pt-5;
173
+ }
174
+
175
+ [data-sidebar='header'] {
176
+ @apply shadow-gray-700 shadow-md z-50;
177
+ }
178
+
179
+ .cm-changedLine {
180
+ background-color: oklch(0.27 0.005 285.823) !important;
181
+ }
182
+
183
+ .file-filters {
184
+ background-color: oklch(0.2 0.005 285.823);
185
+ }