Skip to content

Commit 338539b

Browse files
committed
Add CodeBrowser
1 parent 4adde62 commit 338539b

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.ch-code-browser {
2+
display: flex;
3+
height: 100%;
4+
font-family: Ubuntu, Droid Sans, -apple-system,
5+
BlinkMacSystemFont, Segoe WPC, Segoe UI, sans-serif;
6+
}
7+
8+
.ch-code-browser-sidebar {
9+
border-right: 1px solid;
10+
min-width: 100px;
11+
padding: 1em 0;
12+
}
13+
14+
.ch-code-browser-content {
15+
overflow: auto;
16+
flex: 1;
17+
padding: 1em;
18+
white-space: pre;
19+
font-family: monospace;
20+
font-weight: normal;
21+
font-size: 14px;
22+
line-height: 19px;
23+
letter-spacing: 0px;
24+
}
25+
26+
.ch-code-browser-content ::selection {
27+
background-color: var(--ch-selection-background);
28+
color: inherit;
29+
}
30+
31+
.ch-code-browser-sidebar-folder,
32+
.ch-code-browser-sidebar-file {
33+
padding: 0.1em 1em;
34+
}
35+
36+
.ch-code-browser-sidebar-file {
37+
cursor: pointer;
38+
}
39+
40+
.ch-code-browser-sidebar-file:hover {
41+
background-color: var(--ch-hover-background);
42+
color: var(--ch-hover-foreground);
43+
}
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import { CodeFile } from "./editor-shift"
2+
import { IRawTheme } from "vscode-textmate"
3+
import { ColorName, getColor } from "utils"
4+
import React from "react"
5+
6+
export function CodeBrowser({
7+
files,
8+
theme,
9+
startingFileName,
10+
}: {
11+
files: CodeFile[]
12+
theme: IRawTheme
13+
startingFileName: string
14+
}) {
15+
const [activeFile, setActiveFile] = React.useState(() =>
16+
files.find(f => f.name === startingFileName)
17+
)
18+
19+
return (
20+
<div
21+
className="ch-code-browser"
22+
style={{
23+
color: getColor(theme, ColorName.EditorForeground),
24+
}}
25+
>
26+
<Sidebar
27+
files={files}
28+
theme={theme}
29+
activeFile={activeFile}
30+
setActiveFile={setActiveFile}
31+
/>
32+
<Content file={activeFile} theme={theme} />
33+
</div>
34+
)
35+
}
36+
37+
function Sidebar({
38+
theme,
39+
files,
40+
activeFile,
41+
setActiveFile,
42+
}: {
43+
files: CodeFile[]
44+
theme: IRawTheme
45+
activeFile: CodeFile
46+
setActiveFile: (file: CodeFile) => void
47+
}) {
48+
const tree = React.useMemo(
49+
() => toFileTree(files),
50+
[files]
51+
)
52+
return (
53+
<div
54+
className="ch-code-browser-sidebar"
55+
style={{
56+
borderColor: getColor(
57+
theme,
58+
ColorName.SideBarBorder
59+
),
60+
background: getColor(
61+
theme,
62+
ColorName.SideBarBackground
63+
),
64+
color: getColor(theme, ColorName.SideBarForeground),
65+
["--ch-list-selection-background" as any]: getColor(
66+
theme,
67+
ColorName.ListActiveSelectionBackground
68+
),
69+
["--ch-list-selection-foreground" as any]: getColor(
70+
theme,
71+
ColorName.ListActiveSelectionForeground
72+
),
73+
["--ch-hover-background" as any]: getColor(
74+
theme,
75+
ColorName.ListHoverBackground
76+
),
77+
["--ch-hover-foreground" as any]: getColor(
78+
theme,
79+
ColorName.ListHoverForeground
80+
),
81+
}}
82+
>
83+
<SidebarNodes
84+
tree={tree}
85+
activeFile={activeFile}
86+
setActiveFile={setActiveFile}
87+
/>
88+
</div>
89+
)
90+
}
91+
92+
function SidebarNodes({
93+
tree,
94+
activeFile,
95+
setActiveFile,
96+
level = 0,
97+
}: {
98+
tree: Node[]
99+
activeFile: CodeFile
100+
setActiveFile: (file: CodeFile) => void
101+
level?: number
102+
}) {
103+
return (
104+
<>
105+
{tree.map(node => (
106+
<SidebarNode
107+
key={node.name}
108+
node={node}
109+
activeFile={activeFile}
110+
setActiveFile={setActiveFile}
111+
level={level}
112+
/>
113+
))}
114+
</>
115+
)
116+
}
117+
function SidebarNode({
118+
node,
119+
activeFile,
120+
setActiveFile,
121+
level,
122+
}: {
123+
node: Node
124+
activeFile: CodeFile
125+
setActiveFile: (file: CodeFile) => void
126+
level?: number
127+
}) {
128+
const isFolder = node.children && node.children.length > 0
129+
const isSelected = node.codeFile === activeFile
130+
if (isFolder) {
131+
return (
132+
<div>
133+
<div className="ch-code-browser-sidebar-folder">
134+
<div style={{ paddingLeft: level * 1.5 + "ch" }}>
135+
{node.name}
136+
</div>
137+
</div>
138+
<SidebarNodes
139+
tree={node.children}
140+
activeFile={activeFile}
141+
setActiveFile={setActiveFile}
142+
level={level + 1}
143+
/>
144+
</div>
145+
)
146+
} else {
147+
return (
148+
<div>
149+
<div
150+
className="ch-code-browser-sidebar-file"
151+
onClick={() => setActiveFile(node.codeFile)}
152+
style={
153+
isSelected
154+
? {
155+
color:
156+
"var(--ch-list-selection-foreground)",
157+
background:
158+
"var(--ch-list-selection-background)",
159+
}
160+
: {}
161+
}
162+
>
163+
<div style={{ paddingLeft: level * 1.5 + "ch" }}>
164+
{node.name}
165+
</div>
166+
</div>
167+
</div>
168+
)
169+
}
170+
}
171+
172+
function Content({
173+
file,
174+
theme,
175+
}: {
176+
file: CodeFile
177+
theme: IRawTheme
178+
}) {
179+
return (
180+
<div
181+
className="ch-code-browser-content"
182+
style={{
183+
background: getColor(
184+
theme,
185+
ColorName.CodeBackground
186+
),
187+
color: getColor(theme, ColorName.CodeForeground),
188+
["--ch-selection-background" as any]: getColor(
189+
theme,
190+
ColorName.SelectionBackground
191+
),
192+
}}
193+
>
194+
{file.code.lines.map((line, i) => (
195+
<div key={i}>
196+
{line.tokens.map((token, i) => (
197+
<span key={i} {...token.props}>
198+
{token.content}
199+
</span>
200+
))}
201+
</div>
202+
))}
203+
</div>
204+
)
205+
}
206+
207+
type Node = {
208+
name: string
209+
codeFile: CodeFile
210+
children: Node[]
211+
}
212+
function toFileTree(files: CodeFile[]): Node[] {
213+
let tree = []
214+
for (const file of files) {
215+
const parts = file.name.split("/")
216+
let current = tree
217+
for (let i = 0; i < parts.length; i++) {
218+
const part = parts[i]
219+
const isLastPart = i === parts.length - 1
220+
const index = current.findIndex(f => f.name === part)
221+
if (index === -1) {
222+
const sub = {
223+
name: part,
224+
children: [],
225+
codeFile: undefined,
226+
}
227+
if (isLastPart) {
228+
sub.codeFile = file
229+
}
230+
current.push(sub)
231+
current = sub.children
232+
} else {
233+
current = current[index].children
234+
}
235+
}
236+
}
237+
238+
tree = sortTree(tree)
239+
return tree
240+
}
241+
242+
function sortTree(tree: Node[]): Node[] {
243+
for (const child of tree) {
244+
child.children = sortTree(child.children)
245+
}
246+
return tree.sort((a, b) => {
247+
const aIsFolder = a.children && a.children.length > 0
248+
const bIsFolder = b.children && b.children.length > 0
249+
if (
250+
(aIsFolder && bIsFolder) ||
251+
(!aIsFolder && !bIsFolder)
252+
) {
253+
return a.name.localeCompare(b.name)
254+
}
255+
if (aIsFolder) {
256+
return -1
257+
}
258+
return 1
259+
})
260+
}

