Skip to content

Commit 1a80126

Browse files
committed
chore: Refactor code_editor.py for improved readability and maintainability. Also add support for saving files to sqlite and opening saved files
1 parent 70bc63d commit 1a80126

File tree

2 files changed

+272
-36
lines changed

2 files changed

+272
-36
lines changed

examples/code_editor.ipynb

Lines changed: 158 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
"- ~~Line number side bar~~\n",
2727
"- auto-language detection\n",
2828
"- inline autocompletion\n",
29-
"- save files\n",
30-
"- handle multiple files"
29+
"- ~~save files~~\n",
30+
"- ~~handle multiple files~~\n",
31+
"- execute code and show output"
3132
]
3233
},
3334
{
@@ -37,21 +38,51 @@
3738
"outputs": [],
3839
"source": [
3940
"#| export\n",
40-
"from fasthtml import *\n",
41+
"from fasthtml.fastapp import *\n",
42+
"\n",
43+
"# Ace Editor (https://ace.c9.io/)\n",
44+
"ace_editor = Script(src=\"https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.0/ace.min.js\")\n",
45+
"# Flexbox CSS (http://flexboxgrid.com/)\n",
46+
"gridlink = Link(rel=\"stylesheet\", href=\"https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css\", type=\"text/css\")\n",
4147
"\n",
4248
"css = Style('''\\\n",
49+
".sidebar {\n",
50+
" background-color: #f4f4f4;\n",
51+
" overflow-y: auto;\n",
52+
" padding: 10px;\n",
53+
" box-shadow: 2px 0 5px rgba(0,0,0,0.1);\n",
54+
" height: calc(100vh - 40px);\n",
55+
"}\n",
56+
"\n",
57+
"#editor-container {\n",
58+
" flex: 1;\n",
59+
" height: calc(100vh - 40px);\n",
60+
"}\n",
61+
"\n",
4362
"#editor {\n",
44-
" position: absolute;\n",
45-
" top: 0;\n",
46-
" right: 0;\n",
47-
" bottom: 0;\n",
48-
" left: 0;\n",
63+
" height: 100%;\n",
64+
" width: 100%;\n",
65+
"}\n",
66+
"\n",
67+
".box-row {\n",
68+
" border: 1px solid #ccc;\n",
4969
"}\n",
5070
"''')\n",
51-
"# Ace Editor (https://ace.c9.io/)\n",
52-
"ace_editor = Script(src=\"https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.0/ace.min.js\")\n",
53-
"app = FastHTML(hdrs=(ace_editor, css))\n",
54-
"rt = app.route"
71+
"\n",
72+
"app, files, File = fast_app('data/files.db', hdrs=(ace_editor, gridlink, css), id=int, filename=str, content=str, pk='id')\n",
73+
"rt = app.route\n",
74+
"\n",
75+
"id_curr = 'current-file'\n",
76+
"id_list = 'file-list'"
77+
]
78+
},
79+
{
80+
"cell_type": "code",
81+
"execution_count": null,
82+
"metadata": {},
83+
"outputs": [],
84+
"source": [
85+
"files.insert(File(filename='file1.txt', content='Hello, World!'))"
5586
]
5687
},
5788
{
@@ -62,9 +93,18 @@
6293
"source": [
6394
"#| export\n",
6495
"js_code = \"\"\"\\\n",
65-
"var editor = ace.edit(\"editor\");\n",
66-
"editor.setTheme(\"ace/theme/monokai\");\n",
67-
"editor.session.setMode(\"ace/mode/javascript\");\n",
96+
"function renderEditor() {\n",
97+
" var editor = ace.edit(\"editor\");\n",
98+
" editor.setTheme(\"ace/theme/monokai\");\n",
99+
" editor.session.setMode(\"ace/mode/javascript\");\n",
100+
"}\n",
101+
"\n",
102+
"function getFileContent() {\n",
103+
" var editor = ace.edit(\"editor\");\n",
104+
" return editor.getValue();\n",
105+
"}\n",
106+
"\n",
107+
"renderEditor();\n",
68108
"\"\"\"\n",
69109
"\n",
70110
"example_code = \"\"\"\\\n",
@@ -74,6 +114,72 @@
74114
"}\"\"\""
75115
]
76116
},
117+
{
118+
"cell_type": "code",
119+
"execution_count": null,
120+
"metadata": {},
121+
"outputs": [],
122+
"source": [
123+
"#| export\n",
124+
"def SaveFile():\n",
125+
" return Form(\n",
126+
" Input(type=\"text\", id=\"filename\", name=\"filename\", placeholder=\"Filename\", required=True),\n",
127+
" Button(\"Save\", type=\"submit\", hx_post=\"/save\", target_id=id_list, hx_swap=\"beforeend\", hx_vals=\"js:{content: getFileContent(), filename: filename.value}\"),\n",
128+
" cls=\"col-xs-12\"\n",
129+
" )\n",
130+
" ...\n",
131+
"def Toolbar():\n",
132+
" return Div(\n",
133+
" Div(\n",
134+
" Select(\n",
135+
" Option(\"JavaScript\", value=\"javascript\"),\n",
136+
" Option(\"Python\", value=\"python\"),\n",
137+
" Option(\"HTML\", value=\"html\"),\n",
138+
" Option(\"CSS\", value=\"css\"),\n",
139+
" Option(\"Markdown\", value=\"markdown\"),\n",
140+
" id=\"language\"\n",
141+
" ),\n",
142+
" Button(\"Run\", id=\"run\"),\n",
143+
" SaveFile(),\n",
144+
" cls=\"col-xs-12 toolbar\"\n",
145+
" ),\n",
146+
" cls=\"row\"\n",
147+
" )"
148+
]
149+
},
150+
{
151+
"cell_type": "code",
152+
"execution_count": null,
153+
"metadata": {},
154+
"outputs": [],
155+
"source": [
156+
"#| export\n",
157+
"def FileRow(file: File):\n",
158+
" return Li(\n",
159+
" A(\n",
160+
" file.filename, hx_get=f'/files/{file.id}', target_id=\"editor-container\", hx_swap=\"innerHTML\",\n",
161+
" hx_on=\"htmx:afterSwap: renderEditor()\",\n",
162+
" ),\n",
163+
" id=f'file-{file.id}'\n",
164+
" )"
165+
]
166+
},
167+
{
168+
"cell_type": "code",
169+
"execution_count": null,
170+
"metadata": {},
171+
"outputs": [],
172+
"source": [
173+
"#| export\n",
174+
"def Sidebar():\n",
175+
" return Div(\n",
176+
" Div(\n",
177+
" Ul(*map(FileRow, files()), id=id_list), cls=\"sidebar\"\n",
178+
" ),\n",
179+
" cls=\"col-xs-12 col-sm-3\"\n",
180+
" )"
181+
]
182+
},
77183
{
78184
"cell_type": "code",
79185
"execution_count": 3,
@@ -82,8 +188,16 @@
82188
"source": [
83189
"#| export\n",
84190
"def CodeEditor():\n",
85-
" editor = Div(example_code, id=\"editor\")\n",
86-
" return Title(\"Code Editor\",), editor, Script(NotStr(js_code))"
191+
" toolbar = Toolbar()\n",
192+
" main = Div(\n",
193+
" Sidebar(),\n",
194+
" Div(\n",
195+
" Div(example_code, id=\"editor\"),\n",
196+
" id=\"editor-container\", cls=\"col-xs-12 col-sm-9\", hx_on=\"htmx:afterSwap: renderEditor()\"\n",
197+
" ),\n",
198+
" cls=\"row\"\n",
199+
" )\n",
200+
" return Title(\"Code Editor\",), Div(toolbar, main, cls=\"container-fluid\"), Script(NotStr(js_code))"
87201
]
88202
},
89203
{
@@ -134,7 +248,33 @@
134248
},
135249
{
136250
"cell_type": "code",
137-
"execution_count": 22,
251+
"execution_count": null,
252+
"metadata": {},
253+
"outputs": [],
254+
"source": [
255+
"#| export\n",
256+
"@rt(\"/files/{id}\")\n",
257+
"def get(id:int):\n",
258+
" return Div(files[id].content, id=\"editor\", cls=\"ace_editor ace-tm\")#, hx_on=\"htmx:afterSwap: renderEditor()\"),"
259+
]
260+
},
261+
{
262+
"cell_type": "code",
263+
"execution_count": null,
264+
"metadata": {},
265+
"outputs": [],
266+
"source": [
267+
"#| export\n",
268+
"@rt(\"/save\")\n",
269+
"def post(filename: str, content: str):\n",
270+
" file = File(filename=filename, content=content, id=len(files()) + 1)\n",
271+
" files.insert(file)\n",
272+
" return FileRow(file)"
273+
]
274+
},
275+
{
276+
"cell_type": "code",
277+
"execution_count": 53,
138278
"metadata": {},
139279
"outputs": [],
140280
"source": [

examples/code_editor.py

Lines changed: 114 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,61 @@
11
# AUTOGENERATED! DO NOT EDIT! File to edit: code_editor.ipynb.
22

33
# %% auto 0
4-
__all__ = ['css', 'ace_editor', 'app', 'rt', 'js_code', 'example_code', 'CodeEditor', 'get']
4+
__all__ = ['ace_editor', 'gridlink', 'css', 'app', 'files', 'File', 'rt', 'id_curr', 'id_list', 'js_code', 'example_code',
5+
'SaveFile', 'Toolbar', 'FileRow', 'Sidebar', 'CodeEditor', 'get', 'post']
56

67
# %% code_editor.ipynb 3
7-
from fasthtml import *
8+
from fasthtml.fastapp import *
9+
10+
# Ace Editor (https://ace.c9.io/)
11+
ace_editor = Script(src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.0/ace.min.js")
12+
# Flexbox CSS (http://flexboxgrid.com/)
13+
gridlink = Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css", type="text/css")
814

915
css = Style('''\
16+
.sidebar {
17+
background-color: #f4f4f4;
18+
overflow-y: auto;
19+
padding: 10px;
20+
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
21+
height: calc(100vh - 40px);
22+
}
23+
24+
#editor-container {
25+
flex: 1;
26+
height: calc(100vh - 40px);
27+
}
28+
1029
#editor {
11-
position: absolute;
12-
top: 0;
13-
right: 0;
14-
bottom: 0;
15-
left: 0;
30+
height: 100%;
31+
width: 100%;
32+
}
33+
34+
.box-row {
35+
border: 1px solid #ccc;
1636
}
1737
''')
18-
# Ace Editor (https://ace.c9.io/)
19-
ace_editor = Script(src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.0/ace.min.js")
20-
app = FastHTML(hdrs=(ace_editor, css))
38+
39+
app, files, File = fast_app('data/files.db', hdrs=(ace_editor, gridlink, css), id=int, filename=str, content=str, pk='id')
2140
rt = app.route
2241

23-
# %% code_editor.ipynb 4
42+
id_curr = 'current-file'
43+
id_list = 'file-list'
44+
45+
# %% code_editor.ipynb 5
2446
js_code = """\
25-
var editor = ace.edit("editor");
26-
editor.setTheme("ace/theme/monokai");
27-
editor.session.setMode("ace/mode/javascript");
47+
function renderEditor() {
48+
var editor = ace.edit("editor");
49+
editor.setTheme("ace/theme/monokai");
50+
editor.session.setMode("ace/mode/javascript");
51+
}
52+
53+
function getFileContent() {
54+
var editor = ace.edit("editor");
55+
return editor.getValue();
56+
}
57+
58+
renderEditor();
2859
"""
2960

3061
example_code = """\
@@ -33,12 +64,77 @@
3364
return x;
3465
}"""
3566

36-
# %% code_editor.ipynb 5
37-
def CodeEditor():
38-
editor = Div(example_code, id="editor")
39-
return Title("Code Editor",), editor, Script(NotStr(js_code))
67+
# %% code_editor.ipynb 6
68+
def SaveFile():
69+
return Form(
70+
Input(type="text", id="filename", name="filename", placeholder="Filename", required=True),
71+
Button("Save", type="submit", hx_post="/save", target_id=id_list, hx_swap="beforeend", hx_vals="js:{content: getFileContent(), filename: filename.value}"),
72+
cls="col-xs-12"
73+
)
74+
...
75+
def Toolbar():
76+
return Div(
77+
Div(
78+
Select(
79+
Option("JavaScript", value="javascript"),
80+
Option("Python", value="python"),
81+
Option("HTML", value="html"),
82+
Option("CSS", value="css"),
83+
Option("Markdown", value="markdown"),
84+
id="language"
85+
),
86+
Button("Run", id="run"),
87+
SaveFile(),
88+
cls="col-xs-12 toolbar"
89+
),
90+
cls="row"
91+
)
4092

4193
# %% code_editor.ipynb 7
94+
def FileRow(file: File):
95+
return Li(
96+
A(
97+
file.filename, hx_get=f'/files/{file.id}', target_id="editor-container", hx_swap="innerHTML",
98+
hx_on="htmx:afterSwap: renderEditor()",
99+
),
100+
id=f'file-{file.id}'
101+
)
102+
103+
# %% code_editor.ipynb 8
104+
def Sidebar():
105+
return Div(
106+
Div(
107+
Ul(*map(FileRow, files()), id=id_list), cls="sidebar"
108+
),
109+
cls="col-xs-12 col-sm-3"
110+
)
111+
112+
# %% code_editor.ipynb 9
113+
def CodeEditor():
114+
toolbar = Toolbar()
115+
main = Div(
116+
Sidebar(),
117+
Div(
118+
Div(example_code, id="editor"),
119+
id="editor-container", cls="col-xs-12 col-sm-9", hx_on="htmx:afterSwap: renderEditor()"
120+
),
121+
cls="row"
122+
)
123+
return Title("Code Editor",), Div(toolbar, main, cls="container-fluid"), Script(NotStr(js_code))
124+
125+
# %% code_editor.ipynb 11
42126
@rt("/")
43127
def get():
44128
return CodeEditor()
129+
130+
# %% code_editor.ipynb 12
131+
@rt("/files/{id}")
132+
def get(id:int):
133+
return Div(files[id].content, id="editor", cls="ace_editor ace-tm")#, hx_on="htmx:afterSwap: renderEditor()"),
134+
135+
# %% code_editor.ipynb 13
136+
@rt("/save")
137+
def post(filename: str, content: str):
138+
file = File(filename=filename, content=content, id=len(files()) + 1)
139+
files.insert(file)
140+
return FileRow(file)

0 commit comments

Comments
 (0)