mirror of
https://github.com/TxtDot/vigi.git
synced 2024-11-21 11:06:21 +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]]
|
||||
name = "bytes"
|
||||
version = "1.6.1"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -603,9 +603,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "dalet"
|
||||
version = "1.0.0-pre6"
|
||||
version = "1.0.0-pre9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bc4c347533f8341633bd820799dea680f600e50891310b74bc914740681e8c2"
|
||||
checksum = "2095f83b5256dc9a981639c3250aba53c02736a3601c1e6b2c54c27ec786274a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"enum-procs",
|
||||
|
@ -3093,6 +3093,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
|
@ -3685,8 +3694,11 @@ dependencies = [
|
|||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
|
@ -3704,6 +3716,17 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
|
@ -4006,6 +4029,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|||
name = "vigi"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"dalet",
|
||||
"mime",
|
||||
"reqwest 0.12.5",
|
||||
|
@ -4013,7 +4037,9 @@ dependencies = [
|
|||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tokio",
|
||||
"tokio-gemini",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
|
|
@ -26,7 +26,14 @@ tauri = { version = "1", features = [
|
|||
] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
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"
|
||||
tokio-gemini = "0.1.0"
|
||||
|
|
|
@ -7,7 +7,7 @@ mod process_input;
|
|||
mod types;
|
||||
mod utils;
|
||||
|
||||
use tauri::async_runtime::Mutex;
|
||||
use tokio::sync::Mutex;
|
||||
use types::{VigiError, VigiJsState, VigiState};
|
||||
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 bytes::Bytes;
|
||||
use mime::Mime;
|
||||
use url::Url;
|
||||
|
||||
mod insecure_gemini_client;
|
||||
mod process_data;
|
||||
mod process_url;
|
||||
|
||||
use process_data::process_data;
|
||||
use process_url::process_url;
|
||||
|
||||
type Data = Vec<u8>;
|
||||
type ReqResult = (Mime, Data);
|
||||
type ReqResult = (Mime, Bytes);
|
||||
|
||||
pub async fn process_input(input: &String) -> Result<VigiOutput, VigiError> {
|
||||
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 std::str;
|
||||
|
||||
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()) {
|
||||
("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)?,
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
async fn process_text(data: String) -> VigiOutput {
|
||||
let mut truncated = data.clone();
|
||||
async fn process_text(data: &str) -> VigiOutput {
|
||||
let mut truncated = data.to_owned();
|
||||
truncated.truncate(50);
|
||||
|
||||
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 reqwest::header::CONTENT_TYPE;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use url::Url;
|
||||
|
||||
use crate::types::VigiError;
|
||||
|
||||
use super::ReqResult;
|
||||
use super::{insecure_gemini_client, ReqResult};
|
||||
|
||||
pub async fn process_url(url: Url) -> Result<ReqResult, VigiError> {
|
||||
let result = match url.scheme() {
|
||||
"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)?,
|
||||
};
|
||||
|
||||
|
@ -37,16 +39,21 @@ async fn process_http(url: String) -> Result<ReqResult, VigiError> {
|
|||
))
|
||||
}
|
||||
|
||||
// async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
|
||||
// let res = tokio_gemini::Client::default()
|
||||
// .request(&url)
|
||||
// .await
|
||||
// .map_err(|e| {
|
||||
// println!("{:#?}", e);
|
||||
// VigiError::Network
|
||||
// })?;
|
||||
async fn process_gemini(url: String) -> Result<ReqResult, VigiError> {
|
||||
let mut res = insecure_gemini_client::insecure_gemini_client()
|
||||
.request(&url)
|
||||
.await
|
||||
.map_err(|_| 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,
|
||||
InvalidMimeType,
|
||||
|
||||
TextIsNotUtf8,
|
||||
InvalidCharset,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
24
src/app.css
24
src/app.css
|
@ -1,3 +1,5 @@
|
|||
@import "tags.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
@ -12,14 +14,14 @@
|
|||
--min-bg: #96e28a;
|
||||
--max-bg: #193815;
|
||||
|
||||
--min-text: #bef5b5;
|
||||
--min-text: #82de73;
|
||||
--max-text: #fff;
|
||||
|
||||
/* Dark */
|
||||
/* --min-bg: #555;
|
||||
--max-bg: #000;
|
||||
|
||||
--min-text: #cfcfcf;
|
||||
--min-text: #555;
|
||||
--max-text: #fff; */
|
||||
}
|
||||
|
||||
|
@ -27,7 +29,7 @@
|
|||
/* Components */
|
||||
|
||||
body {
|
||||
@apply color-vigi-90 cursor-default;
|
||||
@apply color-vigi-90 cursor-default overflow-clip;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
|
@ -43,11 +45,11 @@ body {
|
|||
}
|
||||
|
||||
.main-window {
|
||||
@apply grow flex flex-col gap-3;
|
||||
@apply grow flex flex-col gap-3 w-3/4;
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
@ -110,7 +112,7 @@ body {
|
|||
}
|
||||
|
||||
.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 focus:color-vigi-50 hover:color-vigi-55;
|
||||
|
@ -160,24 +162,26 @@ input::placeholder {
|
|||
@apply color-vigi-60;
|
||||
}
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply rounded-xl color-vigi-70 bg-clip-content;
|
||||
border: 6px solid transparent;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@apply color-vigi-75;
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
@apply color-vigi-100;
|
||||
border: 4px solid transparent;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
<script lang="ts">
|
||||
import type { Root } from "@txtdot/dalet";
|
||||
import Block from "./Block.svelte";
|
||||
import Renderer from "./DaletlRenderer/Renderer.svelte";
|
||||
import { isLoading, state } from "$lib/stores";
|
||||
import type { VigiState } from "$lib/types";
|
||||
import GooLoadSpin from "$lib/icons/GooLoadSpin.svelte";
|
||||
import { slide } from "svelte/transition";
|
||||
import type { Root } from "@txtdot/dalet";
|
||||
import Block from "./Block.svelte";
|
||||
import Renderer from "./DaletlRenderer/Renderer.svelte";
|
||||
import { isLoading, state } from "$lib/stores";
|
||||
import type { VigiState } from "$lib/types";
|
||||
import GooLoadSpin from "$lib/icons/GooLoadSpin.svelte";
|
||||
import { slide } from "svelte/transition";
|
||||
|
||||
let loading = false;
|
||||
let loading = false;
|
||||
|
||||
let data: Root;
|
||||
let tabId = 0;
|
||||
let data: Root;
|
||||
let tabId = 0;
|
||||
|
||||
state.subscribe((st) => {
|
||||
data = (st as VigiState).current_data;
|
||||
state.subscribe((st) => {
|
||||
data = (st as VigiState).current_data;
|
||||
console.log("ada");
|
||||
if (!loading) {
|
||||
tabId = (st as VigiState).current_tab_index;
|
||||
}
|
||||
});
|
||||
|
||||
if (!loading) {
|
||||
tabId = (st as VigiState).current_tab_index;
|
||||
}
|
||||
});
|
||||
isLoading.subscribe((val) => {
|
||||
loading = val;
|
||||
|
||||
isLoading.subscribe((val) => {
|
||||
loading = val;
|
||||
|
||||
if (loading) {
|
||||
tabId = -1;
|
||||
}
|
||||
});
|
||||
if (loading) {
|
||||
tabId = -1;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Block className="browser-window">
|
||||
{#if loading}
|
||||
<div transition:slide>
|
||||
<GooLoadSpin />
|
||||
</div>
|
||||
{/if}
|
||||
{#if loading}
|
||||
<div transition:slide>
|
||||
<GooLoadSpin />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Renderer {data} />
|
||||
<Renderer {data} />
|
||||
</Block>
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<script lang="ts">
|
||||
import type { Body } from "@txtdot/dalet";
|
||||
import TagRenderer from "./TagRenderer.svelte";
|
||||
import Element from "./tags/Element.svelte";
|
||||
import type { Body } from "@txtdot/dalet";
|
||||
import TagRenderer from "./TagRenderer.svelte";
|
||||
|
||||
export let body: Body;
|
||||
export let body: Body;
|
||||
export let ifNull: any = undefined;
|
||||
</script>
|
||||
|
||||
{#if typeof body === "string"}
|
||||
<Element {body} />
|
||||
{#each body.split("\n") as line}
|
||||
{line} <br />
|
||||
{/each}
|
||||
{:else if body !== null}
|
||||
{#each body as tag}
|
||||
<TagRenderer {tag} />
|
||||
{/each}
|
||||
{#each body as tag}
|
||||
<TagRenderer {tag} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
{#if body === null && ifNull}
|
||||
{ifNull}
|
||||
{/if}
|
||||
|
|
|
@ -1,10 +1,43 @@
|
|||
<script lang="ts">
|
||||
import type { Tag } from "@txtdot/dalet";
|
||||
import Element from "./tags/Element.svelte";
|
||||
import type { Tag } from "@txtdot/dalet";
|
||||
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>
|
||||
|
||||
{#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}
|
||||
|
|
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">
|
||||
import type { Body, Tag } from "@txtdot/dalet";
|
||||
import BodyRenderer from "../BodyRenderer.svelte";
|
||||
import type { Tag } from "@txtdot/dalet";
|
||||
import BodyRenderer from "../BodyRenderer.svelte";
|
||||
|
||||
export let body: Body;
|
||||
export let tag: Tag;
|
||||
</script>
|
||||
|
||||
<section class="el">
|
||||
{#if typeof body === "string"}
|
||||
{#each body.split("\n") as line}
|
||||
{line} <br />
|
||||
{/each}
|
||||
{:else}
|
||||
<BodyRenderer {body} />
|
||||
{/if}
|
||||
<BodyRenderer body={tag.body} />
|
||||
</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: [
|
||||
plugin(({ addUtilities }) => {
|
||||
let utilities = {};
|
||||
|
||||
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}`] = {
|
||||
color: `color-mix(in var(--colorspace), var(--max-text) ${i}%, var(--min-text))`,
|
||||
"background-color": `color-mix(in var(--colorspace), var(--max-bg) ${i}%, var(--min-bg))`,
|
||||
color: text,
|
||||
"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