Skip to content

Commit

Permalink
vaev-tools: Implemented the CLI with karm-cli.
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepy-monax committed Sep 17, 2024
1 parent d15a108 commit 6584dab
Show file tree
Hide file tree
Showing 11 changed files with 572 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/apps/hideo-files/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void reduce(State &s, Action a) {
dest.append(navigate.item);

auto stat = Sys::stat(dest).unwrap();
if (stat.type == Sys::Stat::FILE) {
if (stat.type == Sys::Type::FILE) {
(void)Sys::launch(Mime::Uti::PUBLIC_PREVIEW, dest);
} else {
reduce(s, GoTo{dest});
Expand Down
2 changes: 1 addition & 1 deletion src/apps/hideo-files/widgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Ui::Child directorEntry(Sys::DirEntry const &entry, bool odd) {
return Ui::button(
Model::bind<Navigate>(entry.name),
itemStyle(odd),
entry.isDir
entry.type == Sys::Type::DIR
? Mdi::FOLDER
: Mime::iconFor(Mime::sniffSuffix(Mime::suffixOf(entry.name)).unwrapOr("file"s)),
entry.name
Expand Down
29 changes: 25 additions & 4 deletions src/libs/karm-cli/args.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <karm-base/rc.h>
#include <karm-base/string.h>
#include <karm-base/vec.h>
#include <karm-logger/logger.h>
#include <karm-sys/chan.h>
#include <karm-sys/context.h>

Expand Down Expand Up @@ -427,8 +428,7 @@ struct Command {
co_return co_await execAsync(ctx, tokens);
}

Async::Task<> execAsync(Sys::Context &ctx, Slice<Token> tokens) {
Cursor<Token> c = tokens;
Async::Task<> execAsync(Sys::Context &ctx, Cursor<Token> c) {
co_try$(_evalParams(c));

if (_help) {
Expand All @@ -447,9 +447,30 @@ struct Command {
if (callbackAsync)
co_trya$(callbackAsync.unwrap()(ctx));

for (auto &cmd : _commands)
co_trya$(cmd->execAsync(ctx));
if (not c.ended()) {
if (c->kind != Token::OPERAND)
co_return Error::invalidInput("expected subcommand");

if (not any(_commands))
co_return Error::invalidInput("unexpected subcommand");

auto value = c->value;
c.next();

for (auto &cmd : _commands) {

bool shortNameMatch = value.len() == 1 and iterRunes(value).first() == cmd->shortName;
bool longNameMatch = value == cmd->longName;

if (not(shortNameMatch or longNameMatch))
continue;

co_return co_await cmd->execAsync(ctx, c);
}

logError("unknown subcommand '{}'", value);
co_return Error::invalidInput("unknown subcommand");
}
co_return Ok();
}

Expand Down
2 changes: 1 addition & 1 deletion src/libs/karm-text/book.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Res<> FontBook::loadAll() {
auto dir = maybeDir.take();

for (auto &diren : dir.entries()) {
if (diren.isDir)
if (diren.type != Sys::Type::FILE)
continue;

auto fontUrl = dir.path() / diren.name;
Expand Down
14 changes: 14 additions & 0 deletions src/libs/karm-ui/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,18 @@ Res<> runApp(Sys::Context &ctx, Child root) {
return try$(_Embed::makeHost(root))->run();
}

void mountApp(Cli::Command &cmd, Slot rootSlot) {
Cli::Flag debugArg = Cli::flag(NONE, "debug"s, "Show debug inspector."s);
Cli::Flag mobileArg = Cli::flag(NONE, "mobile"s, "Show mobile layout."s);

cmd.option(debugArg);
cmd.option(mobileArg);
cmd.callbackAsync = [rootSlot = std::move(rootSlot), debugArg](Sys::Context &) -> Async::Task<> {
auto root = rootSlot();
if (debugArg)
root = root | inspector;
co_return co_try$(_Embed::makeHost(root))->run();
};
}

} // namespace Karm::Ui
3 changes: 3 additions & 0 deletions src/libs/karm-ui/app.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <karm-cli/args.h>
#include <karm-sys/context.h>

#include "node.h"
Expand All @@ -10,4 +11,6 @@ Child inspector(Child child);

Res<> runApp(Sys::Context &ctx, Child root);

void mountApp(Cli::Command &cmd, Slot rootSlot);

} // namespace Karm::Ui
1 change: 1 addition & 0 deletions src/libs/karm-ui/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"type": "lib",
"description": "A graphical user interface library",
"requires": [
"karm-cli",
"karm-ui-impl",
"karm-image",
"karm-media",
Expand Down
213 changes: 213 additions & 0 deletions src/web/vaev-tools/inspector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
#include <karm-kira/context-menu.h>
#include <karm-kira/dialog.h>
#include <karm-kira/error-page.h>
#include <karm-kira/scaffold.h>
#include <karm-kira/side-panel.h>
#include <karm-kira/titlebar.h>
#include <karm-kira/toolbar.h>
#include <karm-mime/mime.h>
#include <karm-sys/file.h>
#include <karm-sys/launch.h>
#include <karm-ui/app.h>
#include <karm-ui/dialog.h>
#include <karm-ui/drag.h>
#include <karm-ui/input.h>
#include <karm-ui/popover.h>
#include <karm-ui/reducer.h>
#include <karm-ui/scroll.h>
#include <vaev-driver/fetcher.h>
#include <vaev-view/inspect.h>
#include <vaev-view/view.h>

#include "inspector.h"

namespace Vaev::Tools {

enum struct SidePanel {
CLOSE,
DEVELOPER_TOOLS,
};

struct State {
Mime::Url url;
Res<Strong<Markup::Document>> dom;
SidePanel sidePanel = SidePanel::CLOSE;

bool canGoBack() const {
return false;
}

bool canGoForward() const {
return false;
}
};

struct Reload {};

struct GoBack {};

struct GoForward {};

using Action = Union<Reload, GoBack, GoForward, SidePanel>;

void reduce(State &s, Action a) {
a.visit(Visitor{
[&](Reload) {
s.dom = Driver::fetchDocument(s.url);
},
[&](GoBack) {
},
[&](GoForward) {
},
[&](SidePanel p) {
s.sidePanel = p;
},
});
}

using Model = Ui::Model<State, Action, reduce>;

Ui::Child mainMenu([[maybe_unused]] State const &s) {
return Kr::contextMenuContent({
Ui::separator(),
Kr::contextMenuItem(Ui::NOP, Mdi::PRINTER, "Print..."),
Kr::contextMenuItem(
[&](auto &n) {
auto res = Sys::launch(Mime::Uti::PUBLIC_OPEN, s.url);
if (not res)
Ui::showDialog(
n,
Kr::alert(
"Error"s,
Io::format("Failed to open in browser\n\n{}", res).unwrap()
)
);
},
Mdi::WEB, "Open in browser..."
),
Ui::separator(),
Kr::contextMenuItem(Model::bind(SidePanel::DEVELOPER_TOOLS), Mdi::CODE_TAGS, "Inspector"),
});
}

Ui::Child addressBar(Mime::Url const &url) {
return Ui::hflow(
0,
Math::Align::CENTER,
Ui::text("{}", url),
Ui::grow(NONE),
Kr::contextMenuIcon(Model::bind<Reload>(), Mdi::REFRESH)
) |
Ui::box({
.padding = {12, 0, 0, 0},
.borderRadii = 4,
.borderWidth = 1,
.backgroundFill = Ui::GRAY800,
});
}

Ui::Child contextMenu(State const &s) {
return Kr::contextMenuContent({
Kr::contextMenuDock({
Kr::contextMenuIcon(Model::bind<Reload>(), Mdi::REFRESH),
}),
Ui::separator(),
Kr::contextMenuItem(
[s](auto &) {
(void)Sys::launch(Mime::Uti::PUBLIC_MODIFY, s.url);
},
Mdi::CODE_TAGS, "View Source..."
),
Kr::contextMenuItem(Model::bind(SidePanel::DEVELOPER_TOOLS), Mdi::BUTTON_CURSOR, "Inspect"),
});
}

Ui::Child inspectorContent(State const &s) {
if (not s.dom) {
return Ui::labelMedium(Ui::GRAY500, "No document") |
Ui::center();
}

return View::inspect(s.dom.unwrap()) | Ui::vhscroll();
}

Ui::Child sidePanel(State const &s) {
switch (s.sidePanel) {
case SidePanel::DEVELOPER_TOOLS:
return Kr::sidePanelContent({
Kr::sidePanelTitle(Model::bind(SidePanel::CLOSE), "Inspector"),
Ui::separator(),
inspectorContent(s) | Ui::grow(),
});

default:
return Ui::empty();
}
}

Ui::Child alert(State const &s, String title, String body) {
return Kr::errorPageContent({
Kr::errorPageTitle(Mdi::ALERT_DECAGRAM, title),
Kr::errorPageBody(body),
Kr::errorPageFooter({
Ui::button(Model::bindIf<GoBack>(s.canGoBack()), "Go Back"),
Ui::button(Model::bind<Reload>(), Ui::ButtonStyle::primary(), "Reload"),
}),
});
}

Ui::Child webview(State const &s) {
if (not s.dom)
return alert(s, "The page could not be loaded"s, Io::toStr(s.dom).unwrap());

return View::view(s.dom.unwrap()) |
Ui::vscroll() |
Ui::box({
.backgroundFill = Gfx::WHITE,
}) |
Kr::contextMenu(slot$(contextMenu(s)));
}

Ui::Child appContent(State const &s) {
if (s.sidePanel == SidePanel::CLOSE)
return webview(s);
return Ui::hflow(
webview(s) | Ui::grow(),
Ui::separator(),
sidePanel(s)
);
}

Ui::Child inspector(Mime::Url url, Res<Strong<Markup::Document>> dom) {
return Ui::reducer<Model>(
{
url,
dom,
},
[](State const &s) {
return Ui::vflow(
Kr::toolbar({
Ui::button(
[&](Ui::Node &n) {
Ui::showDialog(n, Kr::alert("Paper-Muncher"s, "Copyright © 2024, Odoo S.A."s));
},
Ui::ButtonStyle::subtle(),
Mdi::SURFING
),
addressBar(s.url) | Ui::grow(),
Ui::button(
[&](Ui::Node &n) {
Ui::showPopover(n, n.bound().bottomEnd(), mainMenu(s));
},
Ui::ButtonStyle::subtle(), Mdi::DOTS_HORIZONTAL
),
Kr::titlebarControls(),
}) | Ui::dragRegion(),
appContent(s) | Ui::grow()
) |
Ui::pinSize({800, 600}) | Ui::dialogLayer() | Ui::popoverLayer();
}
);
}

} // namespace Vaev::Tools
10 changes: 10 additions & 0 deletions src/web/vaev-tools/inspector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <karm-ui/node.h>
#include <vaev-markup/dom.h>

namespace Vaev::Tools {

Ui::Child inspector(Mime::Url url, Res<Strong<Vaev::Markup::Document>> dom);

} // namespace Vaev::Tools
Loading

0 comments on commit 6584dab

Please sign in to comment.