Skip to content

Commit a5f1d61

Browse files
sync from internal repository
1 parent e4452ad commit a5f1d61

File tree

12 files changed

+77
-51
lines changed

12 files changed

+77
-51
lines changed

AGENTS.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ bin/bump-version # Bump version (--patch, --minor (default), --major)
2424
dkdc-links/ # Core Rust crate (standalone, not in monorepo workspace)
2525
src/lib.rs # Library root
2626
src/main.rs # Binary entry point
27-
src/cli.rs # CLI (clap), optional `gui` subcommand
27+
src/cli.rs # CLI (clap) with --app and --webapp flags
2828
src/config.rs # Config loading/saving (~/.config/dkdc/links/config.toml)
2929
src/open.rs # Link resolution (alias → link → URI)
30-
src/gui.rs # iced GUI (behind `gui` feature flag)
30+
src/app.rs # iced desktop app (behind `app` feature flag)
31+
src/webapp.rs # Axum HTMX webapp on port 1414 (behind `webapp` feature flag)
32+
assets/icon.png # App window icon
3133
dkdc-links-py/ # PyO3 bindings (cdylib)
3234
src/dkdc_links/ # Python wrapper + type stubs (core.pyi, py.typed)
3335
```

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ dkdc-links dev
5252
| Flag | Short | Description |
5353
|------|-------|-------------|
5454
| `--config` | `-c` | Open config file in editor |
55-
| `--app` | `-a` | Open desktop app (requires `app` feature; under construction) |
56-
| `--webapp` | `-w` | Open the webapp (requires `webapp` feature) |
55+
| `--app` | `-a` | Open desktop app (iced GUI, requires `app` feature) |
56+
| `--webapp` | `-w` | Open the webapp on localhost:1414 (requires `webapp` feature) |
5757
| `--help` | `-h` | Print help |
5858
| `--version` | `-V` | Print version |
5959

dkdc-links-py/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dkdc-links-py/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[package]
44
name = "dkdc-links-py"
5-
version = "4.1.1"
5+
version = "4.1.2"
66
edition = "2021"
77
authors = ["Cody <cody@dkdc.dev>"]
88
license = "MIT"

dkdc-links/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dkdc-links/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ codegen-units = 1
77

88
[package]
99
name = "dkdc-links"
10-
version = "4.1.1"
10+
version = "4.1.2"
1111
edition = "2021"
1212
authors = ["Cody <cody@dkdc.dev>"]
1313
description = "Bookmarks in your terminal"
1414
repository = "https://github.com/lostmygithubaccount/dkdc-links"
1515
homepage = "https://github.com/lostmygithubaccount/dkdc-links"
1616
license = "MIT"
17+
readme = "../README.md"
1718

1819
[lib]
1920
name = "dkdc_links"

dkdc-links/src/app.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::collections::HashSet;
99

1010
use crate::config::Config;
1111
use crate::storage::Storage;
12+
use crate::strings;
1213

1314
// -- Colors ------------------------------------------------------------------
1415

@@ -259,8 +260,7 @@ impl Links {
259260
let target = self.add_alias_target.trim().to_string();
260261
if !alias.is_empty() && !target.is_empty() {
261262
if !self.config.links.contains_key(&target) {
262-
self.error =
263-
Some(format!("alias target '{target}' does not exist in links"));
263+
self.error = Some(strings::err_alias_target_missing(&target));
264264
} else {
265265
self.config.aliases.insert(alias, target);
266266
self.save();
@@ -290,8 +290,7 @@ impl Links {
290290
.map(String::as_str)
291291
.collect();
292292
if !missing.is_empty() {
293-
self.error =
294-
Some(format!("group entries not found: {}", missing.join(", ")));
293+
self.error = Some(strings::err_group_entries_missing(&missing));
295294
} else {
296295
self.config.groups.insert(name, entries);
297296
self.save();
@@ -477,7 +476,7 @@ impl Links {
477476
}
478477
(ItemKind::Alias, "value") => {
479478
if !self.config.links.contains_key(value) {
480-
self.error = Some(format!("alias target '{value}' does not exist in links"));
479+
self.error = Some(strings::err_alias_target_missing(value));
481480
return;
482481
}
483482
if let Some(target) = self.config.aliases.get_mut(name) {
@@ -506,7 +505,7 @@ impl Links {
506505
.map(String::as_str)
507506
.collect();
508507
if !missing.is_empty() {
509-
self.error = Some(format!("group entries not found: {}", missing.join(", ")));
508+
self.error = Some(strings::err_group_entries_missing(&missing));
510509
return;
511510
}
512511
if let Some(existing) = self.config.groups.get_mut(name) {
@@ -559,7 +558,7 @@ impl Links {
559558
iced::widget::span("dkdc-links")
560559
.size(13)
561560
.color(colors::PURPLE)
562-
.link("https://dkdc.io/links/".to_string()),
561+
.link(strings::PROJECT_URL.to_string()),
563562
iced::widget::span(": bookmarks in your ")
564563
.size(13)
565564
.color(colors::TEXT_DIM),
@@ -637,7 +636,7 @@ impl Links {
637636
}
638637

639638
fn view_toolbar(&self) -> Element<'_, Message> {
640-
let search = text_input("filter...", &self.search)
639+
let search = text_input(strings::PH_FILTER, &self.search)
641640
.on_input(Message::SearchChanged)
642641
.size(13)
643642
.width(200)
@@ -727,13 +726,13 @@ impl Links {
727726

728727
fn view_add_forms(&self) -> Element<'_, Message> {
729728
let link_form = row![
730-
text_input("link name", &self.add_link_name)
729+
text_input(strings::PH_LINK_NAME, &self.add_link_name)
731730
.on_input(Message::AddLinkName)
732731
.on_submit(Message::SubmitLink)
733732
.size(13)
734733
.width(Length::FillPortion(2))
735734
.style(|_, status| input_style(status)),
736-
text_input("https://...", &self.add_link_url)
735+
text_input(strings::PH_LINK_URL, &self.add_link_url)
737736
.on_input(Message::AddLinkUrl)
738737
.on_submit(Message::SubmitLink)
739738
.size(13)
@@ -749,13 +748,13 @@ impl Links {
749748
.align_y(iced::Alignment::Center);
750749

751750
let alias_form = row![
752-
text_input("alias", &self.add_alias_name)
751+
text_input(strings::PH_ALIAS_NAME, &self.add_alias_name)
753752
.on_input(Message::AddAliasName)
754753
.on_submit(Message::SubmitAlias)
755754
.size(13)
756755
.width(Length::FillPortion(2))
757756
.style(|_, status| input_style(status)),
758-
text_input("link name", &self.add_alias_target)
757+
text_input(strings::PH_ALIAS_TARGET, &self.add_alias_target)
759758
.on_input(Message::AddAliasTarget)
760759
.on_submit(Message::SubmitAlias)
761760
.size(13)
@@ -771,13 +770,13 @@ impl Links {
771770
.align_y(iced::Alignment::Center);
772771

773772
let group_form = row![
774-
text_input("group name", &self.add_group_name)
773+
text_input(strings::PH_GROUP_NAME, &self.add_group_name)
775774
.on_input(Message::AddGroupName)
776775
.on_submit(Message::SubmitGroup)
777776
.size(13)
778777
.width(Length::FillPortion(2))
779778
.style(|_, status| input_style(status)),
780-
text_input("link1, alias2, ...", &self.add_group_entries)
779+
text_input(strings::PH_GROUP_ENTRIES, &self.add_group_entries)
781780
.on_input(Message::AddGroupEntries)
782781
.on_submit(Message::SubmitGroup)
783782
.size(13)

dkdc-links/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod cli;
22
pub mod config;
33
pub mod open;
44
pub mod storage;
5+
pub mod strings;
56
pub mod toml_storage;
67

78
#[cfg(feature = "app")]

dkdc-links/src/strings.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//! Shared string constants used across CLI, app, and webapp.
2+
3+
// -- Project ----------------------------------------------------------------
4+
5+
pub const PROJECT_URL: &str = "https://dkdc.io/links/";
6+
7+
// -- Placeholders ------------------------------------------------------------
8+
9+
pub const PH_LINK_NAME: &str = "link name";
10+
pub const PH_LINK_URL: &str = "https://...";
11+
pub const PH_ALIAS_NAME: &str = "alias name";
12+
pub const PH_ALIAS_TARGET: &str = "link name";
13+
pub const PH_GROUP_NAME: &str = "group name";
14+
pub const PH_GROUP_ENTRIES: &str = "link name, alias name, ...";
15+
pub const PH_FILTER: &str = "filter...";
16+
17+
// -- Error templates ---------------------------------------------------------
18+
19+
pub fn err_alias_target_missing(target: &str) -> String {
20+
format!("alias target '{target}' does not exist in links")
21+
}
22+
23+
pub fn err_group_entries_missing(missing: &[&str]) -> String {
24+
format!("group entries not found: {}", missing.join(", "))
25+
}

dkdc-links/src/webapp.rs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::sync::{Arc, Mutex};
99

1010
use crate::config::Config;
1111
use crate::storage::Storage;
12+
use crate::strings;
1213

1314
struct AppState {
1415
storage: Mutex<Box<dyn Storage>>,
@@ -34,6 +35,7 @@ fn escape(s: &str) -> String {
3435
// -- HTML rendering ----------------------------------------------------------
3536

3637
fn page(body: &str) -> String {
38+
let project_url = strings::PROJECT_URL;
3739
format!(
3840
r##"<!DOCTYPE html>
3941
<html lang="en">
@@ -131,7 +133,7 @@ fn page(body: &str) -> String {
131133
</head>
132134
<body>
133135
<h1>Bookmarks</h1>
134-
<p class="subtitle"><a href="https://dkdc.io/links/" target="_blank" rel="noopener">dkdc-links</a>: bookmarks in your <s>terminal</s> browser</p>
136+
<p class="subtitle"><a href="{project_url}" target="_blank" rel="noopener">dkdc-links</a>: bookmarks in your <s>terminal</s> browser</p>
135137
<div id="content">
136138
{body}
137139
</div>
@@ -483,14 +485,15 @@ fn render_content(config: &Config, sort: SortField, error: Option<&str>) -> Stri
483485
// Toolbar: search + tab filter
484486
html.push_str(&format!(
485487
r##"<div class="toolbar">
486-
<input id="search" type="text" placeholder="filter..." oninput="filterRows()" autocomplete="off">
488+
<input id="search" type="text" placeholder="{ph_filter}" oninput="filterRows()" autocomplete="off">
487489
<div class="tabs">
488490
<button id="tab-all" class="tab active" onclick="showTab('all')">all</button>
489491
<button id="tab-links" class="tab" onclick="showTab('links')">links<span class="counts">{lc}</span></button>
490492
<button id="tab-aliases" class="tab" onclick="showTab('aliases')">aliases<span class="counts">{ac}</span></button>
491493
<button id="tab-groups" class="tab" onclick="showTab('groups')">groups<span class="counts">{gc}</span></button>
492494
</div>
493495
</div>"##,
496+
ph_filter = strings::PH_FILTER,
494497
lc = links.len(),
495498
ac = aliases.len(),
496499
gc = groups.len(),
@@ -514,25 +517,31 @@ fn render_content(config: &Config, sort: SortField, error: Option<&str>) -> Stri
514517
);
515518

516519
// Add forms at the top
517-
html.push_str(
520+
html.push_str(&format!(
518521
r##"<div class="section">
519522
<form class="inline" hx-post="/add/link" hx-target="#content">
520-
<input name="name" placeholder="link name" required>
521-
<input name="url" placeholder="https://..." required>
523+
<input name="name" placeholder="{ph_link_name}" required>
524+
<input name="url" placeholder="{ph_link_url}" required>
522525
<button class="btn btn-add" type="submit">+ link</button>
523526
</form>
524527
<form class="inline" hx-post="/add/alias" hx-target="#content">
525-
<input name="alias" placeholder="alias" required>
526-
<input name="target" placeholder="link name" required>
528+
<input name="alias" placeholder="{ph_alias_name}" required>
529+
<input name="target" placeholder="{ph_alias_target}" required>
527530
<button class="btn btn-add" type="submit">+ alias</button>
528531
</form>
529532
<form class="inline" hx-post="/add/group" hx-target="#content">
530-
<input name="name" placeholder="group name" required>
531-
<input name="entries" placeholder="link1, alias2, ..." required>
533+
<input name="name" placeholder="{ph_group_name}" required>
534+
<input name="entries" placeholder="{ph_group_entries}" required>
532535
<button class="btn btn-add" type="submit">+ group</button>
533536
</form>
534537
</div>"##,
535-
);
538+
ph_link_name = strings::PH_LINK_NAME,
539+
ph_link_url = strings::PH_LINK_URL,
540+
ph_alias_name = strings::PH_ALIAS_NAME,
541+
ph_alias_target = strings::PH_ALIAS_TARGET,
542+
ph_group_name = strings::PH_GROUP_NAME,
543+
ph_group_entries = strings::PH_GROUP_ENTRIES,
544+
));
536545

537546
// Links section
538547
html.push_str(r##"<div class="section" id="section-links"><h2>links</h2>"##);
@@ -641,10 +650,7 @@ async fn add_alias(State(state): S, axum::extract::Form(form): Form) -> Html<Str
641650
if !alias.is_empty() && !target.is_empty() {
642651
let config = state.load_config();
643652
if !config.links.contains_key(&target) {
644-
return content_err(
645-
&state,
646-
&format!("alias target '{target}' does not exist in links"),
647-
);
653+
return content_err(&state, &strings::err_alias_target_missing(&target));
648654
}
649655
let mut config = config;
650656
config.aliases.insert(alias, target);
@@ -673,10 +679,7 @@ async fn add_group(State(state): S, axum::extract::Form(form): Form) -> Html<Str
673679
.map(String::as_str)
674680
.collect();
675681
if !missing.is_empty() {
676-
return content_err(
677-
&state,
678-
&format!("group entries not found: {}", missing.join(", ")),
679-
);
682+
return content_err(&state, &strings::err_group_entries_missing(&missing));
680683
}
681684
let mut config = config;
682685
config.groups.insert(name, entries);
@@ -747,10 +750,7 @@ async fn edit_alias(
747750

748751
if let Some(new_target) = new_target {
749752
if !config.links.contains_key(new_target) {
750-
return content_err(
751-
&state,
752-
&format!("alias target '{new_target}' does not exist in links"),
753-
);
753+
return content_err(&state, &strings::err_alias_target_missing(new_target));
754754
}
755755
if let Some(target) = config.aliases.get_mut(&name) {
756756
*target = new_target.clone();
@@ -792,10 +792,7 @@ async fn edit_group(
792792
.map(String::as_str)
793793
.collect();
794794
if !missing.is_empty() {
795-
return content_err(
796-
&state,
797-
&format!("group entries not found: {}", missing.join(", ")),
798-
);
795+
return content_err(&state, &strings::err_group_entries_missing(&missing));
799796
}
800797
if let Some(existing) = config.groups.get_mut(&name) {
801798
*existing = entries;

0 commit comments

Comments
 (0)