getting closer to full port of web app starter
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
Output / Invocation Functions
|
||||
|
||||
1_RENDER
|
||||
call_file
|
||||
unit_call
|
||||
component
|
||||
component_exists
|
||||
component_resolve
|
||||
@@ -11,5 +11,6 @@ ob_get
|
||||
ob_get_close
|
||||
ob_start
|
||||
print
|
||||
render_component
|
||||
render_file
|
||||
component_render
|
||||
unit_load
|
||||
unit_render
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Runtime
|
||||
unit_info
|
||||
units_list
|
||||
unit_compile
|
||||
@@ -1,5 +1,5 @@
|
||||
Sessions
|
||||
|
||||
make_session_id
|
||||
session_id_create
|
||||
session_destroy
|
||||
session_start
|
||||
|
||||
@@ -8,11 +8,11 @@ file_exists
|
||||
file_get_contents
|
||||
file_mtime
|
||||
file_put_contents
|
||||
get_cwd
|
||||
cwd_get
|
||||
ls
|
||||
mkdir
|
||||
path_join
|
||||
set_cwd
|
||||
cwd_set
|
||||
shell_escape
|
||||
shell_exec
|
||||
unlink
|
||||
file_unlink
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Time and Date Functions
|
||||
|
||||
microtime
|
||||
time_precise
|
||||
time
|
||||
date
|
||||
gmdate
|
||||
parse_time
|
||||
time_format_local
|
||||
time_format_utc
|
||||
time_parse
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
URI Functions
|
||||
|
||||
encode_query
|
||||
make_session_id
|
||||
session_id_create
|
||||
parse_query
|
||||
uri_decode
|
||||
uri_encode
|
||||
|
||||
+85
-24
@@ -7,15 +7,15 @@ void render_see_section(String name)
|
||||
{
|
||||
if(idx == 0)
|
||||
{
|
||||
<><h3><?= line ?></h3><ul></>
|
||||
<><div class="category"><h3><?= line ?></h3><ul></>
|
||||
}
|
||||
else if(line != "")
|
||||
{
|
||||
<><li><a href="index.uce?p=<?= uri_encode(line) ?>"><?= line ?><span style="opacity:0.5">()</span></a></li></>
|
||||
<><li><a href="index.uce?p=<?= uri_encode(line) ?>"><?= line ?><span class="dim">()</span></a></li></>
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
<></ul></>
|
||||
<></ul></div></>
|
||||
}
|
||||
|
||||
|
||||
@@ -26,20 +26,39 @@ RENDER(Request& context)
|
||||
|
||||
<><html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"></meta>
|
||||
<link rel="stylesheet" href='style.css?v=<?= time() ?>'></link>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Docs</a>:
|
||||
<?= page ?>
|
||||
<a href="index.uce">UCE Docs</a><?
|
||||
if(page != "index")
|
||||
{
|
||||
?><span class="dim"> / </span><?= page ?><?
|
||||
}
|
||||
?>
|
||||
</h1>
|
||||
<?
|
||||
|
||||
if(page == "index")
|
||||
{
|
||||
?><div style="display:flex;"><?
|
||||
?><div style="flex:1"><?
|
||||
?><h3>All API Functions</h3><?
|
||||
?><div class="search-bar">
|
||||
<input id="doc-search" type="search" placeholder="Search API…" autocomplete="off" spellcheck="false"/>
|
||||
<span class="search-count" id="search-count"></span>
|
||||
</div>
|
||||
<div id="search-results" hidden></div>
|
||||
<div class="index-pane" id="index-pane">
|
||||
<div class="index-layout">
|
||||
<nav class="sidebar"><?
|
||||
for(auto file_name : ls("areas/"))
|
||||
{
|
||||
String ft = nibble(file_name, ".");
|
||||
render_see_section(ft);
|
||||
}
|
||||
?></nav>
|
||||
<main class="content">
|
||||
<h2>All API Functions</h2>
|
||||
<div class="func-grid"><?
|
||||
for(auto file_name : ls("pages/"))
|
||||
{
|
||||
String ft = nibble(file_name, ".");
|
||||
@@ -48,7 +67,7 @@ RENDER(Request& context)
|
||||
String fn = ft;
|
||||
String pre = nibble(fn, "_");
|
||||
?>
|
||||
<div><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?></a><span style="opacity:0.5"> : struct</span></div>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?><span class="badge">struct</span></a></div>
|
||||
<?
|
||||
}
|
||||
else if(ft.substr(0, 2) == "1_")
|
||||
@@ -56,28 +75,65 @@ RENDER(Request& context)
|
||||
String fn = ft;
|
||||
String pre = nibble(fn, "_");
|
||||
?>
|
||||
<div><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?></a><span style="opacity:0.5"> : directive</span></div>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= fn ?><span class="badge">directive</span></a></div>
|
||||
<?
|
||||
}
|
||||
else
|
||||
{
|
||||
?>
|
||||
<div><a href="?p=<?= uri_encode(ft) ?>"><?= ft ?><span style="opacity:0.5">()</span></a></div>
|
||||
<div class="func-item"><a href="?p=<?= uri_encode(ft) ?>"><?= ft ?><span class="dim">()</span></a></div>
|
||||
<?
|
||||
}
|
||||
}
|
||||
?></div><?
|
||||
?><div style="flex:1"><?
|
||||
for(auto file_name : ls("areas/"))
|
||||
{
|
||||
String ft = nibble(file_name, ".");
|
||||
render_see_section(ft);
|
||||
}
|
||||
?></div><?
|
||||
?></div><?
|
||||
?></div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<?
|
||||
<><script>
|
||||
(function () {
|
||||
var input = document.getElementById('doc-search');
|
||||
var results = document.getElementById('search-results');
|
||||
var pane = document.getElementById('index-pane');
|
||||
var counter = document.getElementById('search-count');
|
||||
var timer;
|
||||
|
||||
function showResults(html) {
|
||||
results.innerHTML = html;
|
||||
results.hidden = false;
|
||||
pane.hidden = true;
|
||||
var hits = results.querySelectorAll('.search-hit').length;
|
||||
counter.textContent = hits ? hits + ' result' + (hits > 1 ? 's' : '') : '';
|
||||
}
|
||||
|
||||
function showIndex() {
|
||||
results.innerHTML = '';
|
||||
results.hidden = true;
|
||||
pane.hidden = false;
|
||||
counter.textContent = '';
|
||||
}
|
||||
|
||||
input.addEventListener('input', function () {
|
||||
var q = this.value.trim();
|
||||
clearTimeout(timer);
|
||||
if (q.length < 2) { showIndex(); return; }
|
||||
counter.textContent = '\u2026';
|
||||
timer = setTimeout(function () {
|
||||
fetch('search.uce?q=' + encodeURIComponent(q))
|
||||
.then(function (r) { return r.text(); })
|
||||
.then(showResults);
|
||||
}, 220);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape') { this.value = ''; showIndex(); }
|
||||
});
|
||||
})();
|
||||
</script></>
|
||||
}
|
||||
else
|
||||
{
|
||||
?><article class="doc-detail"><?
|
||||
auto doc = split(file_get_contents("pages/"+page+".txt"), "\n");
|
||||
String layout_class = "text";
|
||||
u32 line_idx = 0;
|
||||
@@ -95,7 +151,7 @@ RENDER(Request& context)
|
||||
{
|
||||
?></div><?
|
||||
}
|
||||
?><div class="<?= layout_class ?>"><?
|
||||
?><div class="doc-section <?= layout_class ?>"><?
|
||||
if(layout_class == "params")
|
||||
{
|
||||
?><h3>Parameters</h3><?
|
||||
@@ -114,7 +170,7 @@ RENDER(Request& context)
|
||||
}
|
||||
else if(layout_class == "see")
|
||||
{
|
||||
/*?><h3>Related</h3><?*/
|
||||
?><h3>Related</h3><?
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,15 +196,20 @@ RENDER(Request& context)
|
||||
}
|
||||
else
|
||||
{
|
||||
?><div><a href="index.uce?p=<?= trim(s) ?>"><?= trim(s) ?><span style="opacity:0.5">()</span></a></div><?
|
||||
?><div><a href="index.uce?p=<?= trim(s) ?>"><?= trim(s) ?><span class="dim">()</span></a></div><?
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
?><div><? print(s); ?></div><?
|
||||
?><div><?: markdown_to_html(s) ?></div><?
|
||||
}
|
||||
}
|
||||
}
|
||||
if(line_idx > 0)
|
||||
{
|
||||
?></div><?
|
||||
}
|
||||
?></article><?
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -64,9 +64,8 @@ Whether the request should be logged
|
||||
f64 stats.time_start
|
||||
f64 stats.time_end
|
||||
|
||||
:render_file(String file_name, [Request& context])
|
||||
:unit_render(String file_name, [Request& context])
|
||||
Invokes another UCE file using the current or supplied request context
|
||||
|
||||
:see
|
||||
>types
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ COMPONENT(Request& context)
|
||||
:desc
|
||||
Defines the default component entrypoint for the current `.uce` file.
|
||||
|
||||
`component()` and `render_component()` invoke `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
|
||||
`component()` and `component_render()` invoke `COMPONENT(Request& context)` by default. Named component entrypoints use `COMPONENT:NAME(Request& context)`.
|
||||
|
||||
This keeps page rendering and component rendering separate:
|
||||
|
||||
@@ -16,7 +16,7 @@ A file intended only for component reuse can define `COMPONENT()` without defini
|
||||
|
||||
Inside component handlers, props arrive through `context.call`.
|
||||
|
||||
When you call `component(":NAME", props, context)` or `render_component(":NAME", props, context)`, the runtime resolves `:NAME` against the current `.uce` file instead of requiring the file name again.
|
||||
When you call `component(":NAME", props, context)` or `component_render(":NAME", props, context)`, the runtime resolves `:NAME` against the current `.uce` file instead of requiring the file name again.
|
||||
|
||||
Examples:
|
||||
`COMPONENT(Request& context)`
|
||||
@@ -31,6 +31,6 @@ Examples:
|
||||
|
||||
:see
|
||||
>component
|
||||
>render_component
|
||||
>component_render
|
||||
>1_RENDER
|
||||
>1_WS
|
||||
|
||||
@@ -14,7 +14,7 @@ The request environment is passed explicitly through `context`, including params
|
||||
|
||||
For a normal direct page request, `context.call` starts empty.
|
||||
|
||||
If the page is invoked from another UCE file via `render_file(file_name, context)`, the callee receives that same `context`.
|
||||
If the page is invoked from another UCE file via `unit_render(file_name, context)`, the callee receives that same `context`.
|
||||
|
||||
Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when they intentionally need both page and component behavior in one unit. In that case `RENDER(Request& context)` serves the direct HTTP response, `WS(Request& context)` handles subsequent WebSocket messages, and `COMPONENT()` remains available only through the component helpers.
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ The loaded file is resolved relative to the current source file unless the path
|
||||
- `#load` is recognized only when the current line starts with `#load ` at column 1.
|
||||
- `EXPORT` harvesting likewise only triggers when the current line starts with `EXPORT` at column 1 and is followed by whitespace.
|
||||
- Relative `#load` paths are expanded against the including unit's source directory.
|
||||
- `render_file()` and `call_file()` are runtime APIs; `#load` is a compile-time include/composition feature.
|
||||
- `unit_render()` and `unit_call()` are runtime APIs; `#load` is a compile-time include/composition feature.
|
||||
|
||||
:Limitations
|
||||
- This pass is character-wise, not a full parser.
|
||||
@@ -92,7 +92,7 @@ The loaded file is resolved relative to the current source file unless the path
|
||||
|
||||
:see
|
||||
load
|
||||
render_file
|
||||
call_file
|
||||
unit_render
|
||||
unit_call
|
||||
0_context
|
||||
1_COMPONENT
|
||||
|
||||
@@ -12,7 +12,7 @@ Common uses include:
|
||||
`json_decode()` / `json_encode()`
|
||||
`context.var`
|
||||
`context.call`
|
||||
`call_file()` return values
|
||||
`unit_call()` return values
|
||||
|
||||
Useful methods include:
|
||||
`to_string()`
|
||||
|
||||
@@ -8,7 +8,7 @@ Renders another `.uce` file as a component and returns the captured output as a
|
||||
|
||||
Component props are passed in `context.call`.
|
||||
|
||||
Because `<?= ... ?>` HTML-escapes its value, embed component markup with `<?: component(...) ?>`, `print(component(...))`, or use `render_component(...)` for direct output.
|
||||
Because `<?= ... ?>` HTML-escapes its value, embed component markup with `<?: component(...) ?>`, `print(component(...))`, or use `component_render(...)` for direct output.
|
||||
|
||||
When `name` contains a colon, such as `components/card:BODY`, the part after the colon selects a named component handler exported from the component file through `COMPONENT:BODY(Request& context)`.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
void render_component(String name, [DTree props], [Request& context])
|
||||
void component_render(String name, [DTree props], [Request& context])
|
||||
|
||||
:desc
|
||||
Renders another `.uce` file as a component and writes the result directly to the current output buffer.
|
||||
@@ -10,12 +10,12 @@ Component props are passed through `context.call`, and `name:COMPONENTFUNC` may
|
||||
|
||||
When `name` starts with `:`, the runtime resolves that named handler against the current `.uce` file.
|
||||
|
||||
Use `render_component()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
|
||||
Use `component_render()` when you want to write component output directly from C++ code instead of capturing it as a `String`.
|
||||
|
||||
Example:
|
||||
`DTree props;`
|
||||
`props["body"] = "Hello";`
|
||||
`render_component("components/card:BODY", props, context);`
|
||||
`component_render("components/card:BODY", props, context);`
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String get_cwd()
|
||||
String cwd_get()
|
||||
|
||||
:params
|
||||
return value : the current working directory
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
void set_cwd(String path)
|
||||
void cwd_set(String path)
|
||||
|
||||
:params
|
||||
path : the new working directory
|
||||
@@ -0,0 +1,11 @@
|
||||
:sig
|
||||
void file_unlink(String file_name)
|
||||
|
||||
:params
|
||||
file_name : name of the file
|
||||
|
||||
:desc
|
||||
Deletes the file identified by `file_name`.
|
||||
|
||||
:see
|
||||
>sys
|
||||
@@ -72,6 +72,6 @@ The parser preserves directive data needed by those hooks.
|
||||
:see
|
||||
markdown_to_html
|
||||
component
|
||||
render_component
|
||||
component_render
|
||||
json_encode
|
||||
DTree
|
||||
|
||||
@@ -95,6 +95,6 @@ This makes directive components a good fit for alerts, callouts, cards, embeds,
|
||||
:see
|
||||
markdown_to_ast
|
||||
component
|
||||
render_component
|
||||
component_render
|
||||
json_decode
|
||||
String
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
:sig
|
||||
String make_session_id()
|
||||
String session_id_create()
|
||||
|
||||
:params
|
||||
return value : a new session ID
|
||||
|
||||
:desc
|
||||
Creates a session ID
|
||||
Creates a session ID.
|
||||
|
||||
:see
|
||||
>session
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
s64 kill(pid_t pid, s64 sig)
|
||||
int task_kill(pid_t pid, int sig = 0)
|
||||
|
||||
:params
|
||||
pid : PID of the process
|
||||
@@ -7,7 +7,7 @@ sig : signal number
|
||||
return value : 0 if signal was sent, -1 otherwise
|
||||
|
||||
:desc
|
||||
This is the standard POSIX kill() function, provided here for reference.
|
||||
Wraps the standard POSIX `kill()` function.
|
||||
|
||||
Possible signal numbers are: SIGABND, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE, SIGPOLL, SIGPROF, SIGQUIT, SIGSEGV, SIGSYS, SIGTERM, SIGTRAP, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGXCPU, SIGXFSZ, SIGCHLD, SIGIO, SIGIOERR, SIGWINCH, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGCONT.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String date(String format = "", u64 timestamp = 0)
|
||||
String time_format_local(String format = "", u64 timestamp = 0)
|
||||
|
||||
:params
|
||||
format : formatting string specifying the date format
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
String gmdate(String format = "", u64 timestamp = 0)
|
||||
String time_format_utc(String format = "", u64 timestamp = 0)
|
||||
|
||||
:params
|
||||
format : formatting string specifying the date format
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
u64 parse_time(String time_string)
|
||||
u64 time_parse(String time_string)
|
||||
|
||||
:params
|
||||
time_string : a string containing a date and/or time in text form
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
f64 microtime()
|
||||
f64 time_precise()
|
||||
|
||||
:params
|
||||
return value : current Unix timestamp
|
||||
@@ -1,5 +1,5 @@
|
||||
:sig
|
||||
DTree* call_file(String file_name, String function_name, DTree* call_param = null)
|
||||
DTree* unit_call(String file_name, String function_name, DTree* call_param = null)
|
||||
|
||||
:params
|
||||
file_name : UCE file to load and execute
|
||||
@@ -8,16 +8,17 @@ call_param : optional, call parameter
|
||||
return value : DTree* returned from function
|
||||
|
||||
:desc
|
||||
Calls a function inside a UCE file.
|
||||
Calls an exported function inside a UCE file.
|
||||
|
||||
:Example
|
||||
// export a function
|
||||
EXPORT void test_func()
|
||||
EXPORT DTree* test_func(DTree* call_param)
|
||||
{
|
||||
print("HELLO FROM TEST FUNCTION");
|
||||
return(0);
|
||||
}
|
||||
// use that function in another file
|
||||
call_file("call_file_funcs.uce", "test_func");
|
||||
unit_call("call_file_funcs.uce", "test_func");
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -0,0 +1,15 @@
|
||||
:sig
|
||||
bool unit_compile(String path = "")
|
||||
|
||||
:params
|
||||
path : optional UCE unit path. If empty, recompiles the current executing unit.
|
||||
return value : `true` when the unit was compiled and loaded successfully
|
||||
|
||||
:desc
|
||||
Triggers a manual recompile of a UCE compilation unit.
|
||||
|
||||
If `path` is relative, it is resolved relative to the current executing unit. Successful manual compiles also refresh the in-memory metadata for that unit.
|
||||
|
||||
:see
|
||||
unit_info
|
||||
units_list
|
||||
@@ -0,0 +1,27 @@
|
||||
:sig
|
||||
DTree unit_info(String path = "")
|
||||
|
||||
:params
|
||||
path : optional UCE unit path. If empty, uses the current executing unit.
|
||||
return value : metadata tree for the resolved unit, or an empty tree if the unit cannot be resolved
|
||||
|
||||
:desc
|
||||
Returns runtime metadata for a UCE compilation unit.
|
||||
|
||||
The returned tree includes:
|
||||
|
||||
- normalized paths and generated artifact paths
|
||||
- compile status, compile error status, runtime error status, and a combined `error_status`
|
||||
- request and invocation counters
|
||||
- best, worst, last, and average render time
|
||||
- compile counters plus best, worst, last, and average compile time
|
||||
- file mtimes, stale status, load status, and exported API declarations
|
||||
|
||||
If `path` is relative, it is resolved relative to the current executing unit.
|
||||
|
||||
Because unit metadata lives in process memory alongside `SharedUnit`, request and timing counters reflect the current runtime process, not an aggregate across every worker process.
|
||||
|
||||
:see
|
||||
units_list
|
||||
unit_compile
|
||||
0_context
|
||||
@@ -0,0 +1,16 @@
|
||||
:sig
|
||||
SharedUnit* unit_load(String file_name)
|
||||
|
||||
:params
|
||||
file_name : UCE file to load
|
||||
return value : loaded shared unit, or `null` if the unit could not be loaded
|
||||
|
||||
:desc
|
||||
Loads a UCE compilation unit and returns its in-memory `SharedUnit` record.
|
||||
|
||||
This is a low-level runtime helper. Most application code should prefer `unit_render()`, `unit_call()`, `component()`, or `component_render()`.
|
||||
|
||||
:see
|
||||
unit_render
|
||||
unit_call
|
||||
load
|
||||
@@ -1,6 +1,6 @@
|
||||
:sig
|
||||
void render_file(String file_name)
|
||||
void render_file(String file_name, Request& context)
|
||||
void unit_render(String file_name)
|
||||
void unit_render(String file_name, Request& context)
|
||||
|
||||
:params
|
||||
file_name : UCE file to load and execute
|
||||
@@ -13,10 +13,10 @@ If `context` is omitted, the current active request context is used.
|
||||
|
||||
:Example
|
||||
// call a common page template
|
||||
render_file("page-template.uce");
|
||||
unit_render("page-template.uce");
|
||||
|
||||
// explicitly pass a request context
|
||||
render_file("page-template.uce", context);
|
||||
unit_render("page-template.uce", context);
|
||||
|
||||
:see
|
||||
>ob
|
||||
@@ -0,0 +1,11 @@
|
||||
:sig
|
||||
std::vector<String> units_list()
|
||||
|
||||
:desc
|
||||
Returns the normalized paths of all known `.uce` units.
|
||||
|
||||
This includes the shared known-unit registry plus any units already loaded in the current runtime process.
|
||||
|
||||
:see
|
||||
unit_info
|
||||
unit_compile
|
||||
@@ -1,11 +0,0 @@
|
||||
:sig
|
||||
void unlink(String file_name)
|
||||
|
||||
:params
|
||||
file_name : name of the file
|
||||
|
||||
:desc
|
||||
Deletes the file identified by 'file_name'.
|
||||
|
||||
:see
|
||||
>sys
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
// Returns whether haystack contains needle (case-sensitive, both should be pre-lowercased)
|
||||
bool str_contains(String haystack, String needle)
|
||||
{
|
||||
return replace(haystack, needle, "\x01") != haystack;
|
||||
}
|
||||
|
||||
RENDER(Request& context)
|
||||
{
|
||||
String q = to_lower(trim(context.get["q"]));
|
||||
|
||||
if(q.length() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
|
||||
for(auto file_name : ls("pages/"))
|
||||
{
|
||||
String ft = nibble(file_name, ".");
|
||||
String name = ft;
|
||||
String badge = "";
|
||||
|
||||
if(ft.substr(0, 2) == "0_")
|
||||
{
|
||||
nibble(name, "_");
|
||||
badge = "struct";
|
||||
}
|
||||
else if(ft.substr(0, 2) == "1_")
|
||||
{
|
||||
nibble(name, "_");
|
||||
badge = "directive";
|
||||
}
|
||||
|
||||
String content = file_get_contents("pages/" + ft + ".txt");
|
||||
|
||||
if(str_contains(to_lower(name), q) || str_contains(to_lower(content), q))
|
||||
{
|
||||
// Find first content line that contains the query for a snippet
|
||||
String snippet = "";
|
||||
for(auto line : split(content, "\n"))
|
||||
{
|
||||
String t = trim(line);
|
||||
if(t.length() > 4 && t.substr(0, 1) != ":" && str_contains(to_lower(t), q))
|
||||
{
|
||||
snippet = t;
|
||||
if(snippet.length() > 100)
|
||||
snippet = snippet.substr(0, 100) + "…";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
<><div class="func-item search-hit">
|
||||
<a href="index.uce?p=<?= uri_encode(ft) ?>"><?= html_escape(name) ?><? if(badge != "") { ?><span class="badge"><?= badge ?></span><? } ?></a>
|
||||
<? if(snippet != "") { ?><div class="search-snippet"><?= html_escape(snippet) ?></div><? } ?>
|
||||
</div></>
|
||||
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(count == 0)
|
||||
{
|
||||
<><p class="no-results">No results for "<strong><?= html_escape(q) ?></strong>"</p></>
|
||||
}
|
||||
}
|
||||
+449
-74
@@ -1,105 +1,480 @@
|
||||
/* UCE Documentation — Theme
|
||||
Color palette: #139 deep-blue base · white text · yellow accents */
|
||||
|
||||
:root {
|
||||
--bg: #113399;
|
||||
--bg-surface: rgba(255, 255, 255, 0.065);
|
||||
--bg-surface-hover: rgba(255, 255, 255, 0.11);
|
||||
--bg-inset: rgba(0, 0, 0, 0.22);
|
||||
--bg-code: rgba(0, 0, 0, 0.28);
|
||||
--text: #e8ecf4;
|
||||
--text-dim: rgba(255, 255, 255, 0.55);
|
||||
--text-muted: rgba(255, 255, 255, 0.35);
|
||||
--accent: #ffd644;
|
||||
--accent-dim: rgba(255, 214, 68, 0.1);
|
||||
--accent-hover: #ffe680;
|
||||
--border: rgba(255, 255, 255, 0.08);
|
||||
--border-strong: rgba(255, 255, 255, 0.18);
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
--font-mono: 'SF Mono', 'Cascadia Code', 'Fira Code', Consolas, monospace;
|
||||
--radius: 8px;
|
||||
--radius-lg: 14px;
|
||||
--ease: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* ── Reset ── */
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
box-sizing: inherit;
|
||||
color: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: monospace;
|
||||
font-size: 200%;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
/* ── Base ── */
|
||||
|
||||
html {
|
||||
font-size: 15px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 1024px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
font-family: Tahoma, Helvetica, Arial;
|
||||
font-size: 1.2em;
|
||||
box-sizing: border-box;
|
||||
background: #139;
|
||||
color: white;
|
||||
line-height: 150%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 64px;
|
||||
font-family: var(--font-sans);
|
||||
font-size: 1rem;
|
||||
line-height: 1.65;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
body > * {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 32px;
|
||||
margin: 16px;
|
||||
/* ── Typography ── */
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
padding: 28px 0 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: yellow;
|
||||
h1 a {
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
form > div, label {
|
||||
display: block;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
h1 a:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
background: rgba(0,0,0,0.2);
|
||||
border: 2px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
input[type=submit], button {
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=submit]:hover, button:hover {
|
||||
color: yellow;
|
||||
background: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
input[type=text], textarea {
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text]:hover, textarea:hover {
|
||||
background: rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 20%;
|
||||
}
|
||||
|
||||
pre {
|
||||
height: 20%;
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background: rgba(100,100,100,0.15);
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
h2 {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
margin-bottom: 16px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
font-size: 1.4em;
|
||||
margin-bottom: 0.8em;
|
||||
opacity: 0.7;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--text-dim);
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--accent-hover);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ── Index Layout ── */
|
||||
|
||||
.index-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 280px 1fr;
|
||||
gap: 32px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.index-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.category {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 10px;
|
||||
transition: border-color 150ms var(--ease);
|
||||
}
|
||||
|
||||
.category:hover {
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
.category h3 {
|
||||
font-size: 0.75rem;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.category ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.category li {
|
||||
padding: 1px 0;
|
||||
}
|
||||
|
||||
.category li a {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85rem;
|
||||
display: block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
transition: background 150ms var(--ease);
|
||||
}
|
||||
|
||||
.category li a:hover {
|
||||
background: var(--accent-dim);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.func-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.func-item {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.func-item a {
|
||||
display: block;
|
||||
padding: 7px 12px;
|
||||
border-radius: 6px;
|
||||
transition: background 150ms var(--ease);
|
||||
}
|
||||
|
||||
.func-item a:hover {
|
||||
background: var(--bg-surface-hover);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 500;
|
||||
padding: 1px 7px;
|
||||
border-radius: 4px;
|
||||
margin-left: 6px;
|
||||
vertical-align: middle;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.dim {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* ── Detail Page ── */
|
||||
|
||||
.doc-detail {
|
||||
max-width: 780px;
|
||||
}
|
||||
|
||||
.doc-section {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.doc-section h3 {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.sig {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
font-size: 120%;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.05rem;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.5;
|
||||
background: var(--bg-code);
|
||||
border-color: var(--border-strong);
|
||||
}
|
||||
|
||||
.params {
|
||||
.sig div {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.params div {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.params div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.params b {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.see {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.see a {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.see .category {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* ── Forms ── */
|
||||
|
||||
form > div, label {
|
||||
display: block;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
input, textarea, select {
|
||||
background: var(--bg-inset);
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
transition: border-color 150ms var(--ease), background 150ms var(--ease);
|
||||
}
|
||||
|
||||
input[type=text], textarea {
|
||||
padding: 10px 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text]:hover, textarea:hover,
|
||||
input[type=text]:focus, textarea:focus {
|
||||
border-color: var(--accent);
|
||||
outline: none;
|
||||
background: rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
input[type=submit], button {
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
background: var(--accent-dim);
|
||||
border: 1px solid rgba(255, 214, 68, 0.25);
|
||||
color: var(--accent);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
input[type=submit]:hover, button:hover {
|
||||
background: rgba(255, 214, 68, 0.2);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-hover);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* ── Code ── */
|
||||
|
||||
pre {
|
||||
padding: 16px 20px;
|
||||
overflow: auto;
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.55;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9em;
|
||||
padding: 2px 6px;
|
||||
background: var(--bg-inset);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* ── Lists ── */
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
/* ── Details / Summary ── */
|
||||
|
||||
details {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
summary {
|
||||
padding: 14px 20px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
user-select: none;
|
||||
transition: color 150ms var(--ease);
|
||||
}
|
||||
|
||||
summary:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
details pre {
|
||||
border: none;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── Search ── */
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
#doc-search {
|
||||
flex: 1;
|
||||
max-width: 480px;
|
||||
padding: 11px 16px 11px 42px;
|
||||
background: var(--bg-inset) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.35)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E") no-repeat 14px center;
|
||||
border: 1px solid var(--border-strong);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.95rem;
|
||||
transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
|
||||
}
|
||||
|
||||
#doc-search::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
#doc-search:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px rgba(255, 214, 68, 0.12);
|
||||
}
|
||||
|
||||
.search-count {
|
||||
font-size: 0.82rem;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#search-results {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
#search-results .no-results {
|
||||
grid-column: 1 / -1;
|
||||
color: var(--text-dim);
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.search-hit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.search-snippet {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
font-family: var(--font-mono);
|
||||
padding: 0 12px 6px;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* ── Scrollbar ── */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user