mirror of
https://github.com/TxtDot/vigi.git
synced 2024-11-21 19:16:20 +03:00
feat: gemini support
This commit is contained in:
parent
15c43e3482
commit
d96b863ea8
26 changed files with 448 additions and 93 deletions
34
src-tauri/Cargo.lock
generated
34
src-tauri/Cargo.lock
generated
|
@ -258,9 +258,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.6.1"
|
version = "1.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -603,9 +603,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dalet"
|
name = "dalet"
|
||||||
version = "1.0.0-pre6"
|
version = "1.0.0-pre9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bc4c347533f8341633bd820799dea680f600e50891310b74bc914740681e8c2"
|
checksum = "2095f83b5256dc9a981639c3250aba53c02736a3601c1e6b2c54c27ec786274a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"enum-procs",
|
"enum-procs",
|
||||||
|
@ -3093,6 +3093,15 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
@ -3685,8 +3694,11 @@ dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3704,6 +3716,17 @@ dependencies = [
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-native-tls"
|
name = "tokio-native-tls"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -4006,6 +4029,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
name = "vigi"
|
name = "vigi"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
"dalet",
|
"dalet",
|
||||||
"mime",
|
"mime",
|
||||||
"reqwest 0.12.5",
|
"reqwest 0.12.5",
|
||||||
|
@ -4013,7 +4037,9 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
|
"tokio",
|
||||||
"tokio-gemini",
|
"tokio-gemini",
|
||||||
|
"tokio-rustls",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,14 @@ tauri = { version = "1", features = [
|
||||||
] }
|
] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
dalet = "1.0.0-pre6"
|
dalet = "1.0.0-pre9"
|
||||||
|
|
||||||
|
tokio = { version = "1.39.2", features = ["full"] }
|
||||||
|
tokio-rustls = { version = "0.26.0", default-features = false, features = [
|
||||||
|
"ring",
|
||||||
|
] }
|
||||||
|
|
||||||
|
bytes = "1.7.1"
|
||||||
|
|
||||||
reqwest = "0.12.5"
|
reqwest = "0.12.5"
|
||||||
tokio-gemini = "0.1.0"
|
tokio-gemini = "0.1.0"
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod process_input;
|
||||||
mod types;
|
mod types;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use tauri::async_runtime::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use types::{VigiError, VigiJsState, VigiState};
|
use types::{VigiError, VigiJsState, VigiState};
|
||||||
use utils::{read_or_create_jsonl, read_or_create_number};
|
use utils::{read_or_create_jsonl, read_or_create_number};
|
||||||
|
|
||||||
|
|
56
src-tauri/src/process_input/insecure_gemini_client.rs
Normal file
56
src-tauri/src/process_input/insecure_gemini_client.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use tokio_rustls::rustls::{
|
||||||
|
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
|
||||||
|
ClientConfig, SignatureScheme,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// TODO: update to secure version when supported
|
||||||
|
pub fn insecure_gemini_client() -> tokio_gemini::Client {
|
||||||
|
tokio_gemini::Client::from(
|
||||||
|
ClientConfig::builder()
|
||||||
|
.dangerous()
|
||||||
|
.with_custom_certificate_verifier(std::sync::Arc::new(NoCertVerification {}))
|
||||||
|
.with_no_client_auth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct NoCertVerification;
|
||||||
|
|
||||||
|
impl ServerCertVerifier for NoCertVerification {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
_end_entity: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
||||||
|
_intermediates: &[tokio_rustls::rustls::pki_types::CertificateDer<'_>],
|
||||||
|
_server_name: &tokio_rustls::rustls::pki_types::ServerName<'_>,
|
||||||
|
_ocsp_response: &[u8],
|
||||||
|
_now: tokio_rustls::rustls::pki_types::UnixTime,
|
||||||
|
) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
|
||||||
|
Ok(ServerCertVerified::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
||||||
|
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &tokio_rustls::rustls::pki_types::CertificateDer<'_>,
|
||||||
|
_dss: &tokio_rustls::rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
|
||||||
|
vec![
|
||||||
|
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||||
|
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||||
|
SignatureScheme::ECDSA_NISTP521_SHA512,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
use crate::types::{VigiError, VigiOutput};
|
use crate::types::{VigiError, VigiOutput};
|
||||||
|
use bytes::Bytes;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
mod insecure_gemini_client;
|
||||||
mod process_data;
|
mod process_data;
|
||||||
mod process_url;
|
mod process_url;
|
||||||
|
|
||||||
use process_data::process_data;
|
use process_data::process_data;
|
||||||
use process_url::process_url;
|
use process_url::process_url;
|
||||||
|
|
||||||
type Data = Vec<u8>;
|
type ReqResult = (Mime, Bytes);
|
||||||
type ReqResult = (Mime, Data);
|
|
||||||
|
|
||||||
pub async fn process_input(input: &String) -> Result<VigiOutput, VigiError> {
|
pub async fn process_input(input: &String) -> Result<VigiOutput, VigiError> {
|
||||||
let parsed = Url::parse(input);
|
let parsed = Url::parse(input);
|
||||||
|
|
|
@ -1,25 +1,41 @@
|
||||||
use dalet::{daletl::ToDaletlPage, typed::Tag::*};
|
use bytes::Bytes;
|
||||||
|
use dalet::{daletl::ToDaletlPage, parsers::gemtext::parse_gemtext, typed::Tag::*};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use crate::types::{VigiError, VigiOutput};
|
use crate::types::{VigiError, VigiOutput};
|
||||||
|
|
||||||
pub async fn process_data(mime: Mime, data: Vec<u8>) -> Result<VigiOutput, VigiError> {
|
pub async fn process_data(mime: Mime, data: Bytes) -> Result<VigiOutput, VigiError> {
|
||||||
let result = match (mime.type_().as_str(), mime.subtype().as_str()) {
|
let result = match (mime.type_().as_str(), mime.subtype().as_str()) {
|
||||||
("text", "plain") => {
|
("text", "plain") => {
|
||||||
process_text(String::from_utf8(data).map_err(|_| VigiError::TextIsNotUtf8)?).await
|
process_text(str::from_utf8(&data).map_err(|_| VigiError::InvalidCharset)?).await
|
||||||
|
}
|
||||||
|
("text", "gemini") => {
|
||||||
|
process_gemini(str::from_utf8(&data).map_err(|_| VigiError::InvalidCharset)?).await?
|
||||||
}
|
}
|
||||||
// ("text", "gemini") => {
|
|
||||||
// process_text(String::from_utf8(data).map_err(|_| VigiError::TextIsNotUtf8)?).await
|
|
||||||
// }
|
|
||||||
_ => Err(VigiError::UnsupportedMimeType)?,
|
_ => Err(VigiError::UnsupportedMimeType)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_text(data: String) -> VigiOutput {
|
async fn process_text(data: &str) -> VigiOutput {
|
||||||
let mut truncated = data.clone();
|
let mut truncated = data.to_owned();
|
||||||
truncated.truncate(50);
|
truncated.truncate(50);
|
||||||
|
|
||||||
VigiOutput::new(truncated, vec![El(data.into())].to_dl_page())
|
VigiOutput::new(truncated, vec![El(data.into())].to_dl_page())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn process_gemini(data: &str) -> Result<VigiOutput, VigiError> {
|
||||||
|
let mut truncated = data.to_owned();
|
||||||
|
truncated.truncate(50);
|
||||||
|
|
||||||
|
let res = VigiOutput::new(
|
||||||
|
truncated,
|
||||||
|
parse_gemtext(data)
|
||||||
|
.map_err(|_| VigiError::Parse)?
|
||||||
|
.to_dl_page(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
|
use bytes::Bytes;
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::types::VigiError;
|
use crate::types::VigiError;
|
||||||
|
|
||||||
use super::ReqResult;
|
use super::{insecure_gemini_client, ReqResult};
|
||||||
|
|
||||||
pub async fn process_url(url: Url) -> Result<ReqResult, VigiError> {
|
pub async fn process_url(url: Url) -> Result<ReqResult, VigiError> {
|
||||||
let result = match url.scheme() {
|
let result = match url.scheme() {
|
||||||
"http" | "https" => process_http(url.to_string()).await?,
|
"http" | "https" => process_http(url.to_string()).await?,
|
||||||
// "gemini" => process_gemini(url.to_string()).await?,
|
"gemini" => process_gemini(url.to_string()).await?,
|
||||||
_ => Err(VigiError::UnsupportedProtocol)?,
|
_ => Err(VigiError::UnsupportedProtocol)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,16 +39,21 @@ async fn process_http(url: String) -> Result<ReqResult, VigiError> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
|
async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
|
||||||
// let res = tokio_gemini::Client::default()
|
let mut res = insecure_gemini_client::insecure_gemini_client()
|
||||||
// .request(&url)
|
.request(&url)
|
||||||
// .await
|
.await
|
||||||
// .map_err(|e| {
|
.map_err(|_| VigiError::Network)?;
|
||||||
// println!("{:#?}", e);
|
|
||||||
// VigiError::Network
|
|
||||||
// })?;
|
|
||||||
|
|
||||||
// let mime_type = res.mime().map_err(|_| VigiError::InvalidMimeType)?;
|
let mime_type = res.mime().map_err(|_| VigiError::InvalidMimeType)?;
|
||||||
|
|
||||||
// Ok((mime_type, res.message().as_bytes().into()))
|
let mut buffer = Vec::new();
|
||||||
// }
|
|
||||||
|
let tls_stream = res.body();
|
||||||
|
tls_stream
|
||||||
|
.read_to_end(&mut buffer)
|
||||||
|
.await
|
||||||
|
.map_err(|_| VigiError::Network)?;
|
||||||
|
|
||||||
|
Ok((mime_type, Bytes::from(buffer).into()))
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub enum VigiError {
|
||||||
UnsupportedMimeType,
|
UnsupportedMimeType,
|
||||||
InvalidMimeType,
|
InvalidMimeType,
|
||||||
|
|
||||||
TextIsNotUtf8,
|
InvalidCharset,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
24
src/app.css
24
src/app.css
|
@ -1,3 +1,5 @@
|
||||||
|
@import "tags.css";
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
@ -12,14 +14,14 @@
|
||||||
--min-bg: #96e28a;
|
--min-bg: #96e28a;
|
||||||
--max-bg: #193815;
|
--max-bg: #193815;
|
||||||
|
|
||||||
--min-text: #bef5b5;
|
--min-text: #82de73;
|
||||||
--max-text: #fff;
|
--max-text: #fff;
|
||||||
|
|
||||||
/* Dark */
|
/* Dark */
|
||||||
/* --min-bg: #555;
|
/* --min-bg: #555;
|
||||||
--max-bg: #000;
|
--max-bg: #000;
|
||||||
|
|
||||||
--min-text: #cfcfcf;
|
--min-text: #555;
|
||||||
--max-text: #fff; */
|
--max-text: #fff; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@
|
||||||
/* Components */
|
/* Components */
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply color-vigi-90 cursor-default;
|
@apply color-vigi-90 cursor-default overflow-clip;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,11 +45,11 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window {
|
.main-window {
|
||||||
@apply grow flex flex-col gap-3;
|
@apply grow flex flex-col gap-3 w-3/4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.browser-window {
|
.browser-window {
|
||||||
@apply grow overflow-auto text-wrap select-text cursor-auto;
|
@apply grow overflow-y-auto text-wrap select-text cursor-auto overflow-x-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.window-controls {
|
.window-controls {
|
||||||
|
@ -110,7 +112,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
@apply px-2 py-1 rounded-xl grow;
|
@apply px-2 py-1 rounded-xl grow min-w-0;
|
||||||
|
|
||||||
@apply color-vigi-60 outline-none;
|
@apply color-vigi-60 outline-none;
|
||||||
@apply focus:color-vigi-50 hover:color-vigi-55;
|
@apply focus:color-vigi-50 hover:color-vigi-55;
|
||||||
|
@ -160,24 +162,26 @@ input::placeholder {
|
||||||
@apply color-vigi-60;
|
@apply color-vigi-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* width */
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 15px;
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Track */
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
@apply bg-transparent;
|
@apply bg-transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle */
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
@apply rounded-xl color-vigi-70 bg-clip-content;
|
@apply rounded-xl color-vigi-70 bg-clip-content;
|
||||||
border: 6px solid transparent;
|
border: 6px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle on hover */
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
@apply color-vigi-75;
|
@apply color-vigi-75;
|
||||||
border: 5px solid transparent;
|
border: 5px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:active {
|
||||||
|
@apply color-vigi-100;
|
||||||
|
border: 4px solid transparent;
|
||||||
|
}
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Root } from "@txtdot/dalet";
|
import type { Root } from "@txtdot/dalet";
|
||||||
import Block from "./Block.svelte";
|
import Block from "./Block.svelte";
|
||||||
import Renderer from "./DaletlRenderer/Renderer.svelte";
|
import Renderer from "./DaletlRenderer/Renderer.svelte";
|
||||||
import { isLoading, state } from "$lib/stores";
|
import { isLoading, state } from "$lib/stores";
|
||||||
import type { VigiState } from "$lib/types";
|
import type { VigiState } from "$lib/types";
|
||||||
import GooLoadSpin from "$lib/icons/GooLoadSpin.svelte";
|
import GooLoadSpin from "$lib/icons/GooLoadSpin.svelte";
|
||||||
import { slide } from "svelte/transition";
|
import { slide } from "svelte/transition";
|
||||||
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
let data: Root;
|
let data: Root;
|
||||||
let tabId = 0;
|
let tabId = 0;
|
||||||
|
|
||||||
state.subscribe((st) => {
|
state.subscribe((st) => {
|
||||||
data = (st as VigiState).current_data;
|
data = (st as VigiState).current_data;
|
||||||
|
console.log("ada");
|
||||||
|
if (!loading) {
|
||||||
|
tabId = (st as VigiState).current_tab_index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!loading) {
|
isLoading.subscribe((val) => {
|
||||||
tabId = (st as VigiState).current_tab_index;
|
loading = val;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
isLoading.subscribe((val) => {
|
if (loading) {
|
||||||
loading = val;
|
tabId = -1;
|
||||||
|
}
|
||||||
if (loading) {
|
});
|
||||||
tabId = -1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Block className="browser-window">
|
<Block className="browser-window">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<div transition:slide>
|
<div transition:slide>
|
||||||
<GooLoadSpin />
|
<GooLoadSpin />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Renderer {data} />
|
<Renderer {data} />
|
||||||
</Block>
|
</Block>
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Body } from "@txtdot/dalet";
|
import type { Body } from "@txtdot/dalet";
|
||||||
import TagRenderer from "./TagRenderer.svelte";
|
import TagRenderer from "./TagRenderer.svelte";
|
||||||
import Element from "./tags/Element.svelte";
|
|
||||||
|
|
||||||
export let body: Body;
|
export let body: Body;
|
||||||
|
export let ifNull: any = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if typeof body === "string"}
|
{#if typeof body === "string"}
|
||||||
<Element {body} />
|
{#each body.split("\n") as line}
|
||||||
|
{line} <br />
|
||||||
|
{/each}
|
||||||
{:else if body !== null}
|
{:else if body !== null}
|
||||||
{#each body as tag}
|
{#each body as tag}
|
||||||
<TagRenderer {tag} />
|
<TagRenderer {tag} />
|
||||||
{/each}
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if body === null && ifNull}
|
||||||
|
{ifNull}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,10 +1,43 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Tag } from "@txtdot/dalet";
|
import type { Tag } from "@txtdot/dalet";
|
||||||
import Element from "./tags/Element.svelte";
|
import Element from "./tags/Element.svelte";
|
||||||
|
import Heading from "./tags/Heading.svelte";
|
||||||
|
import Paragraph from "./tags/Paragraph.svelte";
|
||||||
|
import LineBreak from "./tags/LineBreak.svelte";
|
||||||
|
import UnorderedList from "./tags/UnorderedList.svelte";
|
||||||
|
import Link from "./tags/Link.svelte";
|
||||||
|
import NavLink from "./tags/NavLink.svelte";
|
||||||
|
import Button from "./tags/Button.svelte";
|
||||||
|
import NavButton from "./tags/NavButton.svelte";
|
||||||
|
import Blockquote from "./tags/Blockquote.svelte";
|
||||||
|
import Code from "./tags/Code.svelte";
|
||||||
|
import Pre from "./tags/Pre.svelte";
|
||||||
|
|
||||||
export let tag: Tag;
|
export let tag: Tag;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if tag.id === 0}
|
{#if tag.id === 0}
|
||||||
<Element body={tag.body} />
|
<Element {tag} />
|
||||||
|
{:else if tag.id === 1}
|
||||||
|
<Heading {tag} />
|
||||||
|
{:else if tag.id === 2}
|
||||||
|
<Paragraph {tag} />
|
||||||
|
{:else if tag.id === 3}
|
||||||
|
<LineBreak />
|
||||||
|
{:else if tag.id === 4}
|
||||||
|
<UnorderedList {tag} />
|
||||||
|
{:else if tag.id === 7}
|
||||||
|
<Link {tag} />
|
||||||
|
{:else if tag.id === 8}
|
||||||
|
<NavLink {tag} />
|
||||||
|
{:else if tag.id === 9}
|
||||||
|
<Button {tag} />
|
||||||
|
{:else if tag.id === 10}
|
||||||
|
<NavButton {tag} />
|
||||||
|
{:else if tag.id === 18}
|
||||||
|
<Blockquote {tag} />
|
||||||
|
{:else if tag.id === 28}
|
||||||
|
<Code {tag} />
|
||||||
|
{:else if tag.id === 29}
|
||||||
|
<Pre {tag} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
10
src/lib/components/DaletlRenderer/tags/Blockquote.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/Blockquote.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="bq">
|
||||||
|
<BodyRenderer body={tag.body} />
|
||||||
|
</div>
|
10
src/lib/components/DaletlRenderer/tags/Button.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/Button.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button class="btn">
|
||||||
|
<BodyRenderer body={tag.body} ifNull={tag.argument} />
|
||||||
|
</button>
|
11
src/lib/components/DaletlRenderer/tags/Code.svelte
Normal file
11
src/lib/components/DaletlRenderer/tags/Code.svelte
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
export let body = tag.body as string;
|
||||||
|
export let argument = tag.argument as string | undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<pre class={`pre code${argument ? ` lang-${argument}` : ""}`}><code>{body}</code
|
||||||
|
></pre>
|
|
@ -1,16 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Body, Tag } from "@txtdot/dalet";
|
import type { Tag } from "@txtdot/dalet";
|
||||||
import BodyRenderer from "../BodyRenderer.svelte";
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
export let body: Body;
|
export let tag: Tag;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="el">
|
<section class="el">
|
||||||
{#if typeof body === "string"}
|
<BodyRenderer body={tag.body} />
|
||||||
{#each body.split("\n") as line}
|
|
||||||
{line} <br />
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<BodyRenderer {body} />
|
|
||||||
{/if}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
23
src/lib/components/DaletlRenderer/tags/Heading.svelte
Normal file
23
src/lib/components/DaletlRenderer/tags/Heading.svelte
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Argument, Body, Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
|
||||||
|
let body = tag.body as string;
|
||||||
|
let argument = tag.argument as number | null;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if argument === 2}
|
||||||
|
<h2 class="h2">{body}</h2>
|
||||||
|
{:else if argument === 3}
|
||||||
|
<h3 class="h3">{body}</h3>
|
||||||
|
{:else if argument === 4}
|
||||||
|
<h4 class="h4">{body}</h4>
|
||||||
|
{:else if argument === 5}
|
||||||
|
<h5 class="h5">{body}</h5>
|
||||||
|
{:else if argument === 6}
|
||||||
|
<h6 class="h6">{body}</h6>
|
||||||
|
{:else}
|
||||||
|
<h1 class="h1">{body}</h1>
|
||||||
|
{/if}
|
1
src/lib/components/DaletlRenderer/tags/LineBreak.svelte
Normal file
1
src/lib/components/DaletlRenderer/tags/LineBreak.svelte
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<br />
|
10
src/lib/components/DaletlRenderer/tags/Link.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/Link.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button class="link">
|
||||||
|
<BodyRenderer body={tag.body} ifNull={tag.argument} />
|
||||||
|
</button>
|
10
src/lib/components/DaletlRenderer/tags/NavButton.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/NavButton.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button class="navbtn">
|
||||||
|
<BodyRenderer body={tag.body} ifNull={tag.argument} />
|
||||||
|
</button>
|
10
src/lib/components/DaletlRenderer/tags/NavLink.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/NavLink.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button class="navlink">
|
||||||
|
<BodyRenderer body={tag.body} ifNull={tag.argument} />
|
||||||
|
</button>
|
10
src/lib/components/DaletlRenderer/tags/Paragraph.svelte
Normal file
10
src/lib/components/DaletlRenderer/tags/Paragraph.svelte
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p class="p">
|
||||||
|
<BodyRenderer body={tag.body} />
|
||||||
|
</p>
|
9
src/lib/components/DaletlRenderer/tags/Pre.svelte
Normal file
9
src/lib/components/DaletlRenderer/tags/Pre.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
export let body = tag.body as string;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<pre class="pre">{body}</pre>
|
15
src/lib/components/DaletlRenderer/tags/UnorderedList.svelte
Normal file
15
src/lib/components/DaletlRenderer/tags/UnorderedList.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Tag } from "@txtdot/dalet";
|
||||||
|
import BodyRenderer from "../BodyRenderer.svelte";
|
||||||
|
import TagRenderer from "../TagRenderer.svelte";
|
||||||
|
|
||||||
|
export let tag: Tag;
|
||||||
|
|
||||||
|
let body = tag.body as Tag[];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ul class="ul">
|
||||||
|
{#each body as tag}
|
||||||
|
<li class="ul-li"><TagRenderer {tag} /></li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
65
src/tags.css
Normal file
65
src/tags.css
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
.headings {
|
||||||
|
@apply font-bold my-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h1 {
|
||||||
|
@apply text-3xl headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h2 {
|
||||||
|
@apply text-2xl headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h3 {
|
||||||
|
@apply text-xl headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h4 {
|
||||||
|
@apply text-lg headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5 {
|
||||||
|
@apply text-base headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h6 {
|
||||||
|
@apply text-sm headings;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p {
|
||||||
|
@apply my-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn,
|
||||||
|
.navbtn {
|
||||||
|
@apply p-1 rounded-lg;
|
||||||
|
@apply ease-out duration-150;
|
||||||
|
|
||||||
|
@apply hover:color-vigi-90;
|
||||||
|
@apply cursor-pointer;
|
||||||
|
|
||||||
|
@apply color-vigi-70 active:color-vigi-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link,
|
||||||
|
.navlink {
|
||||||
|
@apply underline font-semibold;
|
||||||
|
/* @apply text-bg-vigi-90 hover:text-bg-vigi-95 active:text-bg-vigi-100; */
|
||||||
|
@apply text-vigi-30 hover:text-vigi-20 active:text-vigi-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ul {
|
||||||
|
@apply list-disc list-inside ms-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pre {
|
||||||
|
@apply font-mono;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
@apply color-vigi-50 p-4 rounded-xl overflow-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bq {
|
||||||
|
@apply color-vigi-50 p-4 rounded-xl border-s-4 text-wrap;
|
||||||
|
}
|
|
@ -13,10 +13,31 @@ export default {
|
||||||
plugins: [
|
plugins: [
|
||||||
plugin(({ addUtilities }) => {
|
plugin(({ addUtilities }) => {
|
||||||
let utilities = {};
|
let utilities = {};
|
||||||
|
|
||||||
for (let i = 0; i <= 100; i += 5) {
|
for (let i = 0; i <= 100; i += 5) {
|
||||||
|
let text = `color-mix(in var(--colorspace), var(--max-text) ${i}%, var(--min-text))`;
|
||||||
|
let bg = `color-mix(in var(--colorspace), var(--max-bg) ${i}%, var(--min-bg))`;
|
||||||
|
|
||||||
utilities[`.color-vigi-${i}`] = {
|
utilities[`.color-vigi-${i}`] = {
|
||||||
color: `color-mix(in var(--colorspace), var(--max-text) ${i}%, var(--min-text))`,
|
color: text,
|
||||||
"background-color": `color-mix(in var(--colorspace), var(--max-bg) ${i}%, var(--min-bg))`,
|
"background-color": bg,
|
||||||
|
"border-color": text,
|
||||||
|
};
|
||||||
|
|
||||||
|
utilities[`.text-vigi-${i}`] = {
|
||||||
|
color: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
utilities[`.text-bg-vigi-${i}`] = {
|
||||||
|
color: bg,
|
||||||
|
};
|
||||||
|
|
||||||
|
utilities[`.bg-vigi-${i}`] = {
|
||||||
|
"background-color": bg,
|
||||||
|
};
|
||||||
|
|
||||||
|
utilities[`.bg-text-vigi-${i}`] = {
|
||||||
|
"background-color": text,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue