TRANSCRIPT SNIPPET
[2025-08-20 05:54:01+08:00, USER]
PROMPT
What is needed to make this an arch linux compatible package, and how should i setup the makepkg, and how to install locally, should i put it on github, and make it released as version 0.1, i find it very useful, from using it to highlight stuff in nvim, and pipe :w !waydoc-pipe; waydoc-pipe open, or piping in files from shell, or even wl-paste | waydoc-pipe, its useful for terminal-ai which i built it so i could get it to build, im starting to think i should just make it pipe in just the html and not serve it, but i think it could by default just pipe it in and open it, but keep state in the xdg compliant path, so you can iterate on css or js, and have it automatically open the other, the idea is to devvelop in a way where you decouple the html, js and css; and it was to allow for easy way to turn like markdown into a html thing, and have a print friendly interface, also i wanted to be able to see all the different html things as i had so many, as i would write in a literate way, and then iterate with the ai, trying to get it to work with smaller things, and design patterns that compose, i thinking of ussing configs in xdg spec to define behaviour, i have put it in ~/.local/bin, but this is worthy of a /usr/local/bin, and use the xdg base dirs ffor the ~/.local/state and thing, and have the base configs and man pages in /usr/local/share... but if its nicely packaged and organised, i can make a arch linux package and install it the normal way. Write a very idiomatic approach to allowing all the required standards, its got a lot of use of taking other html, and allowing custom styles, think of the future, i am on a mission of composability, and this is a good constraint, because if something is composable i should be able to build something with just cat .. .. .. then pipe it in. feedback please
**attachment**
```rs
#!/usr/bin/env rust-script
//? webdoc-pipe.rs: Pipe HTML/CSS/JS to a tiny HTTP viewer (Firefox) via a per-user daemon.
//? - No external crates; only Rust std.
//? - Linux/Unix only (uses Unix domain sockets).
//? Usage examples:
//? $ rustc -O -o ~/.local/bin/webdoc-pipe webdoc-pipe.rs
//? $ webdoc-pipe open # start server (if needed) and open URL
//? $ webdoc-pipe url # print URL
//? $ cat index.html | webdoc-pipe # default = HTML update
//? $ cat index.html | webdoc-pipe html # explicit HTML update
//? $ cat style.css | webdoc-pipe css
//? $ cat script.js | webdoc-pipe js
//? $ webdoc-pipe stop # stop the daemon
//?
//? Protocol to server over UDS:
//? <KIND>
//? <BODY... until EOF>
//? where KIND in {"HTML","CSS","JS","STOP"}
//?
//? State is in-memory; updates are atomic via RwLock swap.
use std::env;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::{TcpListener, TcpStream};
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::{Duration, Instant};
#[derive(Clone)]
struct DocState {
html: Arc<RwLock<Vec<u8>>>,
css: Arc<RwLock<Vec<u8>>>,
js: Arc<RwLock<Vec<u8>>>,
}
impl DocState {
fn new() -> Self {
let html = b"<!doctype html>
<meta charset=\"utf-8\">
<title>webdoc</title>
<link rel=\"stylesheet\" href=\"/style.css\">
<script defer src=\"/script.js\"></script>
<h1>webdoc-pipe</h1>
<p>Pipe HTML here: cat index.html | webdoc-pipe html</p>
".to_vec();
let css = b"html{font-family:system-ui,sans-serif}body{margin:2rem;max-width:65ch}".to_vec();
let js = b"/* webdoc default script */".to_vec();
Self {
html: Arc::new(RwLock::new(html)),
css: Arc::new(RwLock::new(css)),
js: Arc::new(RwLock::new(js)),
}
}
}
#[derive(Debug, Clone)]
struct Meta {
pid: u32,
socket_path: PathBuf,
port: u16,
}
fn runtime_dir() -> PathBuf {
if let Ok(p) = env::var("XDG_RUNTIME_DIR") {
PathBuf::from(p)
} else {
// Fallback for systems without XDG_RUNTIME_DIR
env::temp_dir()
}
}
fn meta_path() -> PathBuf {
runtime_dir().join("webdoc-pipe.current")
}
fn parse_meta(s: &str) -> Option<Meta> {
let mut pid = None::<u32>;
let mut socket = None::<PathBuf>;
let mut port = None::<u16>;
for line in s.lines() {
if let Some(rest) = line.strip_prefix("PID=") {
pid = rest.trim().parse::<u32>().ok();
} else if let Some(rest) = line.strip_prefix("SOCKET=") {
socket = Some(PathBuf::from(rest.trim()));
} else if let Some(rest) = line.strip_prefix("PORT=") {
port = rest.trim().parse::<u16>().ok();
}
}
match (pid, socket, port) {
(Some(pid), Some(socket_path), Some(port)) => Some(Meta { pid, socket_path, port }),
_ => None,
}
}
fn load_meta() -> Option<Meta> {
let p = meta_path();
let data = fs::read_to_string(&p).ok()?;
let m = parse_meta(&data)?;
// sanity check: socket exists?
if m.socket_path.exists() {
Some(m)
} else {
// stale meta; clean up
let _ = fs::remove_file(&p);
None
}
}
fn atomic_write(path: &Path, contents: &str) -> io::Result<()> {
let tmp = path.with_extension(format!("new.{}", std::process::id()));
{
let mut f = File::create(&tmp)?;
f.write_all(contents.as_bytes())?;
f.sync_all()?;
}
fs::rename(tmp, path)?;
Ok(())
}
fn write_meta(m: &Meta) -> io::Result<()> {
let p = meta_path();
let text = format!("PID={}
SOCKET={}
PORT={}
", m.pid, m.socket_path.display(), m.port);
atomic_write(&p, &text)
}
fn remove_meta_if_ours(pid: u32) {
if let Some(m) = load_meta() {
if m.pid == pid {
let _ = fs::remove_file(meta_path());
}
}
}
fn try_connect_uds(p: &Path) -> io::Result<UnixStream> {
UnixStream::connect(p)
}
fn ensure_server() -> io::Result<Meta> {
if let Some(m) = load_meta() {
if try_connect_uds(&m.socket_path).is_ok() {
return Ok(m);
} else {
// stale socket; try to remove and continue
let _ = fs::remove_file(&m.socket_path);
let _ = fs::remove_file(meta_path());
}
}
// Spawn server
let exe = env::current_exe()?;
let mut child = Command::new(exe)
.arg("serve")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
// Wait until meta is ready and socket responds
let deadline = Instant::now() + Duration::from_secs(3);
loop {
if let Some(m) = load_meta() {
if try_connect_uds(&m.socket_path).is_ok() {
return Ok(m);
}
}
if Instant::now() > deadline {
let _ = child.kill();
return Err(io::Error::new(io::ErrorKind::TimedOut, "server start timeout"));
}
thread::sleep(Duration::from_millis(50));
}
}
fn open_url(port: u16) {
let url = format!("http://127.0.0.1:{}/", port);
// Prefer firefox
let ok = Command::new("firefox")
.arg(&url)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false);
if ok {
return;
}
let ok2 = Command::new("xdg-open")
.arg(&url)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false);
if !ok2 {
println!("{}", url);
}
}
fn send_update(kind: &str, data: &[u8]) -> io::Result<Meta> {
let m = ensure_server()?;
let mut s = try_connect_uds(&m.socket_path)?;
s.write_all(kind.as_bytes())?;
s.write_all(b"
")?;
s.write_all(data)?;
s.flush()?;
// Optional ACK (not strictly needed); ignore errors
let _ = s.shutdown(std::net::Shutdown::Write);
Ok(m)
}
fn send_stop() -> io::Result<()> {
let m = ensure_server()?;
let mut s = try_connect_uds(&m.socket_path)?;
s.write_all(b"STOP
")?;
let _ = s.shutdown(std::net::Shutdown::Write);
Ok(())
}
fn serve_http_conn(mut tcp: TcpStream, st: DocState) -> io::Result<()> {
tcp.set_nodelay(true).ok();
let peer = tcp.peer_addr().ok();
let mut reader = BufReader::new(tcp.try_clone()?);
let mut req_line = String::new();
if reader.read_line(&mut req_line)? == 0 {
return Ok(());
}
// strip CRLF
' || c == 'e.truncate(req_line.trim_end_matches(|c| c == '
').len());
let parts: Vec<&str> = req_line.split_whitespace().collect();
let method = parts.get(0).copied().unwrap_or("");
let mut path = parts.get(1).copied().unwrap_or("/");
// consume headers
loop {
let mut h = String::new();
let n = reader.read_line(&mut h)?;
if n == 0 { break; }
' || c == ' hs = h.trim_end_matches(|c| c == '
');
if hs.is_empty() { break; }
}
// normalize path
if path.is_empty() { path = "/"; }
if let Some(qpos) = path.find('?') {
path = &path[..qpos];
}
let rel = match path {
"/" | "/index.html" => "index.html",
"/style.css" => "style.css",
"/script.js" => "script.js",
s if s.starts_with('/') => &s[1..],
s => s,
};
// disallow traversal and dotfiles
if rel.contains("..") || rel.starts_with('.') {
respond_plain(&mut tcp, 403, "Forbidden")?;
return Ok(());
}
match rel {
"index.html" => {
let body = st.html.read().unwrap().clone();
respond(&mut tcp, 200, "text/html; charset=utf-8", &body)?;
}
"style.css" => {
let body = st.css.read().unwrap().clone();
respond(&mut tcp, 200, "text/css; charset=utf-8", &body)?;
}
"script.js" => {
let body = st.js.read().unwrap().clone();
respond(&mut tcp, 200, "application/javascript; charset=utf-8", &body)?;
}
_ => {
respond_plain(&mut tcp, 404, "Not Found")?;
}
}
let _ = peer;
Ok(())
}
fn respond(stream: &mut TcpStream, code: u16, ctype: &str, body: &[u8]) -> io::Result<()> {
let status = match code {
200 => "200 OK",
403 => "403 Forbidden",
404 => "404 Not Found",
_ => "200 OK",
};
let headers = format!(
"HTTP/1.1 {status}
Content-Type: {ctype}
Content-Length: {len}
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Connection: close
",
status=status, ctype=ctype, len=body.len()
);
stream.write_all(headers.as_bytes())?;
stream.write_all(body)?;
Ok(())
}
fn respond_plain(stream: &mut TcpStream, code: u16, msg: &str) -> io::Result<()> {
respond(stream, code, "text/plain; charset=utf-8", msg.as_bytes())
}
fn handle_update_conn(mut u: UnixStream, st: DocState, sock_path: PathBuf) -> io::Result<()> {
let mut reader = BufReader::new(u.try_clone()?);
let mut kind = String::new();
if reader.read_line(&mut kind)? == 0 {
return Ok(());
}
// strip CRLF
' || c == 'uncate(kind.trim_end_matches(|c| c == '
').len());
let mut blank = String::new();
// Expect a blank line after kind
let _ = reader.read_line(&mut blank);
let mut data = Vec::new();
reader.read_to_end(&mut data)?;
match kind.trim() {
"HTML" => {
let mut w = st.html.write().unwrap();
*w = data;
}
"CSS" => {
let mut w = st.css.write().unwrap();
*w = data;
}
"JS" => {
let mut w = st.js.write().unwrap();
*w = data;
}
"STOP" => {
// best-effort cleanup then exit fast
let _ = fs::remove_file(&sock_path);
remove_meta_if_ours(std::process::id());
std::process::exit(0);
}
_ => {
// unknown command; ignore
}
}
Ok(())
}
fn run_server() -> io::Result<()> {
let st = DocState::new();
// HTTP
let http = TcpListener::bind(("127.0.0.1", 0))?;
http.set_nonblocking(false).ok();
let port = http.local_addr()?.port();
// UDS control socket
let pid = std::process::id();
let sock_path = runtime_dir().join(format!("webdoc-pipe-{}.sock", pid));
if sock_path.exists() {
let _ = fs::remove_file(&sock_path);
}
let uds = UnixListener::bind(&sock_path)?;
// Restrict permissions best-effort
#[allow(unused_must_use)]
{
let _ = fs::set_permissions(&sock_path, fs::Permissions::from_mode(0o600));
}
// Publish meta
write_meta(&Meta {
pid,
socket_path: sock_path.clone(),
port,
})?;
// UDS loop
let st_for_uds = st.clone();
let sock_path_for_uds = sock_path.clone();
let uds_thread = thread::spawn(move || -> io::Result<()> {
for conn in uds.incoming() {
match conn {
Ok(s) => {
let st2 = st_for_uds.clone();
let sp = sock_path_for_uds.clone();
thread::spawn(move || {
let _ = handle_update_conn(s, st2, sp);
});
}
Err(e) => {
// If listener is closed, exit loop
if e.kind() == io::ErrorKind::Interrupted {
continue;
} else {
break;
}
}
}
}
Ok(())
});
// HTTP loop (main thread)
for conn in http.incoming() {
match conn {
Ok(tcp) => {
let st2 = st.clone();
thread::spawn(move || {
let _ = serve_http_conn(tcp, st2);
});
}
Err(e) => {
if e.kind() == io::ErrorKind::Interrupted {
continue;
} else {
break;
}
}
}
}
let _ = uds_thread.join();
// Cleanup best-effort
let _ = fs::remove_file(&sock_path);
remove_meta_if_ours(pid);
Ok(())
}
fn print_usage() {
eprintln!("Usage:
webdoc-pipe open
webdoc-pipe url
webdoc-pipe stop
webdoc-pipe html < file.html
webdoc-pipe css < file.css
webdoc-pipe js < file.js
# Shortcut: default to 'html' when data is piped:
cat index.html | webdoc-pipe
");
}
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
// Detect piped stdin for default 'html'
let piped_stdin = {
{
use std::io::IsTerminal;
!io::stdin().is_terminal()
}
};
match args.get(1).map(|s| s.as_str()) {
Some("serve") => run_server(),
Some("open") => {
let m = ensure_server()?;
open_url(m.port);
Ok(())
}
Some("url") => {
let m = ensure_server()?;
println!("http://127.0.0.1:{}/", m.port);
Ok(())
}
Some("stop") => {
let _ = send_stop();
Ok(())
}
Some("html") => {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
let m = send_update("HTML", &buf)?;
println!("Updated HTML. URL: http://127.0.0.1:{}/", m.port);
Ok(())
}
Some("css") => {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
let m = send_update("CSS", &buf)?;
println!("Updated CSS. URL: http://127.0.0.1:{}/", m.port);
Ok(())
}
Some("js") => {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
let m = send_update("JS", &buf)?;
println!("Updated JS. URL: http://127.0.0.1:{}/", m.port);
Ok(())
}
None if piped_stdin => {
// default to HTML when data is piped
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
let m = send_update("HTML", &buf)?;
println!("Updated HTML. URL: http://127.0.0.1:{}/", m.port);
Ok(())
}
_ => {
print_usage();
Ok(())
}
}
}
use std::os::unix::fs::PermissionsExt;
[2025-08-20 05:55:30+08:00, AI]
Great idea — this is exactly the kind of tiny, composable tool that benefits from being easy to install and package.
Below is a pragmatic, idiomatic path to:
- make it XDG-Base-Dir friendly
- package it cleanly for Arch (PKGBUILD)
- set up a GitHub repo and v0.1.0 release
- install locally (with or without a package)
- keep the design composable and future-proof
First: settle on one name
- Your script uses “webdoc-pipe”. Stick with that everywhere (binary, repo, man page).
- Avoid /usr/local in a package; Arch packages install to /usr (local is for admin-managed installs outside the package manager).
Filesystem layout (package)
- /usr/bin/webdoc-pipe (binary)
- /usr/share/man/man1/webdoc-pipe.1.gz (man page)
- /usr/share/doc/webdoc-pipe/README.md (docs, optional)
- No config files in /etc; use XDG for per-user config/state/data.
XDG Base Directory usage (recommended behavior)
- Runtime (you already do this): XDG_RUNTIME_DIR/webdoc-pipe-.sock and webdoc-pipe.current
- If XDG_RUNTIME_DIR is missing, use a per-user dir in $TMPDIR or /tmp and mkdir with 0700, then place files inside (avoid predictable names in /tmp root).
- State (persist last content if you want persistence):
- $XDG_STATE_HOME/webdoc-pipe/{index.html,style.css,script.js}
- On server start: if present, load these as initial DocState.
- Add CLI flag: webdoc-pipe serve --persist (or environment WEBDOC_PERSIST=1)
- Config (user behavior, defaults):
- $XDG_CONFIG_HOME/webdoc-pipe/config.toml
- Values you might read:
- browser = "firefox" | "xdg-open" | "custom command"
- open_on_update = true|false
- persist = true|false
- profile = "default" (allow multiple independent instances via profile suffixing file names)
- Data (optional assets users want to keep):
- $XDG_DATA_HOME/webdoc-pipe/templates/… if you ever add templates/snippets.
CLI polish (backwards-compatible)
- Keep current subcommands.
- Add:
- webdoc-pipe serve [--persist] [--profile NAME]
- webdoc-pipe open [--profile NAME]
- webdoc-pipe url [--profile NAME]
- webdoc-pipe stop [--profile NAME]
- Implement profiles by suffixing the socket and meta file names and the state paths with “-”.
- Honor config defaults and allow env overrides (WEBDOC_PROFILE, WEBDOC_BROWSER, WEBDOC_PERSIST).
Security/robustness notes
- In the /tmp fallback, create a private dir: /tmp/webdoc-pipe-$UID with 0700, then put the socket and meta file there. Don’t place the socket directly under /tmp with a predictable name.
- You already chmod the UDS to 0600 — good.
- Consider a simple HUP/USR signal or STOP command already implemented — you’re good.
Make it a Cargo project (strongly recommended) This enables reproducible builds and standard Rust packaging.
Directory structure:
- Cargo.toml
- src/main.rs
- LICENSE
- README.md
- man/webdoc-pipe.1.scd (if you use scdoc) or man/webdoc-pipe.1 (roff)
Example Cargo.toml (0.1.0): [package] name = "webdoc-pipe" version = "0.1.0" edition = "2021" license = "MIT" repository = "https://github.com/you/webdoc-pipe" description = "Pipe HTML/CSS/JS to a tiny per-user HTTP viewer via a Unix socket; opens in your browser" categories = ["command-line-utilities", "web-programming"] keywords = ["html", "pipe", "xdg", "browser"]
[profile.release] lto = true codegen-units = 1 opt-level = "z"
- Move your script body into src/main.rs (remove the rust-script shebang/comments).
- No external crates needed, so Cargo.lock will be trivial.
Man page (optional but nice)
- If you want a light toolchain, use scdoc (small dependency). Example skeleton (man/webdoc-pipe.1.scd):
webdoc-pipe(1) "User Commands" == NAME webdoc-pipe - pipe HTML/CSS/JS to a per-user HTTP viewer
== SYNOPSIS webdoc-pipe open webdoc-pipe url webdoc-pipe stop webdoc-pipe serve [--persist] [--profile NAME] webdoc-pipe [html|css|js] < input
== DESCRIPTION webdoc-pipe runs a per-user HTTP server and updates its in-memory HTML/CSS/JS via a Unix domain socket, then opens it in your browser.
== ENVIRONMENT WEBDOC_PROFILE WEBDOC_BROWSER WEBDOC_PERSIST
== FILES $XDG_RUNTIME_DIR/webdoc-pipe.current $XDG_STATE_HOME/webdoc-pipe/
== SEE ALSO xdg-open(1), firefox(1)
- Build with: scdoc < man/webdoc-pipe.1.scd > webdoc-pipe.1
Arch packaging (PKGBUILD using cargo)
- Put this PKGBUILD in the repo root:
pkgname=webdoc-pipe pkgver=0.1.0 pkgrel=1 pkgdesc="Pipe HTML/CSS/JS to a tiny per-user HTTP viewer via a Unix socket; opens in your browser" arch=('x86_64' 'aarch64') url="https://github.com/you/webdoc-pipe" license=('MIT') depends=() makedepends=('rust' 'scdoc') # drop scdoc if you pre-generate the manpage source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz") sha256sums=('SKIP') # replace with real checksum for AUR submission
build() { cd "$pkgname-$pkgver" cargo build --release --locked
build man page (if using scdoc)
scdoc < man/webdoc-pipe.1.scd > webdoc-pipe.1 }
check() { cd "$pkgname-$pkgver"
optional: run a quick self-test
cargo test --release --locked
: }
package() { cd "$pkgname-$pkgver" install -Dm755 "target/release/webdoc-pipe" "$pkgdir/usr/bin/webdoc-pipe"
man page
install -Dm644 "webdoc-pipe.1" "$pkgdir/usr/share/man/man1/webdoc-pipe.1" gzip -9n "$pkgdir/usr/share/man/man1/webdoc-pipe.1"
docs and license
install -Dm644 README.md "$pkgdir/usr/share/doc/$pkgname/README.md" install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" }
- Build and install locally:
- makepkg -si
- For quick local install without packaging:
- cargo build --release && install -Dm755 target/release/webdoc-pipe ~/.local/bin/webdoc-pipe
AUR submission
- Host code on GitHub (public).
- Create a Git tag v0.1.0 and a release.
- Replace sha256sums with the real checksum:
- makepkg -g >> PKGBUILD (or sha256sum the downloaded tarball).
- Create an AUR Git repo named webdoc-pipe and push PKGBUILD (and .SRCINFO).
- Version bump workflow: tag v0.1.1, update pkgver, checksum, pkgrel=1.
Optional: systemd user unit (packaged)
- Not required because your client auto-spawns the server, but if you want:
File: webdoc-pipe.service (install to /usr/lib/systemd/user/webdoc-pipe.service) [Unit] Description=webdoc-pipe per-user HTTP server
[Service] ExecStart=/usr/bin/webdoc-pipe serve --persist Restart=on-failure
[Install] WantedBy=default.target
- Enable per-user:
- systemctl --user enable --now webdoc-pipe.service
Design feedback: pipe vs serve, composability
- Your “per-user daemon + UDS updates” is a good model. It’s composable (cat … | webdoc-pipe) and fast to iterate.
- Make “open on first update” the default, and allow:
- webdoc-pipe --no-open
- config: open_on_update = false
- Persistence is a nice default for “dev sessions”: if enabled, you can kill the server, come back later, and it will restore the last HTML/CSS/JS from XDG state.
- Profiles are a great way to compose multiple independent pages simultaneously (e.g., webdoc-pipe --profile draft vs webdoc-pipe --profile demo).
- Let CSS/JS be overridden from stdin or config-defined paths, but keep serving from memory to keep the HTTP server stateless.
- Consider adding a tiny “/reload” URL that shows a minimal reload page, but you already disable caching and read from memory, which is fine.
Small code nits you might consider later
- When XDG_RUNTIME_DIR is missing, create a private dir: let dir = PathBuf::from(env::var_os("TMPDIR").unwrap_or_else(|| "/tmp".into())).join(format!("webdoc-pipe-{}", nix::unistd::Uid::effective())); fs::create_dir_all(&dir); chmod 0700.
- Consider binding to 127.0.0.1 only (you already do) and make port stable per-profile by hashing the profile (optional; dynamic is fine given the meta file).
- Allow browser command override via env/config.
Release plan
- v0.1.0: current functionality, Cargoized, man page, XDG state persistence (optional flag), config file support for a few basics, PKGBUILD published in AUR.
- v0.2.0: profiles, completions (bash/zsh/fish), optional systemd user unit, more config knobs.
Quickstart recap
- Local: cargo install --path . (or rustc -O -o ~/.local/bin/webdoc-pipe src/main.rs)
- Packaged: makepkg -si (using the PKGBUILD above)
- Usage:
- webdoc-pipe open
- echo "
Hi
" | webdoc-pipe - cat style.css | webdoc-pipe css
- wl-paste | webdoc-pipe
- nvim: :w !webdoc-pipe
If you want, share the repo URL and I can draft the Cargo.toml, PKGBUILD, and a minimal man page tailored to exactly what you ship.
RANDOM CRAP
MY NOTES BUT DONT WASTE TIME ON IT.
THE IDEA IS THAT THE UX/JS TEAM DOES EACH THING BY THEMSELVES. SO IF HOT RELOAD, WE CAN SUPPORT A MODULE THAT CAN BE COMPOSED. THINK THAT IF EVERY SCRIPT IS JUST A CONCATENATION OF JS, SO IT WORKS LIKE A STACK, A DIFFERENT LAYER, CAN BE THE THING THAT PIPES IT.