Typstify /chat_message to /chat
This commit is contained in:
parent
8bece23baf
commit
de7ae63a5e
13 changed files with 93 additions and 153 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -2746,7 +2746,6 @@ dependencies = [
|
||||||
"showbits-assets",
|
"showbits-assets",
|
||||||
"showbits-common",
|
"showbits-common",
|
||||||
"showbits-typst",
|
"showbits-typst",
|
||||||
"taffy",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ serde = { workspace = true }
|
||||||
showbits-assets = { workspace = true }
|
showbits-assets = { workspace = true }
|
||||||
showbits-common = { workspace = true }
|
showbits-common = { workspace = true }
|
||||||
showbits-typst = { workspace = true }
|
showbits-typst = { workspace = true }
|
||||||
taffy = { workspace = true }
|
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
@ -93,7 +92,7 @@ class Room:
|
||||||
|
|
||||||
print("Posting", data)
|
print("Posting", data)
|
||||||
try:
|
try:
|
||||||
requests.post(f"http://{self.addr}/chat_message", data=data)
|
requests.post(f"http://{self.addr}/chat", data=data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Oop:", e)
|
print("Oop:", e)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ class Room:
|
||||||
|
|
||||||
print("Posting", data)
|
print("Posting", data)
|
||||||
try:
|
try:
|
||||||
requests.post(f"http://{self.addr}/chat_message", data=data)
|
requests.post(f"http://{self.addr}/chat", data=data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Oop:", e)
|
print("Oop:", e)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use showbits_typst::Typst;
|
||||||
|
|
||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
pub mod cells;
|
pub mod cells;
|
||||||
|
pub mod chat;
|
||||||
pub mod egg;
|
pub mod egg;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
|
||||||
5
showbits-thermal-printer/src/documents/chat/data.json
Normal file
5
showbits-thermal-printer/src/documents/chat/data.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"username": "John Argbuckle aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||||
|
"feed": false
|
||||||
|
}
|
||||||
1
showbits-thermal-printer/src/documents/chat/lib
Symbolic link
1
showbits-thermal-printer/src/documents/chat/lib
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../lib
|
||||||
37
showbits-thermal-printer/src/documents/chat/main.typ
Normal file
37
showbits-thermal-printer/src/documents/chat/main.typ
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#import "lib/main.typ" as lib;
|
||||||
|
#show: it => lib.init(it)
|
||||||
|
|
||||||
|
#let data = json("data.json")
|
||||||
|
|
||||||
|
#let max_width = 32 * 8pt + 4pt
|
||||||
|
#let limit_width(body) = context {
|
||||||
|
let width = measure(body).width
|
||||||
|
if width > max_width { box(body, width: max_width) } else { body }
|
||||||
|
}
|
||||||
|
|
||||||
|
// This way, the top line of the username box looks better.
|
||||||
|
#v(4pt)
|
||||||
|
|
||||||
|
#par(hanging-indent: 32pt)[
|
||||||
|
#limit_width(
|
||||||
|
box(
|
||||||
|
height: 10pt,
|
||||||
|
clip: true,
|
||||||
|
stroke: 1pt + black,
|
||||||
|
inset: (x: 2pt),
|
||||||
|
outset: (y: 3pt + .5pt, x: -.5pt),
|
||||||
|
{
|
||||||
|
// Ensure all characters that fit on the line are displayed on the line.
|
||||||
|
// We don't want a half-empty box.
|
||||||
|
show regex("."): it => it + sym.zws
|
||||||
|
data.username
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
#h(-4pt)
|
||||||
|
#data.content
|
||||||
|
]
|
||||||
|
|
||||||
|
#if data.feed {
|
||||||
|
lib.feed
|
||||||
|
}
|
||||||
38
showbits-thermal-printer/src/documents/chat/mod.rs
Normal file
38
showbits-thermal-printer/src/documents/chat/mod.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use axum::{Form, extract::State};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
drawer::{Command, NewTypstDrawing},
|
||||||
|
server::Server,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Data {
|
||||||
|
username: String,
|
||||||
|
content: String,
|
||||||
|
feed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct FormData {
|
||||||
|
pub username: String,
|
||||||
|
pub content: String,
|
||||||
|
pub feed: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn post(server: State<Server>, Form(form): Form<FormData>) {
|
||||||
|
let data = Data {
|
||||||
|
username: form.username,
|
||||||
|
content: form.content,
|
||||||
|
feed: form.feed.unwrap_or(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
let typst = super::typst_with_lib()
|
||||||
|
.with_json("/data.json", &data)
|
||||||
|
.with_main_file(include_str!("main.typ"));
|
||||||
|
|
||||||
|
let _ = server
|
||||||
|
.tx
|
||||||
|
.send(Command::draw(NewTypstDrawing::new(typst)))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
mod backlog;
|
mod backlog;
|
||||||
mod chat_message;
|
|
||||||
mod new_typst;
|
mod new_typst;
|
||||||
|
|
||||||
use showbits_common::widgets::{FontStuff, HasFontStuff};
|
use showbits_common::widgets::{FontStuff, HasFontStuff};
|
||||||
|
|
@ -7,9 +6,7 @@ use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::persistent_printer::PersistentPrinter;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{backlog::BacklogDrawing, new_typst::NewTypstDrawing};
|
||||||
backlog::BacklogDrawing, chat_message::ChatMessageDrawing, new_typst::NewTypstDrawing,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
use showbits_common::{
|
|
||||||
Node, Tree, WidgetExt,
|
|
||||||
color::{BLACK, WHITE},
|
|
||||||
widgets::{Block, Text},
|
|
||||||
};
|
|
||||||
use taffy::{
|
|
||||||
AlignItems, Display, FlexDirection,
|
|
||||||
style_helpers::{length, percent},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::persistent_printer::PersistentPrinter;
|
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
|
||||||
|
|
||||||
pub struct ChatMessageDrawing {
|
|
||||||
pub username: String,
|
|
||||||
pub content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drawing for ChatMessageDrawing {
|
|
||||||
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
|
||||||
|
|
||||||
let max_username_width_in_chars = 32.0;
|
|
||||||
let max_username_height_in_lines = 3.0;
|
|
||||||
let max_content_height_in_lines = 16.0;
|
|
||||||
|
|
||||||
let username = Text::new()
|
|
||||||
.and_plain(&self.username)
|
|
||||||
.widget(&mut ctx.font_stuff)
|
|
||||||
.node()
|
|
||||||
.with_max_size_width(length(max_username_width_in_chars * 8.0))
|
|
||||||
.with_max_size_height(length(max_username_height_in_lines * 16.0))
|
|
||||||
.register(&mut tree)?;
|
|
||||||
|
|
||||||
let username = Block::new()
|
|
||||||
.with_border(BLACK)
|
|
||||||
.node()
|
|
||||||
.with_border_all(length(1.0))
|
|
||||||
.with_padding_horiz(length(1.0))
|
|
||||||
.with_flex_shrink(0.0) // Avoid wrapping
|
|
||||||
.and_child(username)
|
|
||||||
.register(&mut tree)?;
|
|
||||||
|
|
||||||
let content = if let Some(content) = self.content.strip_prefix("/me") {
|
|
||||||
let content = content.trim_start();
|
|
||||||
|
|
||||||
let content = Text::new()
|
|
||||||
.and_plain(content)
|
|
||||||
.widget(&mut ctx.font_stuff)
|
|
||||||
.node()
|
|
||||||
.with_max_size_height(length(max_content_height_in_lines * 16.0))
|
|
||||||
.register(&mut tree)?;
|
|
||||||
|
|
||||||
Block::new()
|
|
||||||
.with_border(BLACK)
|
|
||||||
.node()
|
|
||||||
.with_border_all(length(1.0))
|
|
||||||
.with_padding_horiz(length(1.0))
|
|
||||||
.and_child(content)
|
|
||||||
.register(&mut tree)?
|
|
||||||
} else {
|
|
||||||
let content = Text::new()
|
|
||||||
.and_plain(&self.content)
|
|
||||||
.widget(&mut ctx.font_stuff)
|
|
||||||
.node()
|
|
||||||
.with_max_size_height(length(max_content_height_in_lines * 16.0))
|
|
||||||
.register(&mut tree)?;
|
|
||||||
|
|
||||||
Node::empty()
|
|
||||||
.with_padding_vert(length(1.0))
|
|
||||||
.and_child(content)
|
|
||||||
.register(&mut tree)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let root = Node::empty()
|
|
||||||
.with_size_width(percent(1.0))
|
|
||||||
.with_padding_all(length(1.0))
|
|
||||||
.with_display(Display::Flex)
|
|
||||||
.with_flex_direction(FlexDirection::Row)
|
|
||||||
.with_align_items(Some(AlignItems::Start))
|
|
||||||
.with_gap_width(length(2.0))
|
|
||||||
.and_child(username)
|
|
||||||
.and_child(content)
|
|
||||||
.register(&mut tree)?;
|
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,8 +3,6 @@ use std::{fs, io::ErrorKind, path::PathBuf};
|
||||||
use anyhow::{Context, bail};
|
use anyhow::{Context, bail};
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
use jiff::Timestamp;
|
use jiff::Timestamp;
|
||||||
use showbits_common::Tree;
|
|
||||||
use taffy::{AvailableSpace, NodeId, Size};
|
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
|
@ -30,20 +28,6 @@ impl PersistentPrinter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_tree_to_image<C>(
|
|
||||||
tree: &mut Tree<C>,
|
|
||||||
ctx: &mut C,
|
|
||||||
root: NodeId,
|
|
||||||
) -> anyhow::Result<RgbaImage> {
|
|
||||||
let available = Size {
|
|
||||||
width: AvailableSpace::Definite(Printer::WIDTH as f32),
|
|
||||||
// TODO Maybe MinContent? If not, why not?
|
|
||||||
height: AvailableSpace::MaxContent,
|
|
||||||
};
|
|
||||||
|
|
||||||
tree.render(ctx, root, available)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_image_immediately(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
fn print_image_immediately(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
||||||
let Some(printer) = &mut self.printer else {
|
let Some(printer) = &mut self.printer else {
|
||||||
bail!("no printer found");
|
bail!("no printer found");
|
||||||
|
|
@ -93,17 +77,6 @@ impl PersistentPrinter {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_tree<C>(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree<C>,
|
|
||||||
ctx: &mut C,
|
|
||||||
root: NodeId,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let image = Self::render_tree_to_image(tree, ctx, root)?;
|
|
||||||
self.print_image(&image)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_backlog(&mut self) -> anyhow::Result<()> {
|
pub fn print_backlog(&mut self) -> anyhow::Result<()> {
|
||||||
// Don't try to print if the chances of success are zero.
|
// Don't try to print if the chances of success are zero.
|
||||||
if let Some(file) = &self.printer_file {
|
if let Some(file) = &self.printer_file {
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,13 @@ mod r#static;
|
||||||
pub mod statuscode;
|
pub mod statuscode;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Form, Router,
|
Router,
|
||||||
extract::{DefaultBodyLimit, State},
|
extract::DefaultBodyLimit,
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
|
||||||
use tokio::{net::TcpListener, sync::mpsc};
|
use tokio::{net::TcpListener, sync::mpsc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{documents, drawer::Command};
|
||||||
documents,
|
|
||||||
drawer::{ChatMessageDrawing, Command},
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::r#static::get_static_file;
|
use self::r#static::get_static_file;
|
||||||
|
|
||||||
|
|
@ -32,7 +28,10 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
|
||||||
"/cells",
|
"/cells",
|
||||||
post(documents::cells::post).fallback(get_static_file),
|
post(documents::cells::post).fallback(get_static_file),
|
||||||
)
|
)
|
||||||
.route("/chat_message", post(post_chat_message))
|
.route(
|
||||||
|
"/chat",
|
||||||
|
post(documents::chat::post).fallback(get_static_file),
|
||||||
|
)
|
||||||
.route("/egg", post(documents::egg::post).fallback(get_static_file))
|
.route("/egg", post(documents::egg::post).fallback(get_static_file))
|
||||||
.route(
|
.route(
|
||||||
"/image",
|
"/image",
|
||||||
|
|
@ -54,21 +53,3 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
|
||||||
axum::serve(listener, app).await?;
|
axum::serve(listener, app).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// /chat_message
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct PostChatMessageForm {
|
|
||||||
username: String,
|
|
||||||
content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn post_chat_message(server: State<Server>, request: Form<PostChatMessageForm>) {
|
|
||||||
let _ = server
|
|
||||||
.tx
|
|
||||||
.send(Command::draw(ChatMessageDrawing {
|
|
||||||
username: request.0.username,
|
|
||||||
content: request.0.content,
|
|
||||||
}))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue