update
This commit is contained in:
parent
6405c06646
commit
626e465d8c
23 changed files with 2346 additions and 274 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"[prisma]": {
|
||||
"editor.defaultFormatter": "Prisma.prisma"
|
||||
}
|
||||
},
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
18
apps/calendar/meta.yml
Normal file
18
apps/calendar/meta.yml
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: calendar
|
||||
version: 0.1.0-rc1
|
||||
|
||||
namespaces:
|
||||
panorama.calendar:
|
||||
event-start-timestamp:
|
||||
description: The exact timestamp the event starts
|
||||
type: DateTimeTz
|
||||
|
||||
event-end-timestamp:
|
||||
description: The exact timestamp the event ends
|
||||
type: DateTimeTz
|
||||
|
||||
event-location:
|
||||
description:
|
||||
|
||||
event-location:
|
||||
description:
|
|
@ -1,9 +0,0 @@
|
|||
import { HookContext } from "../../src/api";
|
||||
|
||||
export default class {
|
||||
constructor() {}
|
||||
|
||||
static registerHooks(ct: HookContext) {
|
||||
ct.addSensitivePattern(/ /);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"$schema": "../../generated/spec/meta.schema.json",
|
||||
"title": "Todos",
|
||||
"license": "MIT",
|
||||
|
||||
"depends": {},
|
||||
|
||||
"interfaces": {
|
||||
"Todos": "./Todos.js"
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
# `meta.json`
|
||||
|
||||
`meta.json` is the entrypoint for installing an application. You can also use
|
||||
[JSON5], since it supports comments and trailing commas and a lot of nice
|
||||
features.
|
||||
|
||||
[json5]: https://json5.org
|
||||
|
||||
## API Reference
|
||||
|
||||
This is a rendered schema for how to write `meta.json` files.
|
||||
|
||||
[Documentation](./generated/spec)
|
16
docs/src/dev/permissions.md
Normal file
16
docs/src/dev/permissions.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Permission Model
|
||||
|
||||
Permissions are:
|
||||
|
||||
- compose
|
||||
- This is a read/write/create permission for nodes created by the current app.
|
||||
- NOTE: Unlike most operating systems, this permission does _not_ grant the
|
||||
ability to create new nodes or write to nodes not created by the current app.
|
||||
See `overwrite` below for that.
|
||||
- overread
|
||||
- This grants the ability to query for nodes outside of ones created by the
|
||||
current app.
|
||||
- overwrite
|
||||
- This grants the ability to write into nodes outside of ones created by the
|
||||
current app.
|
||||
- (is it even worth it to eventually move this to being script-dependent?)
|
42
docs/src/dev/types.md
Normal file
42
docs/src/dev/types.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# Data types
|
||||
|
||||
close to javascript types? i guess
|
||||
|
||||
## Primitives
|
||||
|
||||
- number
|
||||
- Arbitrary precision number
|
||||
- End with \_u8, \_u32, \_u64 or \_f- or \_i-equivalents to _suggest_ a
|
||||
particular container size
|
||||
- boolean
|
||||
- literally true or false
|
||||
- string
|
||||
|
||||
## Special primitives
|
||||
|
||||
- DateTimeTz
|
||||
- Timezone-aware date (internally stored as ISO8601)
|
||||
- There is no non-timezone-aware date. Please use a string type for that
|
||||
|
||||
## Type constructors
|
||||
|
||||
- Option
|
||||
- Either
|
||||
- Heterogeneous list
|
||||
- Heterogeneous map
|
||||
- Array
|
||||
- Record
|
||||
- Enum
|
||||
|
||||
## Complex type constructors
|
||||
|
||||
- JSON schema
|
||||
|
||||
# Interfaces
|
||||
|
||||
Interfaces approximately match traits in rust, comprised of
|
||||
|
||||
- Associated types
|
||||
- Functions
|
||||
|
||||
TODO: Generics?
|
79
package-lock.json
generated
79
package-lock.json
generated
|
@ -14,6 +14,7 @@
|
|||
"@chakra-ui/react": "^2.7.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@prisma/client": "^4.8.0",
|
||||
"@tauri-apps/api": "^1.3.0",
|
||||
"classnames": "^2.3.2",
|
||||
"framer-motion": "^10.12.16",
|
||||
|
@ -27,6 +28,7 @@
|
|||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"prisma": "^4.8.0",
|
||||
"sass": "^1.63.3",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.2.1"
|
||||
|
@ -1863,6 +1865,38 @@
|
|||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
||||
"integrity": "sha512-Y1riB0p2W52kh3zgssP/YAhln3RjBFcJy3uwEiyjmU+TQYh6QTZDRFBo3JtBWuq2FyMOl1Rye8jxzUP+n0l5Cg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.8.0.tgz",
|
||||
"integrity": "sha512-A1Asn2rxZMlLAj1HTyfaCv0VQrLUv034jVay05QlqZg1qiHPeA3/pGTfNMijbsMYCsGVxfWEJuaZZuNxXGMCrA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz",
|
||||
"integrity": "sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw=="
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
|
@ -3230,6 +3264,23 @@
|
|||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.8.0.tgz",
|
||||
"integrity": "sha512-DWIhxvxt8f4h6MDd35mz7BJff+fu7HItW3WPDIEpCR3RzcOWyiHBbLQW5/DOgmf+pRLTjwXQob7kuTZVYUAw5w==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
"prisma2": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
|
@ -5256,6 +5307,25 @@
|
|||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
||||
"integrity": "sha512-Y1riB0p2W52kh3zgssP/YAhln3RjBFcJy3uwEiyjmU+TQYh6QTZDRFBo3JtBWuq2FyMOl1Rye8jxzUP+n0l5Cg==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.8.0.tgz",
|
||||
"integrity": "sha512-A1Asn2rxZMlLAj1HTyfaCv0VQrLUv034jVay05QlqZg1qiHPeA3/pGTfNMijbsMYCsGVxfWEJuaZZuNxXGMCrA==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz",
|
||||
"integrity": "sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw=="
|
||||
},
|
||||
"@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
|
@ -6213,6 +6283,15 @@
|
|||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.8.0.tgz",
|
||||
"integrity": "sha512-DWIhxvxt8f4h6MDd35mz7BJff+fu7HItW3WPDIEpCR3RzcOWyiHBbLQW5/DOgmf+pRLTjwXQob7kuTZVYUAw5w==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.8.0"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"@chakra-ui/react": "^2.7.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@prisma/client": "^4.8.0",
|
||||
"@tauri-apps/api": "^1.3.0",
|
||||
"classnames": "^2.3.2",
|
||||
"framer-motion": "^10.12.16",
|
||||
|
@ -29,6 +30,7 @@
|
|||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^3.0.0",
|
||||
"prisma": "^4.8.0",
|
||||
"sass": "^1.63.3",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.2.1"
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
[registries.crates-io]
|
||||
protocol = "sparse"
|
||||
protocol = "sparse"
|
||||
|
||||
[alias]
|
||||
prisma = "run -p prisma-cli --"
|
1
src-tauri/.gitignore
vendored
1
src-tauri/.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
src/prisma.rs
|
2116
src-tauri/Cargo.lock
generated
2116
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -7,7 +7,9 @@ license = ""
|
|||
repository = ""
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[workspace]
|
||||
members = ["prisma-cli"]
|
||||
resolver = "2"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.3", features = [] }
|
||||
|
@ -16,11 +18,18 @@ tauri-build = { version = "1.3", features = [] }
|
|||
tauri = { version = "1.3", features = ["shell-open"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sqlx = { version = "0.6.3", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||
# TODO: L @ prisma-client-rust not updating to 0.25.x of the sqlite lib
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||
tokio = { version = "1.28.2", features = ["full"] }
|
||||
anyhow = "1.0.71"
|
||||
dirs = "5.0.1"
|
||||
|
||||
[dependencies.prisma-client-rust]
|
||||
git = "https://github.com/Brendonovich/prisma-client-rust"
|
||||
tag = "0.6.8"
|
||||
default-features = false
|
||||
features = ["sqlite"]
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
# DO NOT REMOVE!!
|
||||
|
|
|
@ -19,6 +19,7 @@ CREATE TABLE "Graph" (
|
|||
"label" TEXT,
|
||||
"fromId" TEXT NOT NULL,
|
||||
"toId" TEXT NOT NULL,
|
||||
"directional" BOOLEAN NOT NULL,
|
||||
|
||||
PRIMARY KEY ("fromId", "toId"),
|
||||
CONSTRAINT "Graph_fromId_fkey" FOREIGN KEY ("fromId") REFERENCES "Node" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||
|
|
10
src-tauri/prisma-cli/Cargo.toml
Normal file
10
src-tauri/prisma-cli/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "prisma-cli"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies.prisma-client-rust-cli]
|
||||
git = "https://github.com/Brendonovich/prisma-client-rust"
|
||||
tag = "0.6.8"
|
||||
default-features = false
|
||||
features = ["sqlite"]
|
3
src-tauri/prisma-cli/src/main.rs
Normal file
3
src-tauri/prisma-cli/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
prisma_client_rust_cli::run();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["native"]
|
||||
provider = "cargo prisma"
|
||||
output = "../src/prisma.rs"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
|
@ -40,6 +40,8 @@ model Edge {
|
|||
toId String
|
||||
toNode Node @relation(name: "toNode", fields: [toId], references: [id])
|
||||
|
||||
directional Boolean
|
||||
|
||||
@@unique([fromId, toId])
|
||||
@@index([appId, appKey])
|
||||
}
|
||||
|
@ -97,3 +99,11 @@ model Patterns {
|
|||
|
||||
functionName String
|
||||
}
|
||||
|
||||
/// Index table, for keeping track of indexes to build and take down when the database first starts
|
||||
model Indices {
|
||||
id String @id @default(uuid())
|
||||
command String
|
||||
|
||||
// TODO: Does sqlite avoid making duplicate indexes automatically?
|
||||
}
|
50
src-tauri/src/apps/meta.rs
Normal file
50
src-tauri/src/apps/meta.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Metadata describing the app
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AppMetadata {
|
||||
/// The name of the app
|
||||
pub name: String,
|
||||
|
||||
/// Version ([semantic versioning])
|
||||
///
|
||||
/// [semantic versioning]: https://semver.org/
|
||||
#[serde(default = "default_version")]
|
||||
pub version: String,
|
||||
|
||||
/// Namespaces requested
|
||||
///
|
||||
/// A namespace is an app-specific [FQN] and cannot be used by other apps
|
||||
/// unless permission is explicitly granted by the user.
|
||||
///
|
||||
/// [FQN]: https://en.wikipedia.org/wiki/Fully_qualified_name
|
||||
///
|
||||
/// Namespaces are used in metadata keys. For example, a calendar app may
|
||||
/// request the namespace `org.somebody.calendar`. Then, it may use the keys:
|
||||
///
|
||||
/// - `org.somebody.calendar / event-timestamp`
|
||||
/// - `org.somebody.calendar / event-location`
|
||||
///
|
||||
/// Notably, the `/` is purely syntactic here; the system keeps the two parts
|
||||
/// completely isolated, so there is no possible namespace "leakage" based on
|
||||
/// carefully crafted keys.
|
||||
pub namespaces: HashMap<String, NamespaceConfig>,
|
||||
}
|
||||
|
||||
/// Name-to-description mapping of keys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct NamespaceConfig(HashMap<String, KeyDescriptionConfig>);
|
||||
|
||||
/// Name-to-description mapping of keys
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct KeyDescriptionConfig {
|
||||
pub description: String,
|
||||
|
||||
#[serde(rename = "type")]
|
||||
pub ty: String,
|
||||
}
|
||||
|
||||
fn default_version() -> String {
|
||||
"0.0.0".to_owned()
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
pub mod meta;
|
||||
|
||||
use std::{collections::HashMap, env, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::prisma::PrismaClient;
|
||||
|
||||
pub struct Handler {}
|
||||
|
||||
pub enum Event {
|
||||
|
@ -12,21 +16,20 @@ pub enum Event {
|
|||
}
|
||||
|
||||
pub struct AppManager {
|
||||
pool: SqlitePool,
|
||||
prisma: PrismaClient,
|
||||
apps: Vec<App>,
|
||||
|
||||
key_handlers: HashMap<(String, Event), Handler>,
|
||||
}
|
||||
|
||||
impl AppManager {
|
||||
pub fn new(pool: &SqlitePool) -> Self {
|
||||
let pool = pool.clone();
|
||||
pub async fn new() -> Result<Self> {
|
||||
let prisma = PrismaClient::_builder().build().await?;
|
||||
let apps = vec![];
|
||||
Self {
|
||||
pool,
|
||||
Ok(Self {
|
||||
prisma,
|
||||
apps,
|
||||
key_handlers: HashMap::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Register apps into the pool
|
||||
|
@ -46,6 +49,11 @@ impl AppManager {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Figure out which indices are required by the set of known apps
|
||||
pub async fn compute_indices(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct App {
|
|
@ -6,53 +6,35 @@ extern crate serde;
|
|||
|
||||
pub mod apps;
|
||||
pub mod dal;
|
||||
pub mod prisma;
|
||||
|
||||
use anyhow::Result;
|
||||
use apps::AppManager;
|
||||
use sqlx::{
|
||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
||||
SqlitePool,
|
||||
};
|
||||
use tokio::fs;
|
||||
use prisma::PrismaClient;
|
||||
|
||||
use crate::dal::Db;
|
||||
use crate::apps::AppManager;
|
||||
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
pub use crate::apps::meta::AppMetadata;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Open database
|
||||
let pool = open_database().await?;
|
||||
let client = PrismaClient::_builder().build().await?;
|
||||
|
||||
// Register apps
|
||||
let mut app_manager = AppManager::new(&pool);
|
||||
let mut app_manager = AppManager::new().await?;
|
||||
app_manager.register().await?;
|
||||
|
||||
// Read indexes
|
||||
let indexes_from_apps = app_manager.compute_indices().await?;
|
||||
let indexes_from_db = client.indices().find_many(vec![]).exec().await?;
|
||||
println!("indexes: {:?}", indexes_from_db);
|
||||
|
||||
tauri::Builder::default()
|
||||
.manage(Db(pool))
|
||||
.manage(app_manager)
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.manage(client)
|
||||
.invoke_handler(tauri::generate_handler![dal::query])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn open_database() -> Result<SqlitePool> {
|
||||
let app_dir = dirs::data_dir().unwrap().join("panorama");
|
||||
fs::create_dir_all(&app_dir).await?;
|
||||
|
||||
let db_path = app_dir.join("vault.sqlite");
|
||||
let db_options = SqliteConnectOptions::new()
|
||||
.filename(&db_path)
|
||||
.create_if_missing(true);
|
||||
let pool = SqlitePoolOptions::new().connect_with(db_options).await?;
|
||||
println!("Running migrations...");
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
|
||||
Ok(pool)
|
||||
}
|
||||
|
|
127
src/App.tsx
127
src/App.tsx
|
@ -11,9 +11,10 @@ import {
|
|||
Image,
|
||||
MenuList,
|
||||
CloseButton,
|
||||
useColorMode,
|
||||
} from "@chakra-ui/react";
|
||||
import { AddIcon, ChevronDownIcon, SearchIcon } from "@chakra-ui/icons";
|
||||
import { Mosaic, MosaicWindow, RemoveButton } from "react-mosaic-component";
|
||||
import { Mosaic, MosaicWindow } from "react-mosaic-component";
|
||||
import classNames from "classnames";
|
||||
|
||||
import "react-mosaic-component/react-mosaic-component.css";
|
||||
|
@ -27,76 +28,78 @@ function App() {
|
|||
c: <div>Bottom Right Window</div>,
|
||||
};
|
||||
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
|
||||
return (
|
||||
<ChakraProvider>
|
||||
<main className={styles.main}>
|
||||
<nav className={styles.nav}>
|
||||
<h1 className={styles.title}>panorama</h1>
|
||||
<main className={styles.main}>
|
||||
<nav className={styles.nav}>
|
||||
<h1 className={styles.title}>panorama</h1>
|
||||
|
||||
<Menu>
|
||||
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
|
||||
<AddIcon />
|
||||
</MenuButton>
|
||||
{colorMode}
|
||||
|
||||
<MenuList zIndex={500}>
|
||||
<MenuItem minH="48px">
|
||||
<Image
|
||||
boxSize="2rem"
|
||||
borderRadius="full"
|
||||
src="https://placekitten.com/100/100"
|
||||
alt="Fluffybuns the destroyer"
|
||||
mr="12px"
|
||||
/>
|
||||
<span>Fluffybuns the Destroyer</span>
|
||||
</MenuItem>
|
||||
<Menu>
|
||||
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
|
||||
<AddIcon />
|
||||
</MenuButton>
|
||||
|
||||
<MenuItem minH="40px">
|
||||
<Image
|
||||
boxSize="2rem"
|
||||
borderRadius="full"
|
||||
src="https://placekitten.com/120/120"
|
||||
alt="Simon the pensive"
|
||||
mr="12px"
|
||||
/>
|
||||
<span>Simon the pensive</span>
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
<MenuList zIndex={500}>
|
||||
<MenuItem minH="48px" onClick={toggleColorMode}>
|
||||
<Image
|
||||
boxSize="2rem"
|
||||
borderRadius="full"
|
||||
src="https://placekitten.com/100/100"
|
||||
alt="Fluffybuns the destroyer"
|
||||
mr="12px"
|
||||
/>
|
||||
<span>Fluffybuns the Destroyer</span>
|
||||
</MenuItem>
|
||||
|
||||
<div className={styles.spacer} />
|
||||
<MenuItem minH="40px">
|
||||
<Image
|
||||
boxSize="2rem"
|
||||
borderRadius="full"
|
||||
src="https://placekitten.com/120/120"
|
||||
alt="Simon the pensive"
|
||||
mr="12px"
|
||||
/>
|
||||
<span>Simon the pensive</span>
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
|
||||
<InputGroup className={styles.searchContainer}>
|
||||
<Input placeholder="Search..." />
|
||||
<InputRightElement>
|
||||
<SearchIcon />
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</nav>
|
||||
<div className={styles.spacer} />
|
||||
|
||||
<Mosaic<string>
|
||||
className={classNames(styles.container, "mosaic-blueprint-theme")}
|
||||
renderTile={(id, path) => (
|
||||
<MosaicWindow title={id} path={path}></MosaicWindow>
|
||||
)}
|
||||
blueprintNamespace="bp3"
|
||||
initialValue={{
|
||||
<InputGroup className={styles.searchContainer}>
|
||||
<Input placeholder="Search..." />
|
||||
<InputRightElement>
|
||||
<SearchIcon />
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
</nav>
|
||||
|
||||
<Mosaic<string>
|
||||
className={classNames(styles.container, "mosaic-blueprint-theme")}
|
||||
renderTile={(id, path) => (
|
||||
<MosaicWindow title={id} path={path}></MosaicWindow>
|
||||
)}
|
||||
blueprintNamespace="bp3"
|
||||
initialValue={{
|
||||
direction: "row",
|
||||
first: "Journal",
|
||||
second: {
|
||||
direction: "row",
|
||||
first: "Journal",
|
||||
second: {
|
||||
direction: "row",
|
||||
first: {
|
||||
direction: "column",
|
||||
first: "Calendar",
|
||||
second: "Email",
|
||||
},
|
||||
second: "Chat",
|
||||
splitPercentage: 66.66,
|
||||
first: {
|
||||
direction: "column",
|
||||
first: "Calendar",
|
||||
second: "Email",
|
||||
},
|
||||
splitPercentage: 25,
|
||||
}}
|
||||
/>
|
||||
</main>
|
||||
</ChakraProvider>
|
||||
second: "Chat",
|
||||
splitPercentage: 66.66,
|
||||
},
|
||||
splitPercentage: 25,
|
||||
}}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
19
src/main.tsx
19
src/main.tsx
|
@ -2,9 +2,26 @@ import React from "react";
|
|||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./styles.css";
|
||||
import {
|
||||
ChakraProvider,
|
||||
ColorModeProvider,
|
||||
ThemeConfig,
|
||||
ThemeProvider,
|
||||
extendBaseTheme,
|
||||
extendTheme,
|
||||
} from "@chakra-ui/react";
|
||||
|
||||
const config: ThemeConfig = {
|
||||
initialColorMode: "system",
|
||||
useSystemColorMode: true,
|
||||
};
|
||||
|
||||
const theme = extendBaseTheme({ config });
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<ChakraProvider theme={theme}>
|
||||
<App />
|
||||
</ChakraProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
|
@ -5,4 +5,6 @@ body,
|
|||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: var(--chakra-colors-chakra-body-bg);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue