chore: Dioxus-0.7-Projekt mit Tailwind-Setup aufsetzen
Cargo-, Dioxus- und Tailwind-Konfiguration, Clippy-Regeln, Assets und Stylesheets (tokens/base/layout/components). Cargo.lock wird eingecheckt, da es sich um ein Binary-Crate handelt. node_modules/ und .idea/ per .gitignore ausgeschlossen.
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target
|
||||
.DS_Store
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# Dioxus build output
|
||||
/dist
|
||||
|
||||
# Node (tailwind toolchain)
|
||||
node_modules/
|
||||
|
||||
# IDE
|
||||
/.idea
|
||||
265
AGENTS.md
Normal file
265
AGENTS.md
Normal file
@@ -0,0 +1,265 @@
|
||||
You are an expert [0.7 Dioxus](https://dioxuslabs.com/learn/0.7) assistant. Dioxus 0.7 changes every api in dioxus. Only use this up to date documentation. `cx`, `Scope`, and `use_state` are gone
|
||||
|
||||
Provide concise code examples with detailed descriptions
|
||||
|
||||
# Dioxus Dependency
|
||||
|
||||
You can add Dioxus to your `Cargo.toml` like this:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
dioxus = { version = "0.7.1" }
|
||||
|
||||
[features]
|
||||
default = ["web", "webview", "server"]
|
||||
web = ["dioxus/web"]
|
||||
webview = ["dioxus/desktop"]
|
||||
server = ["dioxus/server"]
|
||||
```
|
||||
|
||||
# Launching your application
|
||||
|
||||
You need to create a main function that sets up the Dioxus runtime and mounts your root component.
|
||||
|
||||
```rust
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
rsx! { "Hello, Dioxus!" }
|
||||
}
|
||||
```
|
||||
|
||||
Then serve with `dx serve`:
|
||||
|
||||
```sh
|
||||
curl -sSL http://dioxus.dev/install.sh | sh
|
||||
dx serve
|
||||
```
|
||||
|
||||
# UI with RSX
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
div {
|
||||
class: "container", // Attribute
|
||||
color: "red", // Inline styles
|
||||
width: if condition { "100%" }, // Conditional attributes
|
||||
"Hello, Dioxus!"
|
||||
}
|
||||
// Prefer loops over iterators
|
||||
for i in 0..5 {
|
||||
div { "{i}" } // use elements or components directly in loops
|
||||
}
|
||||
if condition {
|
||||
div { "Condition is true!" } // use elements or components directly in conditionals
|
||||
}
|
||||
|
||||
{children} // Expressions are wrapped in brace
|
||||
{(0..5).map(|i| rsx! { span { "Item {i}" } })} // Iterators must be wrapped in braces
|
||||
}
|
||||
```
|
||||
|
||||
# Assets
|
||||
|
||||
The asset macro can be used to link to local files to use in your project. All links start with `/` and are relative to the root of your project.
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
img {
|
||||
src: asset!("/assets/image.png"),
|
||||
alt: "An image",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Styles
|
||||
|
||||
The `document::Stylesheet` component will inject the stylesheet into the `<head>` of the document
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
document::Stylesheet {
|
||||
href: asset!("/assets/styles.css"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Components
|
||||
|
||||
Components are the building blocks of apps
|
||||
|
||||
* Component are functions annotated with the `#[component]` macro.
|
||||
* The function name must start with a capital letter or contain an underscore.
|
||||
* A component re-renders only under two conditions:
|
||||
1. Its props change (as determined by `PartialEq`).
|
||||
2. An internal reactive state it depends on is updated.
|
||||
|
||||
```rust
|
||||
#[component]
|
||||
fn Input(mut value: Signal<String>) -> Element {
|
||||
rsx! {
|
||||
input {
|
||||
value,
|
||||
oninput: move |e| {
|
||||
*value.write() = e.value();
|
||||
},
|
||||
onkeydown: move |e| {
|
||||
if e.key() == Key::Enter {
|
||||
value.write().clear();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each component accepts function arguments (props)
|
||||
|
||||
* Props must be owned values, not references. Use `String` and `Vec<T>` instead of `&str` or `&[T]`.
|
||||
* Props must implement `PartialEq` and `Clone`.
|
||||
* To make props reactive and copy, you can wrap the type in `ReadOnlySignal`. Any reactive state like memos and resources that read `ReadOnlySignal` props will automatically re-run when the prop changes.
|
||||
|
||||
# State
|
||||
|
||||
A signal is a wrapper around a value that automatically tracks where it's read and written. Changing a signal's value causes code that relies on the signal to rerun.
|
||||
|
||||
## Local State
|
||||
|
||||
The `use_signal` hook creates state that is local to a single component. You can call the signal like a function (e.g. `my_signal()`) to clone the value, or use `.read()` to get a reference. `.write()` gets a mutable reference to the value.
|
||||
|
||||
Use `use_memo` to create a memoized value that recalculates when its dependencies change. Memos are useful for expensive calculations that you don't want to repeat unnecessarily.
|
||||
|
||||
```rust
|
||||
#[component]
|
||||
fn Counter() -> Element {
|
||||
let mut count = use_signal(|| 0);
|
||||
let mut doubled = use_memo(move || count() * 2); // doubled will re-run when count changes because it reads the signal
|
||||
|
||||
rsx! {
|
||||
h1 { "Count: {count}" } // Counter will re-render when count changes because it reads the signal
|
||||
h2 { "Doubled: {doubled}" }
|
||||
button {
|
||||
onclick: move |_| *count.write() += 1, // Writing to the signal rerenders Counter
|
||||
"Increment"
|
||||
}
|
||||
button {
|
||||
onclick: move |_| count.with_mut(|count| *count += 1), // use with_mut to mutate the signal
|
||||
"Increment with with_mut"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Context API
|
||||
|
||||
The Context API allows you to share state down the component tree. A parent provides the state using `use_context_provider`, and any child can access it with `use_context`
|
||||
|
||||
```rust
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
let mut theme = use_signal(|| "light".to_string());
|
||||
use_context_provider(|| theme); // Provide a type to children
|
||||
rsx! { Child {} }
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Child() -> Element {
|
||||
let theme = use_context::<Signal<String>>(); // Consume the same type
|
||||
rsx! {
|
||||
div {
|
||||
"Current theme: {theme}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Async
|
||||
|
||||
For state that depends on an asynchronous operation (like a network request), Dioxus provides a hook called `use_resource`. This hook manages the lifecycle of the async task and provides the result to your component.
|
||||
|
||||
* The `use_resource` hook takes an `async` closure. It re-runs this closure whenever any signals it depends on (reads) are updated
|
||||
* The `Resource` object returned can be in several states when read:
|
||||
1. `None` if the resource is still loading
|
||||
2. `Some(value)` if the resource has successfully loaded
|
||||
|
||||
```rust
|
||||
let mut dog = use_resource(move || async move {
|
||||
// api request
|
||||
});
|
||||
|
||||
match dog() {
|
||||
Some(dog_info) => rsx! { Dog { dog_info } },
|
||||
None => rsx! { "Loading..." },
|
||||
}
|
||||
```
|
||||
|
||||
# Routing
|
||||
|
||||
All possible routes are defined in a single Rust `enum` that derives `Routable`. Each variant represents a route and is annotated with `#[route("/path")]`. Dynamic Segments can capture parts of the URL path as parameters by using `:name` in the route string. These become fields in the enum variant.
|
||||
|
||||
The `Router<Route> {}` component is the entry point that manages rendering the correct component for the current URL.
|
||||
|
||||
You can use the `#[layout(NavBar)]` to create a layout shared between pages and place an `Outlet<Route> {}` inside your layout component. The child routes will be rendered in the outlet.
|
||||
|
||||
```rust
|
||||
#[derive(Routable, Clone, PartialEq)]
|
||||
enum Route {
|
||||
#[layout(NavBar)] // This will use NavBar as the layout for all routes
|
||||
#[route("/")]
|
||||
Home {},
|
||||
#[route("/blog/:id")] // Dynamic segment
|
||||
BlogPost { id: i32 },
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn NavBar() -> Element {
|
||||
rsx! {
|
||||
a { href: "/", "Home" }
|
||||
Outlet<Route> {} // Renders Home or BlogPost
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
rsx! { Router::<Route> {} }
|
||||
}
|
||||
```
|
||||
|
||||
```toml
|
||||
dioxus = { version = "0.7.1", features = ["router"] }
|
||||
```
|
||||
|
||||
# Fullstack
|
||||
|
||||
Fullstack enables server rendering and ipc calls. It uses Cargo features (`server` and a client feature like `web`) to split the code into a server and client binaries.
|
||||
|
||||
```toml
|
||||
dioxus = { version = "0.7.1", features = ["fullstack"] }
|
||||
```
|
||||
|
||||
## Server Functions
|
||||
|
||||
Use the `#[post]` / `#[get]` macros to define an `async` function that will only run on the server. On the server, this macro generates an API endpoint. On the client, it generates a function that makes an HTTP request to that endpoint.
|
||||
|
||||
```rust
|
||||
#[post("/api/double/:path/&query")]
|
||||
async fn double_server(number: i32, path: String, query: i32) -> Result<i32, ServerFnError> {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
Ok(number * 2)
|
||||
}
|
||||
```
|
||||
|
||||
## Hydration
|
||||
|
||||
Hydration is the process of making a server-rendered HTML page interactive on the client. The server sends the initial HTML, and then the client-side runs, attaches event listeners, and takes control of future rendering.
|
||||
|
||||
### Errors
|
||||
The initial UI rendered by the component on the client must be identical to the UI rendered on the server.
|
||||
|
||||
* Use the `use_server_future` hook instead of `use_resource`. It runs the future on the server, serializes the result, and sends it to the client, ensuring the client has the data immediately for its first render.
|
||||
* Any code that relies on browser-specific APIs (like accessing `localStorage`) must be run *after* hydration. Place this code inside a `use_effect` hook.
|
||||
8033
Cargo.lock
generated
Normal file
8033
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "dionotes-client"
|
||||
version = "0.1.0"
|
||||
authors = ["MDietermann <marvin.dietermann@hive-brain.de>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus = { version = "0.7.1", features = ["router"] }
|
||||
dioxus-code = "0.1.2"
|
||||
dioxus-code-editor = { version = "0.1.2", features = ["all-languages"] }
|
||||
strum = { version = "0.28.0", features = ["strum_macros", "derive"] }
|
||||
strum_macros = "0.28.0"
|
||||
|
||||
[features]
|
||||
default = ["desktop"]
|
||||
web = ["dioxus/web"]
|
||||
desktop = ["dioxus/desktop"]
|
||||
mobile = ["dioxus/mobile"]
|
||||
21
Dioxus.toml
Normal file
21
Dioxus.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[application]
|
||||
|
||||
[web.app]
|
||||
|
||||
# HTML title tag content
|
||||
title = "dionotes-client"
|
||||
|
||||
# include `assets` in web platform
|
||||
[web.resource]
|
||||
|
||||
# Additional CSS style files
|
||||
style = []
|
||||
|
||||
# Additional JavaScript files
|
||||
script = []
|
||||
|
||||
[web.resource.dev]
|
||||
|
||||
# Javascript code file
|
||||
# serve: [dev-server] only
|
||||
script = []
|
||||
51
README.md
Normal file
51
README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Development
|
||||
|
||||
Your new bare-bones project includes minimal organization with a single `main.rs` file and a few assets.
|
||||
|
||||
```
|
||||
project/
|
||||
├─ assets/ # Any assets that are used by the app should be placed here
|
||||
├─ src/
|
||||
│ ├─ main.rs # main.rs is the entry point to your application and currently contains all components for the app
|
||||
├─ Cargo.toml # The Cargo.toml file defines the dependencies and feature flags for your project
|
||||
```
|
||||
|
||||
### Automatic Tailwind (Dioxus 0.7+)
|
||||
|
||||
As of Dioxus 0.7, there no longer is a need to manually install tailwind. Simply `dx serve` and you're good to go!
|
||||
|
||||
Automatic tailwind is supported by checking for a file called `tailwind.css` in your app's manifest directory (next to Cargo.toml). To customize the file, use the dioxus.toml:
|
||||
|
||||
```toml
|
||||
[application]
|
||||
tailwind_input = "my.css"
|
||||
tailwind_output = "assets/out.css" # also customize the location of the out file!
|
||||
```
|
||||
|
||||
### Tailwind Manual Install
|
||||
|
||||
To use tailwind plugins or manually customize tailwind, you can can install the Tailwind CLI and use it directly.
|
||||
|
||||
### Tailwind
|
||||
1. Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
|
||||
2. Install the Tailwind CSS CLI: https://tailwindcss.com/docs/installation/tailwind-cli
|
||||
3. Run the following command in the root of the project to start the Tailwind CSS compiler:
|
||||
|
||||
```bash
|
||||
npx @tailwindcss/cli -i ./input.css -o ./assets/tailwind.css --watch
|
||||
```
|
||||
|
||||
### Serving Your App
|
||||
|
||||
Run the following command in the root of your project to start developing with the default platform:
|
||||
|
||||
```bash
|
||||
dx serve
|
||||
```
|
||||
|
||||
To run for a different platform, use the `--platform platform` flag. E.g.
|
||||
```bash
|
||||
dx serve --platform desktop
|
||||
```
|
||||
|
||||
|
||||
172
assets/css/base.css
Normal file
172
assets/css/base.css
Normal file
@@ -0,0 +1,172 @@
|
||||
/* ==========================================================================
|
||||
Baseline — reset and element defaults. No component styling below this line.
|
||||
========================================================================== */
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
color-scheme: dark;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* App chrome should not feel like a document. */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
background-color: var(--dn-bg-app);
|
||||
color: var(--dn-fg);
|
||||
font-family: var(--dn-font-ui);
|
||||
font-size: var(--dn-text-base);
|
||||
line-height: var(--dn-leading-normal);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
overflow: hidden; /* the editor and sidebar scroll, the shell does not */
|
||||
}
|
||||
|
||||
/* Text the user is meant to read or copy opts back in. */
|
||||
input,
|
||||
textarea,
|
||||
[contenteditable],
|
||||
.selectable {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
/* --- Typographic elements ------------------------------------------------- */
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: 600;
|
||||
line-height: var(--dn-leading-tight);
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
h1 { font-size: var(--dn-text-xl); }
|
||||
h2 { font-size: var(--dn-text-lg); }
|
||||
h3 { font-size: var(--dn-text-md); }
|
||||
h4 { font-size: var(--dn-text-base); }
|
||||
|
||||
p {
|
||||
text-wrap: pretty;
|
||||
max-width: var(--dn-measure);
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: var(--dn-text-xs);
|
||||
color: var(--dn-fg-muted);
|
||||
}
|
||||
|
||||
code, kbd, samp, pre {
|
||||
font-family: var(--dn-font-code);
|
||||
font-size: 0.9375em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px solid var(--dn-border);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--dn-accent);
|
||||
text-decoration-color: var(--dn-accent-muted);
|
||||
text-underline-offset: 2px;
|
||||
transition: color var(--dn-dur-fast) var(--dn-ease);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
/* --- Form and media defaults ---------------------------------------------- */
|
||||
|
||||
img, svg, video, canvas {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
button, input, select, textarea {
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
button {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: var(--dn-fg-faint);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--dn-accent-muted);
|
||||
color: var(--dn-fg);
|
||||
}
|
||||
|
||||
/* --- Focus ---------------------------------------------------------------- */
|
||||
|
||||
:focus-visible {
|
||||
outline: 2px solid var(--dn-accent);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--dn-radius-sm);
|
||||
}
|
||||
|
||||
:focus:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* --- Scrollbars ------------------------------------------------------------ */
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--dn-border-strong) transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--dn-border);
|
||||
border: 3px solid transparent;
|
||||
background-clip: content-box;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--dn-border-strong);
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
/* --- Reduced motion --------------------------------------------------------- */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
205
assets/css/components.css
Normal file
205
assets/css/components.css
Normal file
@@ -0,0 +1,205 @@
|
||||
/* ==========================================================================
|
||||
Components — the things you click. Each rule owns one class.
|
||||
========================================================================== */
|
||||
|
||||
/* --- File rail entry -------------------------------------------------------
|
||||
An active file is marked by a rule at the left edge, the way an editor marks
|
||||
the current line — not by a filled pill.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
.file-selector {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: var(--dn-space-1) var(--dn-space-2) var(--dn-space-1) var(--dn-space-3);
|
||||
border-radius: var(--dn-radius-sm);
|
||||
color: var(--dn-fg-muted);
|
||||
font-family: var(--dn-font-code);
|
||||
font-size: var(--dn-text-sm);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color var(--dn-dur-fast) var(--dn-ease),
|
||||
background-color var(--dn-dur-fast) var(--dn-ease);
|
||||
}
|
||||
|
||||
.file-selector::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset-block: 3px;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
border-radius: 999px;
|
||||
background: var(--dn-accent);
|
||||
transform: scaleY(0);
|
||||
transition: transform var(--dn-dur) var(--dn-ease);
|
||||
}
|
||||
|
||||
.file-selector:hover {
|
||||
background: var(--dn-bg-raised);
|
||||
color: var(--dn-fg);
|
||||
}
|
||||
|
||||
.file-selector.active {
|
||||
color: var(--dn-fg);
|
||||
background: var(--dn-bg-raised);
|
||||
}
|
||||
|
||||
.file-selector.active::before {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
/* --- Workspace selector tab ------------------------------------------------- */
|
||||
|
||||
.workspace-selector {
|
||||
padding: var(--dn-space-2) var(--dn-space-4);
|
||||
border-radius: var(--dn-radius);
|
||||
color: var(--dn-fg-muted);
|
||||
font-size: var(--dn-text-base);
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: color var(--dn-dur-fast) var(--dn-ease),
|
||||
background-color var(--dn-dur-fast) var(--dn-ease);
|
||||
}
|
||||
|
||||
.workspace-selector:hover {
|
||||
color: var(--dn-fg);
|
||||
background: var(--dn-bg-raised);
|
||||
}
|
||||
|
||||
.workspace-selector.active {
|
||||
color: var(--dn-fg-on-accent);
|
||||
background: var(--dn-accent);
|
||||
}
|
||||
|
||||
/* --- Buttons ---------------------------------------------------------------- */
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--dn-space-2);
|
||||
padding: var(--dn-space-2) var(--dn-space-4);
|
||||
border: 1px solid var(--dn-border-strong);
|
||||
border-radius: var(--dn-radius);
|
||||
background: var(--dn-bg-raised);
|
||||
color: var(--dn-fg);
|
||||
font-size: var(--dn-text-base);
|
||||
font-weight: 500;
|
||||
transition: background-color var(--dn-dur-fast) var(--dn-ease),
|
||||
border-color var(--dn-dur-fast) var(--dn-ease);
|
||||
}
|
||||
|
||||
.btn:hover:not(:disabled) {
|
||||
background: var(--dn-bg-overlay);
|
||||
border-color: var(--ctp-surface-2);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--dn-accent);
|
||||
border-color: var(--dn-accent);
|
||||
color: var(--dn-fg-on-accent);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--dn-accent-hover);
|
||||
border-color: var(--dn-accent-hover);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.btn-ghost:hover:not(:disabled) {
|
||||
background: var(--dn-bg-raised);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
/* --- Text input --------------------------------------------------------------
|
||||
Workspace names, file names, search.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: var(--dn-space-2) var(--dn-space-3);
|
||||
border: 1px solid var(--dn-border-strong);
|
||||
border-radius: var(--dn-radius);
|
||||
background: var(--dn-bg-well);
|
||||
color: var(--dn-fg);
|
||||
font-family: var(--dn-font-code);
|
||||
font-size: var(--dn-text-base);
|
||||
transition: border-color var(--dn-dur-fast) var(--dn-ease);
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
border-color: var(--dn-accent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.input:focus-visible {
|
||||
outline: 2px solid var(--dn-accent-muted);
|
||||
outline-offset: 0;
|
||||
}
|
||||
|
||||
/* --- Field ------------------------------------------------------------------- */
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--dn-space-2);
|
||||
}
|
||||
|
||||
.field-label {
|
||||
color: var(--dn-fg-muted);
|
||||
font-family: var(--dn-font-code);
|
||||
font-size: var(--dn-text-xs);
|
||||
letter-spacing: var(--dn-tracking-caps);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
color: var(--dn-fg-faint);
|
||||
font-size: var(--dn-text-xs);
|
||||
}
|
||||
|
||||
.field-error {
|
||||
color: var(--dn-danger);
|
||||
font-size: var(--dn-text-xs);
|
||||
}
|
||||
|
||||
/* --- Empty state --------------------------------------------------------------
|
||||
An empty screen is an invitation to act, not a shrug.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--dn-space-3);
|
||||
height: 100%;
|
||||
padding: var(--dn-space-7) var(--dn-space-4);
|
||||
color: var(--dn-fg-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-state-title {
|
||||
color: var(--dn-fg);
|
||||
font-size: var(--dn-text-md);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* --- Utilities ---------------------------------------------------------------- */
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
94
assets/css/layout.css
Normal file
94
assets/css/layout.css
Normal file
@@ -0,0 +1,94 @@
|
||||
/* ==========================================================================
|
||||
Layout — app shell and the two top-level screens. Structure only, no skin.
|
||||
========================================================================== */
|
||||
|
||||
#main {
|
||||
height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* --- Workspace selector ----------------------------------------------------
|
||||
The pre-editor screen: pick or create a workspace. Centered, deliberately
|
||||
sparse — it has exactly one job.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
.workspace-selector-layout {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--dn-space-6);
|
||||
padding: var(--dn-space-6);
|
||||
}
|
||||
|
||||
.workspace-selector-links {
|
||||
display: flex;
|
||||
gap: var(--dn-space-1);
|
||||
padding: var(--dn-space-1);
|
||||
background: var(--dn-bg-sunken);
|
||||
border: 1px solid var(--dn-border);
|
||||
border-radius: var(--dn-radius-lg);
|
||||
}
|
||||
|
||||
.workspace-selector-view {
|
||||
width: 100%;
|
||||
max-width: 34rem;
|
||||
padding: var(--dn-space-6);
|
||||
background: var(--dn-bg-sunken);
|
||||
border: 1px solid var(--dn-border);
|
||||
border-radius: var(--dn-radius-lg);
|
||||
box-shadow: var(--dn-shadow);
|
||||
}
|
||||
|
||||
/* --- Editor shell ----------------------------------------------------------
|
||||
Fixed-width file rail + fluid editor. Both scroll independently.
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
.layout-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.files-bar {
|
||||
flex: 0 0 var(--dn-sidebar-width);
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
padding: var(--dn-space-2);
|
||||
background: var(--dn-bg-sunken);
|
||||
border-right: 1px solid var(--dn-border);
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.editor-class {
|
||||
height: 100%;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
/* Below tablet the rail collapses to a strip across the shell's full width. */
|
||||
@media (max-width: 640px) {
|
||||
.layout-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.files-bar {
|
||||
flex: 0 0 auto;
|
||||
max-height: 40dvh;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid var(--dn-border);
|
||||
}
|
||||
}
|
||||
97
assets/css/tokens.css
Normal file
97
assets/css/tokens.css
Normal file
@@ -0,0 +1,97 @@
|
||||
/* ==========================================================================
|
||||
Design tokens — the only place raw values live.
|
||||
|
||||
Palette: Catppuccin Mocha, matching the editor's Theme::CATPPUCCIN_MOCHA
|
||||
so the editor pane and the app chrome are the same surface.
|
||||
|
||||
All tokens are prefixed --dn-. Tailwind v4 publishes its own --font-*,
|
||||
--text-*, --radius-* and --leading-* on :root and tree-shakes that set as
|
||||
utilities come and go, so an unprefixed token would collide silently.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
/* --- Raw palette ------------------------------------------------------ */
|
||||
--ctp-crust: #11111b;
|
||||
--ctp-mantle: #181825;
|
||||
--ctp-base: #1e1e2e;
|
||||
--ctp-surface-0: #313244;
|
||||
--ctp-surface-1: #45475a;
|
||||
--ctp-surface-2: #585b70;
|
||||
--ctp-overlay-0: #6c7086;
|
||||
--ctp-subtext-0: #a6adc8;
|
||||
--ctp-text: #cdd6f4;
|
||||
|
||||
--ctp-mauve: #cba6f7;
|
||||
--ctp-green: #a6e3a1;
|
||||
--ctp-peach: #fab387;
|
||||
--ctp-red: #f38ba8;
|
||||
|
||||
/* --- Semantic surfaces ------------------------------------------------ */
|
||||
--dn-bg-app: var(--ctp-base); /* editor pane, page background */
|
||||
--dn-bg-sunken: var(--ctp-mantle); /* sidebar, chrome */
|
||||
--dn-bg-well: var(--ctp-crust); /* deepest: inputs, scrollbar track */
|
||||
--dn-bg-raised: var(--ctp-surface-0); /* hover, selected */
|
||||
--dn-bg-overlay: var(--ctp-surface-1); /* popovers, tooltips */
|
||||
|
||||
/* --- Semantic text ---------------------------------------------------- */
|
||||
--dn-fg: var(--ctp-text);
|
||||
--dn-fg-muted: var(--ctp-subtext-0);
|
||||
--dn-fg-faint: var(--ctp-overlay-0);
|
||||
--dn-fg-on-accent: var(--ctp-crust);
|
||||
|
||||
/* --- Lines ------------------------------------------------------------ */
|
||||
--dn-border: var(--ctp-surface-0);
|
||||
--dn-border-strong: var(--ctp-surface-1);
|
||||
|
||||
/* --- Accent & status -------------------------------------------------- */
|
||||
--dn-accent: var(--ctp-mauve);
|
||||
--dn-accent-hover: color-mix(in oklab, var(--ctp-mauve) 88%, white);
|
||||
--dn-accent-muted: color-mix(in oklab, var(--ctp-mauve) 22%, transparent);
|
||||
--dn-success: var(--ctp-green);
|
||||
--dn-warning: var(--ctp-peach);
|
||||
--dn-danger: var(--ctp-red);
|
||||
|
||||
/* --- Type ------------------------------------------------------------- */
|
||||
--dn-font-ui: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI",
|
||||
Roboto, sans-serif;
|
||||
--dn-font-code: "JetBrains Mono", "Cascadia Code", "Fira Code", ui-monospace,
|
||||
"SF Mono", Consolas, "Liberation Mono", monospace;
|
||||
|
||||
--dn-text-xs: 0.75rem; /* 12 — captions, counts */
|
||||
--dn-text-sm: 0.8125rem; /* 13 — sidebar, dense chrome */
|
||||
--dn-text-base: 0.875rem; /* 14 — UI default */
|
||||
--dn-text-md: 1rem; /* 16 — prose */
|
||||
--dn-text-lg: 1.25rem; /* 20 — section headings */
|
||||
--dn-text-xl: 1.5rem; /* 24 — page title */
|
||||
|
||||
--dn-leading-tight: 1.25;
|
||||
--dn-leading-normal: 1.55;
|
||||
|
||||
--dn-tracking-caps: 0.08em; /* eyebrows / all-caps labels */
|
||||
|
||||
/* --- Space (4px base) -------------------------------------------------- */
|
||||
--dn-space-1: 0.25rem;
|
||||
--dn-space-2: 0.5rem;
|
||||
--dn-space-3: 0.75rem;
|
||||
--dn-space-4: 1rem;
|
||||
--dn-space-5: 1.5rem;
|
||||
--dn-space-6: 2rem;
|
||||
--dn-space-7: 3rem;
|
||||
|
||||
/* --- Shape ------------------------------------------------------------- */
|
||||
--dn-radius-sm: 3px;
|
||||
--dn-radius: 5px;
|
||||
--dn-radius-lg: 8px;
|
||||
|
||||
/* --- Motion ------------------------------------------------------------ */
|
||||
--dn-ease: cubic-bezier(0.2, 0, 0, 1);
|
||||
--dn-dur-fast: 90ms;
|
||||
--dn-dur: 160ms;
|
||||
|
||||
/* --- Elevation ---------------------------------------------------------- */
|
||||
--dn-shadow: 0 4px 16px rgb(0 0 0 / 0.35);
|
||||
|
||||
/* --- Layout ------------------------------------------------------------- */
|
||||
--dn-sidebar-width: 240px;
|
||||
--dn-measure: 72ch; /* max line length for prose */
|
||||
}
|
||||
BIN
assets/favicon.ico
Normal file
BIN
assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
20
assets/header.svg
Normal file
20
assets/header.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
263
assets/tailwind.css
Normal file
263
assets/tailwind.css
Normal file
@@ -0,0 +1,263 @@
|
||||
/*! tailwindcss v4.1.5 | MIT License | https://tailwindcss.com */
|
||||
@layer properties;
|
||||
@layer theme, base, components, utilities;
|
||||
@layer theme {
|
||||
:root, :host {
|
||||
--font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
|
||||
monospace;
|
||||
--text-xs: 0.75rem;
|
||||
--text-sm: 0.875rem;
|
||||
--text-base: 1rem;
|
||||
--text-lg: 1.125rem;
|
||||
--text-xl: 1.25rem;
|
||||
--leading-tight: 1.25;
|
||||
--leading-normal: 1.5;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--default-transition-duration: 150ms;
|
||||
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--default-font-family: var(--font-sans);
|
||||
--default-mono-font-family: var(--font-mono);
|
||||
}
|
||||
}
|
||||
@layer base {
|
||||
*, ::after, ::before, ::backdrop, ::file-selector-button {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0 solid;
|
||||
}
|
||||
html, :host {
|
||||
line-height: 1.5;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
tab-size: 4;
|
||||
font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji');
|
||||
font-feature-settings: var(--default-font-feature-settings, normal);
|
||||
font-variation-settings: var(--default-font-variation-settings, normal);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
hr {
|
||||
height: 0;
|
||||
color: inherit;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
-webkit-text-decoration: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
b, strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
code, kbd, samp, pre {
|
||||
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace);
|
||||
font-feature-settings: var(--default-mono-font-feature-settings, normal);
|
||||
font-variation-settings: var(--default-mono-font-variation-settings, normal);
|
||||
font-size: 1em;
|
||||
}
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
sub, sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
table {
|
||||
text-indent: 0;
|
||||
border-color: inherit;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
ol, ul, menu {
|
||||
list-style: none;
|
||||
}
|
||||
img, svg, video, canvas, audio, iframe, embed, object {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
img, video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
button, input, select, optgroup, textarea, ::file-selector-button {
|
||||
font: inherit;
|
||||
font-feature-settings: inherit;
|
||||
font-variation-settings: inherit;
|
||||
letter-spacing: inherit;
|
||||
color: inherit;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
:where(select:is([multiple], [size])) optgroup {
|
||||
font-weight: bolder;
|
||||
}
|
||||
:where(select:is([multiple], [size])) optgroup option {
|
||||
padding-inline-start: 20px;
|
||||
}
|
||||
::file-selector-button {
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
::placeholder {
|
||||
opacity: 1;
|
||||
}
|
||||
@supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
|
||||
::placeholder {
|
||||
color: currentcolor;
|
||||
@supports (color: color-mix(in lab, red, red)) {
|
||||
color: color-mix(in oklab, currentcolor 50%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
::-webkit-date-and-time-value {
|
||||
min-height: 1lh;
|
||||
text-align: inherit;
|
||||
}
|
||||
::-webkit-datetime-edit {
|
||||
display: inline-flex;
|
||||
}
|
||||
::-webkit-datetime-edit-fields-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
|
||||
padding-block: 0;
|
||||
}
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
button, input:where([type='button'], [type='reset'], [type='submit']), ::file-selector-button {
|
||||
appearance: button;
|
||||
}
|
||||
::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
[hidden]:where(:not([hidden='until-found'])) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@layer utilities {
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
.static {
|
||||
position: static;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
@media (width >= 40rem) {
|
||||
max-width: 40rem;
|
||||
}
|
||||
@media (width >= 48rem) {
|
||||
max-width: 48rem;
|
||||
}
|
||||
@media (width >= 64rem) {
|
||||
max-width: 64rem;
|
||||
}
|
||||
@media (width >= 80rem) {
|
||||
max-width: 80rem;
|
||||
}
|
||||
@media (width >= 96rem) {
|
||||
max-width: 96rem;
|
||||
}
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
||||
}
|
||||
.border {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.text-wrap {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
.outline {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 1px;
|
||||
}
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
transition-duration: var(--tw-duration, var(--default-transition-duration));
|
||||
}
|
||||
}
|
||||
@property --tw-rotate-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-rotate-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-rotate-z {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-skew-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-skew-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-border-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@property --tw-outline-style {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: solid;
|
||||
}
|
||||
@layer properties {
|
||||
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
||||
*, ::before, ::after, ::backdrop {
|
||||
--tw-rotate-x: initial;
|
||||
--tw-rotate-y: initial;
|
||||
--tw-rotate-z: initial;
|
||||
--tw-skew-x: initial;
|
||||
--tw-skew-y: initial;
|
||||
--tw-border-style: solid;
|
||||
--tw-outline-style: solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
clippy.toml
Normal file
8
clippy.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
await-holding-invalid-types = [
|
||||
"generational_box::GenerationalRef",
|
||||
{ path = "generational_box::GenerationalRef", reason = "Reads should not be held over an await point. This will cause any writes to fail while the await is pending since the read borrow is still active." },
|
||||
"generational_box::GenerationalRefMut",
|
||||
{ path = "generational_box::GenerationalRefMut", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
|
||||
"dioxus_signals::WriteLock",
|
||||
{ path = "dioxus_signals::WriteLock", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
|
||||
]
|
||||
18
package-lock.json
generated
Normal file
18
package-lock.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "dionotes-client",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"tailwindcss": "^4.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
|
||||
"integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
package.json
Normal file
5
package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"tailwindcss": "^4.3.3"
|
||||
}
|
||||
}
|
||||
0
src/components/custom_link/custom_link_component.rs
Normal file
0
src/components/custom_link/custom_link_component.rs
Normal file
0
src/components/custom_link/custom_link_props.rs
Normal file
0
src/components/custom_link/custom_link_props.rs
Normal file
0
src/components/custom_link/mod.rs
Normal file
0
src/components/custom_link/mod.rs
Normal file
0
src/components/mod.rs
Normal file
0
src/components/mod.rs
Normal file
0
src/views/editor/dionotes_editor.rs
Normal file
0
src/views/editor/dionotes_editor.rs
Normal file
0
src/views/workspace_selector/selector.rs
Normal file
0
src/views/workspace_selector/selector.rs
Normal file
1
tailwind.css
Normal file
1
tailwind.css
Normal file
@@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
||||
Reference in New Issue
Block a user