Skip to content

Commit 47542f8

Browse files
committed
split up fasthtml subprojects
1 parent bf7879f commit 47542f8

File tree

12 files changed

+813
-50
lines changed

12 files changed

+813
-50
lines changed

experimental/fasthtml/components/code_editor.py

Lines changed: 0 additions & 46 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
GPUCPP=../../../
2+
COMMON_FLAGS=-std=c++17 -s USE_WEBGPU=1 -s ASYNCIFY=1 -I$(GPUCPP)
3+
# Note - no spaces after comma
4+
# enable exceptions to recover from WGSL failure
5+
JS_FLAGS=-s EXPORTED_RUNTIME_METHODS=['UTF8ToString','setValue','addFunction'] -s EXPORTED_FUNCTIONS=['_malloc','_free','_executeKernel'] -s NO_DISABLE_EXCEPTION_CATCHING
6+
WASM_FLAGS=-s STANDALONE_WASM -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s EXPORTED_FUNCTIONS=['_executeKernel'] -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] -DSTANDALONE_WASM
7+
MODULARIZE_FLAGS=-s EXPORT_NAME='createModule' -s MODULARIZE=1 --bind
8+
NO_MODULARIZE_FLAGS=-s EXPORTED_FUNCTIONS=['_executeKernel'] -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] --bind
9+
10+
.PHONY: default cmake check-emsdk browser clean server
11+
12+
default: server
13+
14+
build/run.js: check-emsdk run.cpp
15+
em++ run.cpp -o build/run.js \
16+
$(COMMON_FLAGS) $(JS_FLAGS) $(MODULARIZE_FLAGS)
17+
18+
build/run.wasm: check-emsdk run.cpp
19+
em++ run.cpp -o build/run.wasm \
20+
$(COMMON_FLAGS) $(WASM_FLAGS)
21+
22+
watch:
23+
@echo "Watching for changes..."
24+
ls run.cpp | entr -c make build/run.js
25+
26+
server: build/run.js
27+
python3 run.py
28+
29+
clean:
30+
rm -rf build/*
31+
32+
check-emsdk:
33+
@which em++ > /dev/null || (echo "emsdk not found. Please install emsdk and run 'source emsdk_env.sh' in the emsdk directory." && exit 1)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const State = {
2+
editor: null,
3+
terminal: null,
4+
module: null,
5+
completionTippy: null,
6+
currentCompletion: '',
7+
isModuleReady: false
8+
};
9+
10+
11+
function initializeApp() {
12+
initializeTerminal();
13+
initializeEditor();
14+
initializeModule();
15+
setupEventListeners();
16+
}
17+
18+
function initializeTerminal() {
19+
AppState.terminal = new Terminal();
20+
const fitAddon = new FitAddon.FitAddon();
21+
AppState.terminal.loadAddon(fitAddon);
22+
AppState.terminal.open(document.getElementById('output'));
23+
fitAddon.fit();
24+
console.log("Terminal initialized");
25+
}
26+
27+
28+
function initializeEditor(initialContent) {
29+
AppState.editor = ace.edit("editor");
30+
AppState.editor.setTheme("ace/theme/monokai");
31+
AppState.editor.session.setMode("ace/mode/javascript");
32+
AppState.editor.setOptions({
33+
fontSize: "14px",
34+
showPrintMargin: false,
35+
showGutter: false,
36+
highlightActiveLine: true,
37+
wrap: true,
38+
});
39+
AppState.editor.setKeyboardHandler("ace/keyboard/vim");
40+
AppState.editor.setValue(initialContent || '');
41+
42+
AppState.completionTippy = tippy(document.getElementById('editor'), {
43+
content: 'Loading...',
44+
trigger: 'manual',
45+
placement: 'top-start',
46+
arrow: true,
47+
interactive: true
48+
});
49+
50+
console.log("Editor initialized");
51+
}
52+
53+
function initializeModule() {
54+
createModule().then((Module) => {
55+
AppState.module = Module;
56+
AppState.module.print = customPrint;
57+
AppState.module.printErr = customPrint;
58+
AppState.isModuleReady = true;
59+
console.log("Module initialized");
60+
// Attempt to run the kernel with the initial content
61+
updateEditor({ action: 'insert', lines: [''] });
62+
}).catch(error => {
63+
console.error("Failed to initialize module:", error);
64+
});
65+
}
66+
67+
function setupEventListeners() {
68+
AppState.editor.session.on('change', updateEditor);
69+
window.addEventListener('resize', () => AppState.editor.resize());
70+
71+
AppState.editor.commands.addCommand({
72+
name: 'insertCompletion',
73+
bindKey: {win: 'Tab', mac: 'Tab'},
74+
exec: function(editor) {
75+
if (AppState.currentCompletion) {
76+
editor.insert(AppState.currentCompletion);
77+
AppState.currentCompletion = '';
78+
AppState.completionTippy.hide();
79+
} else {
80+
editor.indent();
81+
}
82+
}
83+
});
84+
}
85+
86+
function customPrint(text) {
87+
console.log(text);
88+
if (AppState.terminal) {
89+
AppState.terminal.writeln(text);
90+
} else {
91+
console.warn("Terminal not initialized");
92+
}
93+
}
94+
95+
function updateEditor(delta) {
96+
if (delta.action === 'insert' && (delta.lines[0] === '.' || delta.lines[0] === ' ')) {
97+
showCompletionSuggestion();
98+
}
99+
100+
if (!AppState.isModuleReady) {
101+
console.log("Module not ready, waiting...");
102+
return;
103+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
2+
let editor;
3+
let completionTippy;
4+
let currentCompletion = '';
5+
6+
function updateEditor(delta) {
7+
if (delta.action === 'insert' && (delta.lines[0] === '.' || delta.lines[0] === ' ')) {
8+
showCompletionSuggestion();
9+
}
10+
11+
// Recover from errors TODO(avh): only do this if there's an error
12+
createModule().then((Module) => {
13+
// Keep your existing Module setup
14+
Module.print = window.customPrint;
15+
Module.printErr = window.customPrint;
16+
window.Module = Module;
17+
console.log("updateEditor() - Module ready");
18+
});
19+
20+
if (window.Module && window.Module.executeKernel) {
21+
console.log("Executing kernel");
22+
window.terminal.clear();
23+
window.Module.executeKernel(editor.getValue());
24+
} else {
25+
console.log("updateEditor() - Module not ready");
26+
}
27+
}
28+
29+
function initEditor(initial_content) {
30+
editor = ace.edit("editor");
31+
editor.setTheme("ace/theme/monokai");
32+
editor.session.setMode("ace/mode/javascript");
33+
editor.setOptions({
34+
fontSize: "14px",
35+
showPrintMargin: false,
36+
// disable showing errors in gutter, Ace's WGSL parser is out of date
37+
showGutter: false,
38+
highlightActiveLine: true,
39+
wrap: true,
40+
});
41+
editor.setKeyboardHandler("ace/keyboard/vim");
42+
editor.setValue(initial_content);
43+
window.addEventListener('resize', function() {
44+
editor.resize();
45+
});
46+
// document.getElementById('language').addEventListener('change', function(e) {
47+
// let mode = "ace/mode/" + e.target.value;
48+
// editor.session.setMode(mode);
49+
// });
50+
51+
editor.session.on('change', updateEditor);
52+
53+
completionTippy = tippy(document.getElementById('editor'), {
54+
content: 'Loading...',
55+
trigger: 'manual',
56+
placement: 'top-start',
57+
arrow: true,
58+
interactive: true
59+
});
60+
61+
// Override the default tab behavior
62+
editor.commands.addCommand({
63+
name: 'insertCompletion',
64+
bindKey: {win: 'Tab', mac: 'Tab'},
65+
exec: function(editor) {
66+
if (currentCompletion) {
67+
editor.insert(currentCompletion);
68+
currentCompletion = '';
69+
completionTippy.hide();
70+
} else {
71+
editor.indent();
72+
}
73+
}
74+
});
75+
}
76+
77+
async function showCompletionSuggestion() {
78+
const cursorPosition = editor.getCursorPosition();
79+
const screenPosition = editor.renderer.textToScreenCoordinates(cursorPosition.row, cursorPosition.column);
80+
81+
completionTippy.setContent('Loading...');
82+
completionTippy.setProps({
83+
getReferenceClientRect: () => ({
84+
width: 0,
85+
height: 0,
86+
top: screenPosition.pageY,
87+
bottom: screenPosition.pageY,
88+
left: screenPosition.pageX,
89+
right: screenPosition.pageX,
90+
})
91+
});
92+
completionTippy.show();
93+
94+
try {
95+
const response = await fetch('/complete', {
96+
method: 'POST',
97+
headers: {
98+
'Content-Type': 'application/json',
99+
},
100+
body: JSON.stringify({
101+
code: editor.getValue(),
102+
row: cursorPosition.row,
103+
column: cursorPosition.column
104+
}),
105+
});
106+
107+
if (!response.ok) {
108+
throw new Error(`HTTP error! status: ${response.status}`);
109+
}
110+
111+
const data = await response.json();
112+
currentCompletion = data.completion;
113+
completionTippy.setContent(`${currentCompletion} (Press Tab to insert)`);
114+
} catch (error) {
115+
console.error('Error:', error);
116+
completionTippy.setContent('Error fetching completion');
117+
currentCompletion = '';
118+
}
119+
120+
setTimeout(() => {
121+
if (currentCompletion) {
122+
completionTippy.hide();
123+
currentCompletion = '';
124+
}
125+
}, 5000);
126+
}

0 commit comments

Comments
 (0)