packages/mdx/src/mini-editor/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import "../mini-frame/index.scss";
22
@import "../mini-terminal/index.scss";
33
@import "../smooth-code/index.scss";
4+
@import "./code-browser.scss";
45

56
/** tabs */
67

packages/mdx/src/utils/theme.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ export enum ColorName {
4646
InputBorder,
4747
SelectionBackground,
4848
IconForeground,
49+
ListActiveSelectionBackground,
50+
ListActiveSelectionForeground,
51+
ListHoverBackground,
52+
ListHoverForeground,
53+
SideBarBackground,
54+
SideBarForeground,
55+
SideBarBorder,
4956
}
5057

5158
type Color = string | undefined
@@ -210,6 +217,57 @@ export function getColor(
210217
hc: "#FFFFFF",
211218
})
212219
)
220+
221+
case ColorName.SideBarBackground:
222+
return (
223+
colors["sideBar.background"] ||
224+
getDefault(theme, {
225+
dark: "#252526",
226+
light: "#F3F3F3",
227+
hc: "#000000",
228+
})
229+
)
230+
case ColorName.SideBarForeground:
231+
return (
232+
colors["sideBar.foreground"] ||
233+
getColor(theme, ColorName.EditorForeground)
234+
)
235+
case ColorName.SideBarBorder:
236+
return (
237+
colors["sideBar.border"] ||
238+
getColor(theme, ColorName.SideBarBackground)
239+
)
240+
241+
case ColorName.ListActiveSelectionBackground:
242+
return (
243+
colors["list.activeSelectionBackground"] ||
244+
getDefault(theme, {
245+
dark: "#094771",
246+
light: "#0060C0",
247+
hc: "#000000",
248+
})
249+
)
250+
case ColorName.ListActiveSelectionForeground:
251+
return (
252+
colors["list.activeSelectionForeground"] ||
253+
getDefault(theme, {
254+
dark: "#fffffe",
255+
light: "#fffffe",
256+
hc: "#fffffe",
257+
})
258+
)
259+
case ColorName.ListHoverBackground:
260+
return (
261+
colors["list.hoverBackground"] ||
262+
getDefault(theme, {
263+
dark: "#2A2D2E",
264+
light: "#F0F0F0",
265+
hc: undefined,
266+
})
267+
)
268+
case ColorName.ListHoverForeground:
269+
return colors["list.hoverForeground"] || undefined
270+
213271
default:
214272
return "#f00"
215273
}

0 commit comments

Comments
 (0)