fix: harden UCE runtime and starter
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Login";
|
||||
context.call["app"]["page_title"] = "Login";
|
||||
StarterUser users(context);
|
||||
DTree result;
|
||||
if(context.params["REQUEST_METHOD"] == "POST")
|
||||
@@ -11,7 +10,7 @@ RENDER(Request& context)
|
||||
result = users.sign_in(context.post["email"], context.post["password"]);
|
||||
if(result["result"].to_string() != "")
|
||||
{
|
||||
starter_redirect("account/profile", context);
|
||||
redirect(app_link("account/profile", context));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +22,6 @@ RENDER(Request& context)
|
||||
<label>Password: <input type="password" name="password" required></label><br>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
<p><a href="<?= starter_link("account/register", context) ?>">Register</a></p>
|
||||
<p><a href="<?= app_link("account/register", context) ?>">Register</a></p>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
StarterUser(context).logout();
|
||||
starter_redirect("account/login", context);
|
||||
redirect(app_link("account/login", context));
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
StarterUser users(context);
|
||||
if(!users.is_signed_in())
|
||||
{
|
||||
starter_redirect("account/login", context);
|
||||
redirect(app_link("account/login", context));
|
||||
return;
|
||||
}
|
||||
context.call["starter"]["page_title"] = "Profile";
|
||||
context.call["app"]["page_title"] = "Profile";
|
||||
DTree user = users.current();
|
||||
String roles = "";
|
||||
user["roles"].each([&](DTree role, String key) {
|
||||
@@ -22,6 +21,6 @@ RENDER(Request& context)
|
||||
<p>Email: <?= user["email"].to_string() ?></p>
|
||||
<p>Roles: <?= roles ?></p>
|
||||
<p>Created: <?= time_format_local("%Y-%m-%dT%H:%M:%S", int_val(user["created"].to_string())) ?></p>
|
||||
<p><a href="<?= starter_link("account/logout", context) ?>">Logout</a></p>
|
||||
<p><a href="<?= app_link("account/logout", context) ?>">Logout</a></p>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Register";
|
||||
context.call["app"]["page_title"] = "Register";
|
||||
StarterUser users(context);
|
||||
DTree result;
|
||||
if(context.params["REQUEST_METHOD"] == "POST")
|
||||
@@ -11,7 +10,7 @@ RENDER(Request& context)
|
||||
|
||||
<>
|
||||
<h1>Register</h1>
|
||||
<? if(result["result"].to_string() != "") { ?><div class="banner success">Registration successful. <a href="<?= starter_link("account/login", context) ?>">Log in</a></div><? } ?>
|
||||
<? if(result["result"].to_string() != "") { ?><div class="banner success">Registration successful. <a href="<?= app_link("account/login", context) ?>">Log in</a></div><? } ?>
|
||||
<? if(result["message"].to_string() != "" && result["result"].to_string() == "") { ?><div class="banner error"><?= result["message"].to_string() ?></div><? } ?>
|
||||
<form method="post">
|
||||
<label>Email: <input type="email" name="email" required></label><br>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "OAuth Callback";
|
||||
context.call["app"]["page_title"] = "OAuth Callback";
|
||||
String code = context.get["code"];
|
||||
String state = context.get["state"];
|
||||
String error = context.get["error"];
|
||||
@@ -18,8 +17,8 @@ RENDER(Request& context)
|
||||
<h2>Authentication Failed</h2>
|
||||
<p><?= first(error_description, error) ?></p>
|
||||
<div class="callback-actions">
|
||||
<a href="<?= starter_link("auth/demo", context) ?>" class="btn btn-primary">Try Again</a>
|
||||
<a href="<?= starter_link("", context) ?>" class="btn btn-outline">Go Home</a>
|
||||
<a href="<?= app_link("auth/demo", context) ?>" class="btn btn-primary">Try Again</a>
|
||||
<a href="<?= app_link("", context) ?>" class="btn btn-outline">Go Home</a>
|
||||
</div>
|
||||
</div>
|
||||
<? } else if(code != "" && state != "") { ?>
|
||||
@@ -33,8 +32,8 @@ RENDER(Request& context)
|
||||
<p><strong>State:</strong> <?= state ?></p>
|
||||
</div>
|
||||
<div class="callback-actions">
|
||||
<a href="<?= starter_link("auth/demo", context) ?>" class="btn btn-primary">Back to Auth Demo</a>
|
||||
<a href="<?= starter_link("", context) ?>" class="btn btn-outline">Go Home</a>
|
||||
<a href="<?= app_link("auth/demo", context) ?>" class="btn btn-primary">Back to Auth Demo</a>
|
||||
<a href="<?= app_link("", context) ?>" class="btn btn-outline">Go Home</a>
|
||||
</div>
|
||||
</div>
|
||||
<? } else { ?>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Auth";
|
||||
starter_register_css("views/marketing.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/marketing.css";
|
||||
print(component("../../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Auth";
|
||||
|
||||
DTree props;
|
||||
props["title"] = "Sign In to Your Account";
|
||||
@@ -13,7 +14,7 @@ RENDER(Request& context)
|
||||
props["github_client_id"] = "YOUR_GITHUB_CLIENT_ID";
|
||||
props["discord_client_id"] = "YOUR_DISCORD_CLIENT_ID";
|
||||
props["twitch_client_id"] = "YOUR_TWITCH_CLIENT_ID";
|
||||
props["callback_url"] = starter_link("auth/callback", context);
|
||||
props["callback_url"] = app_link("auth/callback", context);
|
||||
|
||||
<>
|
||||
<h1>Authentication Demo</h1>
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_type"] = "json";
|
||||
context.call["app"]["page_type"] = "json";
|
||||
|
||||
DTree response;
|
||||
DTree body = json_decode(context.in);
|
||||
if(context.params["REQUEST_METHOD"] == "POST" && body["oauth_service"].to_string() != "" && body["oauth_state"].to_string() != "")
|
||||
{
|
||||
context.session["oauth_service"] = body["oauth_service"].to_string();
|
||||
context.session["oauth_state"] = body["oauth_state"].to_string();
|
||||
context.call["starter"]["json"]["status"] = "success";
|
||||
response["status"] = "success";
|
||||
}
|
||||
else
|
||||
{
|
||||
context.set_status(400, "Bad Request");
|
||||
context.call["starter"]["json"]["status"] = "error";
|
||||
context.call["starter"]["json"]["message"] = "Invalid input";
|
||||
response["status"] = "error";
|
||||
response["message"] = "Invalid input";
|
||||
}
|
||||
print(json_encode(response));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
ONCE(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Dashboard";
|
||||
starter_register_css("views/dashboard.css", context);
|
||||
?><link rel="stylesheet" href="<?= app_asset_url("views/dashboard.css", context) ?>" /><?
|
||||
}
|
||||
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
context.call["app"]["page_title"] = "Dashboard";
|
||||
|
||||
DTree props;
|
||||
DTree item;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Features";
|
||||
starter_register_css("views/marketing.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/marketing.css";
|
||||
print(component("../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Features";
|
||||
<>
|
||||
<h1>Features</h1>
|
||||
<p class="card">This page exists in the UCE port so the starter menu resolves cleanly as a full route, while still reusing the same marketing components as the home page.</p>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Gauges";
|
||||
starter_register_css("themes/common/css/gauges.css", context);
|
||||
context.call["app"]["page_title"] = "Gauges";
|
||||
f64 pi = 3.14159265358979323846;
|
||||
|
||||
DTree props;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Home";
|
||||
starter_register_css("views/marketing.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/marketing.css";
|
||||
print(component("../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Home";
|
||||
|
||||
DTree props;
|
||||
|
||||
@@ -79,13 +80,13 @@ RENDER(Request& context)
|
||||
?><div class="component-card">
|
||||
<h3><?= theme["label"].to_string() ?></h3>
|
||||
<p><?= theme["description"].to_string() ?></p>
|
||||
<a class="btn" href="<?= starter_link("themes", theme_params, context) ?>">Preview Theme</a>
|
||||
<a class="btn" href="<?= app_link("themes", theme_params, context) ?>">Preview Theme</a>
|
||||
</div><?
|
||||
}); ?>
|
||||
<div class="component-card">
|
||||
<h3>Starter Themes</h3>
|
||||
<p>The original light and dark starter skins remain available in the same gallery for side-by-side checks.</p>
|
||||
<a class="btn btn-secondary" href="<?= starter_link("themes", context) ?>">Open Gallery</a>
|
||||
<a class="btn btn-secondary" href="<?= app_link("themes", context) ?>">Open Gallery</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,9 +99,16 @@ RENDER(Request& context)
|
||||
props["secondary_text"] = "View GitHub";
|
||||
print(component("../components/example/marketing_blocks:CTA_SECTION", props, context));
|
||||
|
||||
DTree island_props;
|
||||
island_props["name"] = "StarterGuidelines";
|
||||
island_props["id"] = "starter-guidelines-island";
|
||||
island_props["props"]["route"] = context.call["route"]["l_path"].to_string();
|
||||
island_props["props"]["enhancement"] = "progressive";
|
||||
|
||||
<>
|
||||
<div class="card">
|
||||
<h2>Component Development Guidelines</h2>
|
||||
<?: component("../components/theme/web_affordances:island", island_props, context) ?>
|
||||
<div class="guidelines-grid">
|
||||
<div class="guideline-item" style="border-left-color: var(--primary);"><span class="guideline-icon">🏷️</span><span>Use semantic HTML5 elements</span></div>
|
||||
<div class="guideline-item" style="border-left-color: var(--secondary);"><span class="guideline-icon">🎨</span><span>Implement CSS custom properties for theming</span></div>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Components";
|
||||
starter_register_css("views/marketing.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/marketing.css";
|
||||
print(component("../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Components";
|
||||
|
||||
<>
|
||||
<h1>Component System</h1>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_type"] = "blank";
|
||||
context.call["app"]["page_type"] = "blank";
|
||||
<>
|
||||
<?= std::to_string((u64)time()) ?> - Page 2 Section 1 loaded
|
||||
<pre>UCE starter AJAX fragment response</pre>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Ajaxy";
|
||||
context.call["app"]["page_title"] = "Ajaxy";
|
||||
<>
|
||||
<h1>Ajax Demo</h1>
|
||||
<div id="page2-section1">
|
||||
<p>This is the content of Page 2. You can add more information here.</p>
|
||||
<p>Sed at dolor leo. Morbi a tellus sed nisl dictum ultricies sit amet at purus. Nam mattis metus sed nunc egestas convallis.</p>
|
||||
<br/>
|
||||
<button id="page2-load-button" data-load-url="<?= starter_link("page2-section1", context) ?>" type="button">Load new text</button>
|
||||
<button id="page2-load-button" data-load-url="<?= app_link("page2-section1", context) ?>" type="button">Load new text</button>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Theme Preview";
|
||||
starter_register_css("views/themes.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/themes.css";
|
||||
print(component("../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Theme Preview";
|
||||
String current_theme_key = context.cfg.get_by_path("theme/key").to_string();
|
||||
String theme_label = first(context.cfg.get_by_path("theme/label").to_string(), current_theme_key);
|
||||
String theme_mode = context.cfg.get_by_path("theme/mode").to_string();
|
||||
@@ -18,8 +19,8 @@ RENDER(Request& context)
|
||||
<h1><?= theme_label ?></h1>
|
||||
<p>This route renders the same neutral content inside each theme so downstream projects can compare layout, typography, color tokens, and chrome.</p>
|
||||
<div class="theme-preview-actions">
|
||||
<a class="btn" href="<?= starter_link("themes", theme_params, context) ?>">Back to Gallery</a>
|
||||
<a class="btn btn-secondary" href="<?= starter_link("", theme_params, context) ?>">Open Home in This Theme</a>
|
||||
<a class="btn" href="<?= app_link("themes", theme_params, context) ?>">Back to Gallery</a>
|
||||
<a class="btn btn-secondary" href="<?= app_link("", theme_params, context) ?>">Open Home in This Theme</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="theme-preview-grid">
|
||||
@@ -59,7 +60,6 @@ RENDER(Request& context)
|
||||
<tbody>
|
||||
<tr><td>Navigation</td><td>Should remain readable when menus get long.</td><td>Verified</td></tr>
|
||||
<tr><td>Cards</td><td>Should keep spacing and hierarchy on both desktop and mobile.</td><td>Verified</td></tr>
|
||||
<tr><td>Embeds</td><td>Should render cleanly inside the gallery iframe.</td><td>Verified</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -82,21 +82,6 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.theme-gallery-frame-wrap {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: calc(var(--radius-lg, var(--radius)) - 4px);
|
||||
overflow: hidden;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.theme-gallery-frame-wrap iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 430px;
|
||||
border: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.theme-preview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
@@ -150,20 +135,6 @@
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.embed-mode nav,
|
||||
.embed-mode footer,
|
||||
.embed-mode #theme-switcher,
|
||||
.embed-mode .admin-toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.embed-mode #content,
|
||||
.embed-mode .admin-content {
|
||||
margin-top: 0 !important;
|
||||
min-height: 100vh !important;
|
||||
padding-top: 1rem !important;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.theme-gallery-grid,
|
||||
.theme-preview-grid {
|
||||
@@ -173,8 +144,4 @@
|
||||
.theme-gallery-head {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.theme-gallery-frame-wrap iframe {
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,25 @@
|
||||
#load "../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Themes";
|
||||
starter_register_css("views/themes.css", context);
|
||||
DTree asset_props;
|
||||
asset_props["css"]["0"] = "views/themes.css";
|
||||
print(component("../components/theme/assets", asset_props, context));
|
||||
context.call["app"]["page_title"] = "Themes";
|
||||
String current_theme = context.cfg.get_by_path("theme/key").to_string();
|
||||
|
||||
<>
|
||||
<div class="theme-gallery-shell">
|
||||
<section class="theme-gallery-hero card">
|
||||
<span class="theme-gallery-kicker">Starter Theme Gallery</span>
|
||||
<h1>Compare Themes Side by Side</h1>
|
||||
<p>The gallery renders the same preview content in each theme family. This makes it easier to judge shell fit, typography, token balance, and embed behavior.</p>
|
||||
<h1>Compare Themes</h1>
|
||||
<p>Open each theme preview directly to judge shell fit, typography, and token balance.</p>
|
||||
</section>
|
||||
<div class="theme-gallery-grid">
|
||||
<? context.cfg.get_by_path("theme/options").each([&](DTree theme_info, String theme_key) {
|
||||
StringMap params;
|
||||
params["theme"] = theme_key;
|
||||
String preview_href = starter_link("theme-preview", params, context);
|
||||
params["embed"] = "1";
|
||||
String iframe_href = starter_link("theme-preview", params, context);
|
||||
String preview_href = app_link("theme-preview", params, context);
|
||||
StringMap home_params;
|
||||
home_params["theme"] = theme_key;
|
||||
?><section class="theme-gallery-card<?= theme_key == current_theme ? " is-active" : "" ?>">
|
||||
@@ -33,10 +32,7 @@ RENDER(Request& context)
|
||||
</div>
|
||||
<div class="theme-gallery-actions">
|
||||
<a class="btn" href="<?= preview_href ?>">Open Preview</a>
|
||||
<a class="btn btn-secondary" href="<?= starter_link("", home_params, context) ?>">Open Home</a>
|
||||
</div>
|
||||
<div class="theme-gallery-frame-wrap">
|
||||
<iframe title="<?= theme_info["label"].to_string() ?> preview" src="<?= iframe_href ?>"></iframe>
|
||||
<a class="btn btn-secondary" href="<?= app_link("", home_params, context) ?>">Open Home</a>
|
||||
</div>
|
||||
</section><?
|
||||
}); ?>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#load "../../lib/app.uce"
|
||||
|
||||
RENDER(Request& context)
|
||||
COMPONENT(Request& context)
|
||||
{
|
||||
starter_boot(context);
|
||||
context.call["starter"]["page_title"] = "Workspace";
|
||||
String section = first(context.call["starter"]["route"]["param"].to_string(), "overview");
|
||||
context.call["app"]["page_title"] = "Workspace";
|
||||
String section = first(context.call["route"]["param"].to_string(), "overview");
|
||||
if(section != "overview" && section != "projects" && section != "activity")
|
||||
section = "overview";
|
||||
|
||||
@@ -19,9 +18,9 @@ RENDER(Request& context)
|
||||
<>
|
||||
<div class="ws-nav-group-label">Workspace areas</div>
|
||||
<div class="ws-nav-list">
|
||||
<a class="ws-nav-item<?= section == "overview" ? " is-active" : "" ?>" href="<?= starter_link("workspace/overview", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-compass"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Overview</span><span class="ws-nav-meta">Shell</span></span></span></a>
|
||||
<a class="ws-nav-item<?= section == "projects" ? " is-active" : "" ?>" href="<?= starter_link("workspace/projects", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-folder-tree"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Projects</span><span class="ws-nav-meta">Routes</span></span></span></a>
|
||||
<a class="ws-nav-item<?= section == "activity" ? " is-active" : "" ?>" href="<?= starter_link("workspace/activity", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-wave-square"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Activity</span><span class="ws-nav-meta">State</span></span></span></a>
|
||||
<a class="ws-nav-item<?= section == "overview" ? " is-active" : "" ?>" href="<?= app_link("workspace/overview", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-compass"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Overview</span><span class="ws-nav-meta">Shell</span></span></span></a>
|
||||
<a class="ws-nav-item<?= section == "projects" ? " is-active" : "" ?>" href="<?= app_link("workspace/projects", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-folder-tree"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Projects</span><span class="ws-nav-meta">Routes</span></span></span></a>
|
||||
<a class="ws-nav-item<?= section == "activity" ? " is-active" : "" ?>" href="<?= app_link("workspace/activity", context) ?>"><span class="ws-nav-item-inner"><span class="ws-nav-icon"><i class="fas fa-wave-square"></i></span><span class="ws-nav-item-text"><span class="ws-nav-title">Activity</span><span class="ws-nav-meta">State</span></span></span></a>
|
||||
</div>
|
||||
<div class="ws-demo-sidebar-copy"><p>This sidebar and mobile shell pattern was extracted from the AI portal app and normalized against starter theme tokens.</p></div>
|
||||
</>
|
||||
@@ -32,7 +31,7 @@ RENDER(Request& context)
|
||||
sidebar_body += component("../../components/workspace/primitives:LIST_STATE", sidebar_note, context);
|
||||
|
||||
DTree toolbar_props;
|
||||
toolbar_props["action_html"] = "<a class=\"ws-sidebar-action-btn\" href=\"" + starter_link("workspace/overview", context) + "\"><i class=\"fas fa-grid-2\"></i><span>Open shell</span></a>";
|
||||
toolbar_props["action_html"] = "<a class=\"ws-sidebar-action-btn\" href=\"" + app_link("workspace/overview", context) + "\"><i class=\"fas fa-grid-2\"></i><span>Open shell</span></a>";
|
||||
toolbar_props["search_input_id"] = "workspace-demo-search";
|
||||
toolbar_props["search_input_name"] = "workspace_demo_search";
|
||||
toolbar_props["search_placeholder"] = "Search workspace sections";
|
||||
@@ -95,7 +94,7 @@ RENDER(Request& context)
|
||||
String demo_head = component("../../components/workspace/primitives:SECTION_HEAD", section_head, context);
|
||||
section_props.clear();
|
||||
section_props["header_html"] = demo_head;
|
||||
section_props["body_html"] = detail_html + "<p class=\"ws-inline-note\">Try visiting <strong>" + html_escape(starter_link("workspace/projects", context)) + "</strong> or <strong>" + html_escape(starter_link("workspace/activity", context)) + "</strong> to see the nested route fallback in action.</p>";
|
||||
section_props["body_html"] = detail_html + "<p class=\"ws-inline-note\">Try visiting <strong>" + html_escape(app_link("workspace/projects", context)) + "</strong> or <strong>" + html_escape(app_link("workspace/activity", context)) + "</strong> to see the nested route fallback in action.</p>";
|
||||
main_body += component("../../components/workspace/primitives:SECTION", section_props, context);
|
||||
|
||||
if(section == "activity")
|
||||
@@ -104,7 +103,7 @@ RENDER(Request& context)
|
||||
empty_props["icon_class"] = "fas fa-clock-rotate-left";
|
||||
empty_props["title"] = "No live stream wired yet";
|
||||
empty_props["text"] = "The shell is generic. Add your own websocket, polling, or event-driven runtime behind it when a real product needs one.";
|
||||
empty_props["action_html"] = "<a class=\"ws-primary-btn\" href=\"" + starter_link("dashboard", context) + "\">Open dashboard demo</a>";
|
||||
empty_props["action_html"] = "<a class=\"ws-primary-btn\" href=\"" + app_link("dashboard", context) + "\">Open dashboard demo</a>";
|
||||
main_body += component("../../components/workspace/primitives:EMPTY_STATE", empty_props, context);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user