couple commits
This commit is contained in:
93
README.md
Normal file
93
README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# wasm_transcript_browser
|
||||
|
||||
This project generates a searchable transcript browser from local `.srt` files and ships it as a WebAssembly web app.
|
||||
|
||||
Type a word or phrase, click a match, and it opens the exact timestamp in the matching YouTube video. You can also copy a direct timestamped link.
|
||||
|
||||
## What it does
|
||||
|
||||
- Parses subtitle files (`.srt`) from a local folder.
|
||||
- Generates a packed transcript index (`build/entries.inc`) at build time.
|
||||
- Compiles a C codebase to `main.wasm` and serves it with a small HTML/JS shell.
|
||||
- Provides instant text search over all transcript text.
|
||||
- Creates timestamped YouTube links from search hits.
|
||||
|
||||
## How the pipeline works
|
||||
|
||||
1. `build_file.c` defines a hard-coded source folder:
|
||||
|
||||
- `folder_to_create_transcript_for`
|
||||
|
||||
2. During build, `src/prototype/prototype.meta.c`:
|
||||
|
||||
- scans `.srt` files in that folder,
|
||||
- parses subtitle entries,
|
||||
- normalizes text (lowercase, removes punctuation, turns `-` into spaces),
|
||||
- writes generated data to `build/entries.inc`.
|
||||
|
||||
3. `src/prototype/main.c` includes that generated file and compiles to WASM.
|
||||
|
||||
4. The browser app (`package/index.html` + `package/main.wasm`) renders a custom UI and handles link open/copy actions.
|
||||
|
||||
## Transcript filename format
|
||||
|
||||
To build correct YouTube links, filenames are expected to include the 11-char YouTube ID wrapped in one character on each side at the end of the name (commonly brackets).
|
||||
|
||||
Example:
|
||||
|
||||
- `My Video Title [dQw4w9WgXcQ].en.srt`
|
||||
- `My Video Title [dQw4w9WgXcQ].srt`
|
||||
|
||||
The app extracts the video ID from the ending token and creates links like:
|
||||
|
||||
- `https://youtu.be/<id>?feature=shared&t=<seconds>`
|
||||
|
||||
## Build
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- `clang` (or `gcc`/MSVC depending on your platform)
|
||||
- Python 3 (for local static server)
|
||||
|
||||
### Linux
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```bat
|
||||
build.bat
|
||||
```
|
||||
|
||||
Build output of interest:
|
||||
|
||||
- `build/entries.inc` (generated transcript index)
|
||||
- `package/index.html`
|
||||
- `package/main.wasm`
|
||||
|
||||
## Run locally
|
||||
|
||||
From `package/`:
|
||||
|
||||
```bash
|
||||
python3 -m http.server 8080
|
||||
```
|
||||
|
||||
Then open:
|
||||
|
||||
- `http://localhost:8080`
|
||||
|
||||
Windows helper script:
|
||||
|
||||
- `package/run_server.bat`
|
||||
|
||||
## Configuration notes
|
||||
|
||||
- Update transcript source folder in `build_file.c` before building.
|
||||
- The current build file also contains hard-coded deploy commands (`ssh`/`scp`) in `build_prototype_wasm_target`; remove or update those for your own environment.
|
||||
|
||||
## Project status
|
||||
|
||||
This is an older personal project with a custom C build/codegen stack. The rough edges are expected, but the core idea works: local transcript ingestion + fast search + one-click timestamped YouTube links.
|
||||
24
build_file.c
24
build_file.c
@@ -19,12 +19,22 @@
|
||||
#include "src/meta/meta_table_format.c"
|
||||
#include "src/meta/meta_cfiles.c"
|
||||
|
||||
s8_t folder_to_create_transcript_for = s8("D:/videos/jblow");
|
||||
|
||||
#include "src/app/app.meta.c"
|
||||
#include "src/ui/ui.meta.c"
|
||||
#include "src/render/render.meta.c"
|
||||
#include "src/prototype/prototype.meta.c"
|
||||
#include "src/testing/testing.meta.c"
|
||||
|
||||
b32 run_win32_app_base_target = false;
|
||||
b32 run_testing_target = false;
|
||||
b32 run_prototype_dll_target = false;
|
||||
b32 run_prototype_standalone_target = false;
|
||||
|
||||
b32 run_prototype_wasm_target = true;
|
||||
b32 run_server = false;
|
||||
|
||||
void build_testing_target(void) {
|
||||
if (!cache_code_modified(s8("../src/testing/testing_main.c"), s8("testing.exe"))) {
|
||||
return;
|
||||
@@ -102,6 +112,13 @@ void build_prototype_wasm_target(void) {
|
||||
);
|
||||
os_copy(s8("main.wasm"), s8("../package/main.wasm"), os_copy_overwrite);
|
||||
if (ok != 0) exit(ok);
|
||||
|
||||
// os_systemf("ssh root@65.21.244.51 rm /website/index.html /website/main.wasm");
|
||||
// os_systemf("scp ../package/index.html root@65.21.244.51:/website/index.html");
|
||||
// os_systemf("scp ../package/main.wasm root@65.21.244.51:/website/main.wasm");
|
||||
os_systemf("ssh root@157.90.144.237 rm /var/www/html/jiang/index.html /var/www/html/jiang/main.wasm");
|
||||
os_systemf("scp ../package/index.html root@157.90.144.237:/var/www/html/jiang/index.html");
|
||||
os_systemf("scp ../package/main.wasm root@157.90.144.237:/var/www/html/jiang/main.wasm");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,13 +168,6 @@ int main(int argc, char **argv) {
|
||||
generate_render_code(&tcx->temp);
|
||||
generate_testing_code(&tcx->temp);
|
||||
|
||||
b32 run_win32_app_base_target = true;
|
||||
b32 run_testing_target = false;
|
||||
b32 run_prototype_dll_target = false;
|
||||
b32 run_prototype_wasm_target = true;
|
||||
b32 run_prototype_standalone_target = false;
|
||||
b32 run_server = false;
|
||||
|
||||
if (run_server) {
|
||||
os_systemf("start /D ..\\package ..\\package\\run_server.bat");
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
function CC(cmd)
|
||||
Cmd { working_dir = GetProjectPath(), destination = "console", cmd = cmd }
|
||||
end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<title>Hello traveller!</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -60,6 +60,20 @@ class stream_t {
|
||||
}
|
||||
}
|
||||
|
||||
function unsecuredCopyToClipboard(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.error('Unable to copy to clipboard', err);
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
class memory_t {
|
||||
exports = null; // this is set after wasm module created
|
||||
constructor(wasm_memory) {
|
||||
@@ -108,6 +122,9 @@ const wasm_app_imports = {
|
||||
wasm_trap: () => { throw new Error(); },
|
||||
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
|
||||
|
||||
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
|
||||
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
|
||||
|
||||
// gfx
|
||||
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
|
||||
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
|
||||
@@ -137,8 +154,14 @@ const wasm_app_imports = {
|
||||
ctx2d.rect(x, y, w, h);
|
||||
ctx2d.clip();
|
||||
},
|
||||
wasm_open_link: (str, len) => {
|
||||
window.open(mem.read_cstr(str, len));
|
||||
wasm_set_cursor: (cursor_num) => {
|
||||
let cursor = 'auto';
|
||||
if (cursor_num == 0) {
|
||||
cursor = 'text';
|
||||
} else if (cursor_num == 1) {
|
||||
cursor = 'pointer';
|
||||
}
|
||||
document.getElementById("canvas").style.cursor = cursor;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -201,14 +224,20 @@ const wasm_app_imports = {
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
|
||||
|
||||
addEventListener("resize", (event) => { wake_up(); });
|
||||
addEventListener("keydown", (event) => {
|
||||
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
|
||||
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
let memory = wasm_exports["wasm_temp_alloc"](64);
|
||||
mem.write_string_into_cmemory(memory, 64, event.key);
|
||||
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("keyup", (event) => {
|
||||
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
let memory = wasm_exports["wasm_temp_alloc"](64);
|
||||
mem.write_string_into_cmemory(memory, 64, event.key);
|
||||
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mousemove", (event) => {
|
||||
@@ -216,7 +245,7 @@ const wasm_app_imports = {
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mousedown", (event) => {
|
||||
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mouseup", (event) => {
|
||||
|
||||
@@ -266,13 +266,14 @@ type_t type__app_event_t = { type_kind_struct, s8_const("app_event_t"), sizeof(a
|
||||
{.name = s8_const("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key), .dont_serialize = 0},
|
||||
{.name = s8_const("text"), .type = &type__s8_t, .offset = offsetof(app_event_t, text), .dont_serialize = 0},
|
||||
{.name = s8_const("mouse_wheel_delta"), .type = &type__v3f32_t, .offset = offsetof(app_event_t, mouse_wheel_delta), .dont_serialize = 0},
|
||||
{.name = s8_const("clicks"), .type = &type__i8, .offset = offsetof(app_event_t, clicks), .dont_serialize = 0},
|
||||
{.name = s8_const("ctrl"), .type = &type__b8, .offset = offsetof(app_event_t, ctrl), .dont_serialize = 0},
|
||||
{.name = s8_const("shift"), .type = &type__b8, .offset = offsetof(app_event_t, shift), .dont_serialize = 0},
|
||||
{.name = s8_const("alt"), .type = &type__b8, .offset = offsetof(app_event_t, alt), .dont_serialize = 0},
|
||||
{.name = s8_const("mouse_pos"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_pos), .dont_serialize = 0},
|
||||
{.name = s8_const("mouse_delta"), .type = &type__v2f32_t, .offset = offsetof(app_event_t, mouse_delta), .dont_serialize = 0},
|
||||
},
|
||||
.count = 12,
|
||||
.count = 13,
|
||||
};
|
||||
type_t type__app_frame_t = { type_kind_struct, s8_const("app_frame_t"), sizeof(app_frame_t),
|
||||
.members = (type_member_t[]){
|
||||
|
||||
@@ -104,6 +104,7 @@ struct app_event_t {
|
||||
app_key_t key;
|
||||
s8_t text;
|
||||
v3f32_t mouse_wheel_delta;
|
||||
i8 clicks;
|
||||
b8 ctrl;
|
||||
b8 shift;
|
||||
b8 alt;
|
||||
|
||||
@@ -170,6 +170,7 @@ void generate_app_code(ma_arena_t *arena) {
|
||||
v3f32_t mouse_wheel_delta; // @mouse_wheel
|
||||
|
||||
// always present data
|
||||
i8 clicks;
|
||||
b8 ctrl;
|
||||
b8 shift;
|
||||
b8 alt;
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
gb_wasm_export char wasm_temp_buff1[128] = {[127] = 0x13};
|
||||
gb_wasm_export i32 wasm_temp_buff1_len = 127;
|
||||
gb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13};
|
||||
gb_wasm_export i32 wasm_temp_buff2_len = 127;
|
||||
fn_wasm_import void wasm_open_link(isize str, i32 len);
|
||||
|
||||
gb f32 wasm_dpr;
|
||||
gb f32 wasm_delta_time;
|
||||
gb f32 wasm_time;
|
||||
@@ -55,7 +49,7 @@ fn_wasm_export void wasm_mouse_move(f32 x, f32 y, b32 ctrl, b32 shift, b32 alt,
|
||||
wasm_set_cached_buttons(ctrl, shift, alt);
|
||||
}
|
||||
|
||||
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta) {
|
||||
fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shift, b32 alt, b32 meta, i32 clicks) {
|
||||
button += 1;
|
||||
assert(button >= app_mouse_button_left && button <= app_mouse_button_right);
|
||||
wasm_set_cached_buttons(ctrl, shift, alt);
|
||||
@@ -63,6 +57,7 @@ fn_wasm_export void wasm_mouse_down(f32 x, f32 y, i32 button, b32 ctrl, b32 shif
|
||||
wasm_add_event((app_event_t){
|
||||
.kind = app_event_kind_mouse_down,
|
||||
.mouse_button = button,
|
||||
.clicks = clicks,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,8 +83,6 @@ fn_wasm_export void wasm_mouse_wheel(f32 x, f32 y, f32 delta_x, f32 delta_y, f32
|
||||
|
||||
fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
|
||||
wasm_set_cached_buttons(ctrl, shift, alt);
|
||||
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
|
||||
assert(wasm_temp_buff2[127] == 0x13);
|
||||
s8_t key8 = s8_from_char(key);
|
||||
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
|
||||
if (map.key != app_key_null) {
|
||||
@@ -112,8 +105,6 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m
|
||||
|
||||
fn_wasm_export void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 meta) {
|
||||
wasm_set_cached_buttons(ctrl, shift, alt);
|
||||
assert(wasm_temp_buff1[127] == 0x13); // make sure we didn't overwrite memory in JS
|
||||
assert(wasm_temp_buff2[127] == 0x13);
|
||||
s8_t key8 = s8_from_char(key);
|
||||
|
||||
wasm_key_map_t map = wasm_map_key_string_to_app_key(key8);
|
||||
@@ -159,7 +150,15 @@ fn_wasm_export void wasm_init(void) {
|
||||
os_core_init();
|
||||
}
|
||||
|
||||
fn_wasm_export isize wasm_temp_alloc(isize size) {
|
||||
return (isize)ma_push_size(&tcx->temp, size);
|
||||
}
|
||||
|
||||
fn_wasm_import void wasm_open_link(isize str, i32 len);
|
||||
fn_wasm_import void wasm_set_clipboard(isize str, i32 len);
|
||||
fn_wasm_import void wasm_set_cursor(i32 cursor_num);
|
||||
|
||||
// @todo: os?
|
||||
fn void open_link(s8_t url) {
|
||||
wasm_open_link((isize)url.str, (i32)url.len);
|
||||
}
|
||||
fn void open_link(s8_t url) { wasm_open_link((isize)url.str, (i32)url.len); }
|
||||
fn void set_clipboard(s8_t url) { wasm_set_clipboard((isize)url.str, (i32)url.len); }
|
||||
fn void set_cursor(i32 cursor_num) { wasm_set_cursor(cursor_num); }
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<title>Hello traveller!</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -60,6 +60,20 @@ class stream_t {
|
||||
}
|
||||
}
|
||||
|
||||
function unsecuredCopyToClipboard(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.error('Unable to copy to clipboard', err);
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
class memory_t {
|
||||
exports = null; // this is set after wasm module created
|
||||
constructor(wasm_memory) {
|
||||
@@ -108,6 +122,9 @@ const wasm_app_imports = {
|
||||
wasm_trap: () => { throw new Error(); },
|
||||
wasm_write_to_console: (str, len) => { console.log(mem.read_cstr(str, len)); },
|
||||
|
||||
wasm_open_link: (str, len) => { window.open(mem.read_cstr(str, len)); },
|
||||
wasm_set_clipboard: (str, len) => { unsecuredCopyToClipboard(mem.read_cstr(str, len)); },
|
||||
|
||||
// gfx
|
||||
wasm_draw_text: (str, len, x, y, font_str, font_len, font_size, r, g, b, a) => {
|
||||
ctx2d.font = `${font_size}px ${mem.read_cstr(font_str, font_len)}`;
|
||||
@@ -137,8 +154,14 @@ const wasm_app_imports = {
|
||||
ctx2d.rect(x, y, w, h);
|
||||
ctx2d.clip();
|
||||
},
|
||||
wasm_open_link: (str, len) => {
|
||||
window.open(mem.read_cstr(str, len));
|
||||
wasm_set_cursor: (cursor_num) => {
|
||||
let cursor = 'auto';
|
||||
if (cursor_num == 0) {
|
||||
cursor = 'text';
|
||||
} else if (cursor_num == 1) {
|
||||
cursor = 'pointer';
|
||||
}
|
||||
document.getElementById("canvas").style.cursor = cursor;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -201,14 +224,20 @@ const wasm_app_imports = {
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener("contextmenu", (event) => { event.preventDefault(); return false; })
|
||||
|
||||
addEventListener("resize", (event) => { wake_up(); });
|
||||
addEventListener("keydown", (event) => {
|
||||
if (["F1", "F2", "F3", "p"].includes(event.key)) event.preventDefault();
|
||||
wasm_exports["wasm_key_down"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
let memory = wasm_exports["wasm_temp_alloc"](8);
|
||||
mem.write_string_into_cmemory(memory, 8, event.key);
|
||||
wasm_exports["wasm_key_down"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("keyup", (event) => {
|
||||
wasm_exports["wasm_key_up"](mem.write_string_to_cglobal("wasm_temp_buff1", event.key), event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
let memory = wasm_exports["wasm_temp_alloc"](8);
|
||||
mem.write_string_into_cmemory(memory, 8, event.key);
|
||||
wasm_exports["wasm_key_up"](memory, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mousemove", (event) => {
|
||||
@@ -216,7 +245,7 @@ const wasm_app_imports = {
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mousedown", (event) => {
|
||||
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey);
|
||||
wasm_exports["wasm_mouse_down"](event.clientX, event.clientY, event.button, event.ctrlKey, event.shiftKey, event.altKey, event.metaKey, event.detail);
|
||||
wake_up();
|
||||
});
|
||||
addEventListener("mouseup", (event) => {
|
||||
|
||||
@@ -83,7 +83,6 @@ fn void *ma_push_size_ex(ma_arena_t *arena, usize size) {
|
||||
}
|
||||
}
|
||||
if (new_len > arena->commit) {
|
||||
debugf("expression failed(new_len > arena->commit) len:%llu base_len:%llu reserve:%llu commit:%llu, align: %llu", arena->len, arena->base_len, arena->reserve, arena->commit, arena->align);
|
||||
invalid_codepath;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -4479,8 +4479,11 @@ CL_PRIVATE_FUNCTION void CL_DefaultTokenize(CL_Lexer *T, CL_Token *token) {
|
||||
CL_Advance(T);
|
||||
}
|
||||
uint64_t len = T->stream - token->str;
|
||||
CL_ASSERT(len > 2);
|
||||
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
|
||||
if (len <= 2) {
|
||||
CL_ReportError(T, token, "Invalid hex number");
|
||||
} else {
|
||||
token->u64 = CL_ParseInteger(T, token, token->str + 2, len - 2, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,19 +15,17 @@
|
||||
// #include "win32_transcript_browser.c"
|
||||
#include "prototype.gen.c"
|
||||
|
||||
// @todo: how to pack the data so it's available on wasm?
|
||||
// @todo: cursor doesn't change when hovering on text
|
||||
// @todo: scrolling when clicked on bar bugs out and is slow
|
||||
// @todo: copy paste
|
||||
// @todo: selecting and writing is bugged
|
||||
// @todo: make it possible without executable
|
||||
// https://drive.proton.me/urls/F9X0GWGH38#t7MaKdLbvOfN
|
||||
// @todo @ui scrolling when focused button goes out of screen
|
||||
|
||||
fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
||||
tcx = thread_ctx;
|
||||
|
||||
if (frame->first_event->kind == app_event_kind_init) {
|
||||
run_all_tests();
|
||||
|
||||
|
||||
mt_tweak_f32(font_size, 30, 4, 200);
|
||||
mt_tweak_f32(_font_size, 30, 30, 30);
|
||||
|
||||
@@ -38,7 +36,7 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
||||
res_init(tcx->user_ctx);
|
||||
|
||||
res_t *res = tcx->user_ctx;
|
||||
res_report(res, "type in the path to folder with videos and press enter, for me it would be: D:/videos/jiang");
|
||||
res_report(res, "type something!");
|
||||
return true;
|
||||
} else if (frame->first_event->kind == app_event_kind_reload) {
|
||||
ui_reload();
|
||||
@@ -57,4 +55,4 @@ fn_export b32 app_update(thread_ctx_t *thread_ctx, app_frame_t *frame) {
|
||||
// ui_demo_update(frame, tweak_table, lengthof(tweak_table));
|
||||
transcript_browser_update(frame, tweak_table, lengthof(tweak_table));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,8 @@ void generate_transcript(s8_t folder) {
|
||||
i64 i1 = 0;
|
||||
for (i64 i0 = 0; i0 < it->string.len; i0 += 1) {
|
||||
if (it->string.str[i0] == '"' || it->string.str[i0] == '\'' ||
|
||||
it->string.str[i0] == ',' || it->string.str[i0] == '.') {
|
||||
it->string.str[i0] == ',' || it->string.str[i0] == '.' ||
|
||||
it->string.str[i0] == '\\') {
|
||||
continue;
|
||||
} else if (it->string.str[i0] == '-') {
|
||||
temp_string[i1++] = ' ';
|
||||
@@ -77,7 +78,7 @@ void generate_transcript(s8_t folder) {
|
||||
}
|
||||
|
||||
void generate_prototype_code(ma_arena_t *arena) {
|
||||
generate_transcript(s8("D:/videos/jiang"));
|
||||
generate_transcript(folder_to_create_transcript_for);
|
||||
sb8_t *include_paths = sb8(arena);
|
||||
sb8_append(include_paths, os_abs(arena, s8("../src")));
|
||||
mt_files_t files = mt_lex_files(arena, s8("../src/prototype/main.c"), include_paths);
|
||||
|
||||
@@ -31,7 +31,7 @@ fn void res_search_matches(res_t *res) {
|
||||
const i64 search_iter = 4096*16;
|
||||
for (i64 i = res->search_idx; i < res->search_idx + search_iter && i < the_text_itself.len; i += 1) {
|
||||
s8_t text = s8_skip(the_text_itself, i);
|
||||
if (s8_starts_with(text, res->search_text_input.string)) {
|
||||
if (s8_starts_with_ex(text, res->search_text_input.string, true)) {
|
||||
s8_t found = s8_make(text.str, res->search_text_input.len);
|
||||
array_add(&res->search_matches, found);
|
||||
}
|
||||
@@ -64,11 +64,11 @@ fn srt_entry_t res_find_entry(res_t *res, s8_t res_string) {
|
||||
fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i32 tweak_count) {
|
||||
ui_begin_frame(frame);
|
||||
rn_begin_frame(frame);
|
||||
|
||||
res_t *res = (res_t *)tcx->user_ctx;
|
||||
res_search_matches(res);
|
||||
|
||||
for (app_event_t *ev = frame->first_event; ev; ev = ev->next) {
|
||||
|
||||
ui_begin_build(UILOC, ev, window_rect_from_frame(frame));
|
||||
|
||||
locl b8 set_focus;
|
||||
@@ -130,24 +130,30 @@ fn void transcript_browser_update(app_frame_t *frame, mt_tweak_t *tweak_table, i
|
||||
s8_t middle = it;
|
||||
s8_t string = s8_printf(scratch.arena, "%S**%S**%S", left, middle, right);
|
||||
|
||||
ui_box_t *box = ui_box(.string = string, .flags = {.draw_rect = true, .draw_text = true, .keyboard_nav = true});
|
||||
ui_box_t *box = ui_box(.string = string, .flags = {.set_click_cursor = res->search_text_input.len != 0, .draw_rect = true, .draw_text = true, .keyboard_nav = true});
|
||||
ui_signal_t sig = ui_signal_from_box(box);
|
||||
if (sig.clicked && matches.data != res->error_messages.data) {
|
||||
srt_entry_t entry = res_find_entry(res, middle);
|
||||
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
|
||||
debugf("time = %u %u %u seconds = %d", entry.hour, entry.minute, entry.second, seconds);
|
||||
s8_t id = s8_get_postfix(entry.filepath, 13);
|
||||
debugf("id = %S", id);
|
||||
if (id.str[0] != '[') {
|
||||
fatalf("internal error: dear user, it appears that youtube id link is not composed of exactly 11 letters, quite a pickle, if you could report that to me would be very nice");
|
||||
}
|
||||
id = s8_skip(id, 1);
|
||||
id = s8_chop(id, 1);
|
||||
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d", id, seconds);
|
||||
debugf("url = %S", url);
|
||||
open_link(url);
|
||||
}
|
||||
|
||||
b32 right_clicked = (ui->hot.value == box->id.value && ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_right);
|
||||
b32 copied = (ui->focus.value == box->id.value && ev->kind == app_event_kind_key_down && ev->key == app_key_c && ev->ctrl);
|
||||
if (right_clicked || copied) {
|
||||
srt_entry_t entry = res_find_entry(res, middle);
|
||||
int seconds = entry.hour * 60 * 60 + entry.minute * 60 + entry.second;
|
||||
s8_t id = s8_get_postfix(entry.filepath, 13);
|
||||
id = s8_skip(id, 1);
|
||||
id = s8_chop(id, 1);
|
||||
s8_t url = s8_printf(scratch.arena, "https://youtu.be/%S?feature=shared&t=%d %S%S%S", id, seconds, left, middle, right);
|
||||
set_clipboard(url);
|
||||
}
|
||||
|
||||
if (set_focus) {
|
||||
ui->focus = box->id;
|
||||
set_focus = false;
|
||||
|
||||
31
src/ui/ui.c
31
src/ui/ui.c
@@ -402,6 +402,12 @@ fn r2f32_t ui_get_appear_rect(ui_box_t *box) {
|
||||
fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
|
||||
ui_draw_compute_t co = {0};
|
||||
|
||||
if (ui->set_cursor_for_frame == false && box->flags.set_click_cursor &&
|
||||
(ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
|
||||
set_cursor(1);
|
||||
ui->set_cursor_for_frame = true;
|
||||
}
|
||||
|
||||
co.rect = ui_get_appear_rect(box);
|
||||
co.background_color = box->background_color;
|
||||
co.text_color = box->text_color;
|
||||
@@ -431,6 +437,10 @@ fn ui_draw_compute_t ui_draw_compute(ui_box_t *box) {
|
||||
}
|
||||
|
||||
fn void ui_text_input_draw(ui_box_t *box) {
|
||||
if (ui->set_cursor_for_frame == false && (ui->hot.value == box->id.value || ui->active.value == box->id.value)) {
|
||||
set_cursor(0);
|
||||
ui->set_cursor_for_frame = true;
|
||||
}
|
||||
ui_draw_compute_t co = ui_draw_compute(box);
|
||||
|
||||
rn_draw_rect(co.rect, co.background_color);
|
||||
@@ -535,11 +545,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
|
||||
if (ev->kind == app_event_kind_text) {
|
||||
signal.text_changed = true;
|
||||
ui_text_replace(ti, ti->caret.range, ev->text);
|
||||
if (sel_size) {
|
||||
ti->caret = ui_carets(ti->caret.range.min);
|
||||
} else {
|
||||
ti->caret = ui_carets(ti->caret.range.min + 1);
|
||||
}
|
||||
ti->caret = ui_carets(ti->caret.range.min + 1);
|
||||
} else if (ev->kind == app_event_kind_key_down) {
|
||||
if (ev->key == app_key_backspace) {
|
||||
signal.text_changed = true;
|
||||
@@ -564,6 +570,7 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
|
||||
} else {
|
||||
ui_text_replace(ti, r1i32(ti->caret.e[0], ti->caret.e[0] + 1), s8_null);
|
||||
}
|
||||
|
||||
} else if (ev->key == app_key_left) {
|
||||
signal.text_changed = true;
|
||||
if (ev->shift) {
|
||||
@@ -580,13 +587,20 @@ fn ui_signal_t ui__text_input(ui_code_loc_t loc, ui_text_input_t *ti, ui_box_fla
|
||||
}
|
||||
} else if (ev->key == app_key_enter) {
|
||||
signal.text_commit = true;
|
||||
} else if (ev->key == app_key_a && ev->ctrl) {
|
||||
ti->caret = ui_caret(0, ti->len);
|
||||
}
|
||||
|
||||
}
|
||||
v2f32_t size = rn_measure_string(rn->main_font, s8("_"));
|
||||
v2f32_t pos = v2f32_sub(ev->mouse_pos, box->final_rect.min);
|
||||
i32 p = (i32)f32_round(pos.x / size.x);
|
||||
if (ev->kind == app_event_kind_mouse_down && ev->mouse_button == app_mouse_button_left) {
|
||||
ti->caret = ui_carets(p);
|
||||
if (ev->clicks == 2) {
|
||||
// ti->caret = ui_caret(0, ti->len);
|
||||
} else {
|
||||
ti->caret = ui_carets(p);
|
||||
}
|
||||
}
|
||||
if (signal.dragging) {
|
||||
ti->caret = ui_caret_set_front(ti->caret, p);
|
||||
@@ -1203,6 +1217,7 @@ fn void ui_draw(void) {
|
||||
fn void ui_begin_frame(app_frame_t *frame) {
|
||||
ui = tcx->ui_ctx;
|
||||
ui->frame = frame;
|
||||
ui->set_cursor_for_frame = false;
|
||||
}
|
||||
|
||||
fn void ui_end_frame(void) {
|
||||
@@ -1230,6 +1245,10 @@ fn void ui_end_frame(void) {
|
||||
ui->active = ui_null_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->set_cursor_for_frame == false) {
|
||||
set_cursor(32323);
|
||||
}
|
||||
}
|
||||
|
||||
fn void ui_reload(void) {
|
||||
|
||||
@@ -18,6 +18,7 @@ struct ui_box_flags_t {
|
||||
b8 draw_border: 1;
|
||||
b8 draw_text: 1;
|
||||
b8 clip_rect: 1;
|
||||
b8 set_click_cursor : 1;
|
||||
|
||||
b8 animate_appear: 1;
|
||||
|
||||
@@ -154,6 +155,8 @@ struct ui_t {
|
||||
|
||||
// drawing
|
||||
r2f32_t clip_rect;
|
||||
|
||||
b32 set_cursor_for_frame;
|
||||
};
|
||||
|
||||
gb ui_t *ui;
|
||||
|
||||
61
todo.txt
61
todo.txt
@@ -1,61 +0,0 @@
|
||||
[ ] app
|
||||
[ ] event playback
|
||||
[ ] win32
|
||||
[ ] hot reload / plugins
|
||||
[ ] tests using yield
|
||||
[ ] touchpad gestures: https://learn.microsoft.com/en-us/windows/win32/wintouch/windows-touch-gestures-overview
|
||||
[ ] linux
|
||||
[ ] wasm
|
||||
[ ] drag and drop
|
||||
[ ] open file dialog
|
||||
[ ] os
|
||||
[?] wasm (explore)
|
||||
[ ] linux
|
||||
|
||||
[ ] render
|
||||
[ ] wasm (maybe try first without opengl and then opengl)
|
||||
[ ] opengl
|
||||
[ ] maybe copy package stuff to build?
|
||||
|
||||
[ ] ui
|
||||
[ ] sleep when not animating
|
||||
[ ] concept of active panel
|
||||
[ ] separate root so can't focus off with keyboard
|
||||
[ ] clicking away from panel, closes it or changes focus
|
||||
[ ] hot to move between panels?
|
||||
[ ] table
|
||||
[ ] scrolling with keyboard
|
||||
[ ] context menu
|
||||
[ ] maybe a separate root for context menu?
|
||||
[ ] hover tooltips
|
||||
[ ] separate root which gets drawn on top of all ui
|
||||
[ ] upper left menu dynamics
|
||||
[ ] text input
|
||||
[ ] ctrl backspace, ctrl delete
|
||||
[ ] set mouse cursor on hover
|
||||
[ ] maybe copy over text editor code from other project?
|
||||
[ ] everything lister (with edits etc.)
|
||||
[ ] pernament storage?
|
||||
[ ] push pop flag
|
||||
[ ] how to do colors?
|
||||
- css like based on ids and pre built palettes
|
||||
- fat box with stacks (now)
|
||||
[ ] demo style, with different buttons and controls, headings
|
||||
[ ] color picker
|
||||
[ ] slider
|
||||
[ ] draw image in box ui
|
||||
[ ] push flags?
|
||||
|
||||
[ ] core
|
||||
[ ] ast
|
||||
[ ] move to core layer at some point as the abstract format for different types of serialization
|
||||
[ ] use bit fields instead of ast_flag
|
||||
[ ] mt_tag syntax // f32 value; mt_tag(min = 0, min = 10) // mt_tag(something)
|
||||
[ ] remove most flags
|
||||
[ ] explore metadata flags for common formats aka (key_value, list etc.. will
|
||||
[ ] json format
|
||||
[ ] ini format
|
||||
[ ] build system
|
||||
[ ] use threads
|
||||
[ ] meta
|
||||
[ ] s8_bin
|
||||
Reference in New Issue
Block a user