feat: first take at UI app

This commit is contained in:
2026-04-24 14:54:04 +02:00
parent 78609377be
commit 1b5a5008eb
24 changed files with 2630 additions and 3 deletions

View File

@@ -0,0 +1,3 @@
defmodule BDS.Desktop.MainWindow do
@moduledoc false
end

42
lib/bds/desktop/menu.ex Normal file
View File

@@ -0,0 +1,42 @@
defmodule BDS.Desktop.Menu do
@moduledoc false
use Desktop.Menu
alias Desktop.Window
@impl true
def mount(menu) do
{:ok, menu}
end
@impl true
def render(assigns) do
~H"""
<menu>
<item onclick="open">Open</item>
<hr />
<item onclick="quit">Quit</item>
</menu>
"""
end
@impl true
def handle_event("open", menu) do
Window.show(BDS.Desktop.MainWindow)
{:noreply, menu}
end
def handle_event("quit", menu) do
Window.quit()
{:noreply, menu}
end
def handle_event(_, menu) do
{:noreply, menu}
end
@impl true
def handle_info(_, menu) do
{:noreply, menu}
end
end

View File

@@ -0,0 +1,73 @@
defmodule BDS.Desktop.MenuBar do
@moduledoc false
use Desktop.Menu
alias Desktop.Window
def groups(opts \\ []) do
dev_mode? = Keyword.get(opts, :dev_mode?, false)
[
%{id: :app, label: "App", items: [%{id: :about, label: "About"}]},
%{id: :file, label: "File", items: [%{id: :new_post, label: "New Post"}, %{id: :close_tab, label: "Close Tab"}]},
%{id: :edit, label: "Edit", items: [%{id: :undo, label: "Undo"}, %{id: :redo, label: "Redo"}]},
%{id: :view, label: "View", items: view_items(dev_mode?)},
%{id: :window, label: "Window", items: [%{id: :minimize, label: "Minimize"}]},
%{id: :help, label: "Help", items: [%{id: :documentation, label: "Documentation"}]}
]
end
@impl true
def mount(menu) do
{:ok,
Desktop.Menu.assign(
menu,
:groups,
groups(dev_mode?: Application.get_env(:bds, :dev_routes, false))
)}
end
@impl true
def render(assigns) do
~H"""
<menubar>
<%= for group <- @groups do %>
<menu label={group.label}>
<%= for item <- group.items do %>
<item onclick={Atom.to_string(item.id)}>{item.label}</item>
<% end %>
</menu>
<% end %>
</menubar>
"""
end
@impl true
def handle_event("quit", menu) do
Window.quit()
{:noreply, menu}
end
def handle_event(_, menu) do
{:noreply, menu}
end
@impl true
def handle_info(_, menu) do
{:noreply, menu}
end
defp view_items(dev_mode?) do
items = [
%{id: :toggle_sidebar, label: "Toggle Sidebar"},
%{id: :toggle_panel, label: "Toggle Panel"},
%{id: :toggle_assistant_sidebar, label: "Toggle Assistant Sidebar"}
]
if dev_mode? do
items ++ [%{id: :toggle_dev_tools, label: "Toggle Dev Tools"}]
else
items
end
end
end

49
lib/bds/desktop/router.ex Normal file
View File

@@ -0,0 +1,49 @@
defmodule BDS.Desktop.Router do
@moduledoc false
use Plug.Router
plug :put_secret_key_base
plug Plug.Session,
store: :cookie,
key: "_bds_desktop_key",
signing_salt: "desktop-shell"
plug :match
plug Desktop.Auth
plug Plug.Static,
at: "/assets",
from: {:bds, "priv/ui"},
only: ["app.css", "app.js"]
plug :dispatch
get "/" do
conn
|> Plug.Conn.put_resp_content_type("text/html")
|> Plug.Conn.send_resp(200, BDS.Desktop.ShellController.index_html())
end
get "/health" do
Plug.Conn.send_resp(conn, 200, "ok")
end
match _ do
Plug.Conn.send_resp(conn, 404, "not found")
end
defp put_secret_key_base(conn, _opts) do
if conn.secret_key_base do
conn
else
%{conn | secret_key_base: desktop_secret_key_base()}
end
end
defp desktop_secret_key_base do
Application.get_env(:bds, :desktop)[:secret_key_base] ||
raise "missing :desktop secret_key_base configuration"
end
end

38
lib/bds/desktop/server.ex Normal file
View File

@@ -0,0 +1,38 @@
defmodule BDS.Desktop.Server do
@moduledoc false
use GenServer
def child_spec(opts) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [opts]}
}
end
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def url do
BDS.Desktop.url(port())
end
def port do
Application.get_env(:bds, :desktop)[:port] || 4010
end
@impl true
def init(_opts) do
{:ok, bandit_pid} =
Bandit.start_link(
plug: BDS.Desktop.Router,
scheme: :http,
ip: {127, 0, 0, 1},
port: port(),
startup_log: false
)
{:ok, %{bandit_pid: bandit_pid}}
end
end

View File

@@ -0,0 +1,7 @@
defmodule BDS.Desktop.ShellController do
@moduledoc false
def index_html do
File.read!(Application.app_dir(:bds, ["priv", "ui", "index.html"]))
end
end