This commit is contained in:
Michael Zhang 2023-06-12 23:07:03 -05:00
parent 6405c06646
commit 626e465d8c
23 changed files with 2346 additions and 274 deletions

View file

@ -1,5 +1,6 @@
{
"[prisma]": {
"editor.defaultFormatter": "Prisma.prisma"
}
},
"editor.formatOnSave": true
}

18
apps/calendar/meta.yml Normal file
View 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:

View file

@ -1,9 +0,0 @@
import { HookContext } from "../../src/api";
export default class {
constructor() {}
static registerHooks(ct: HookContext) {
ct.addSensitivePattern(/ /);
}
}

View file

@ -1,11 +0,0 @@
{
"$schema": "../../generated/spec/meta.schema.json",
"title": "Todos",
"license": "MIT",
"depends": {},
"interfaces": {
"Todos": "./Todos.js"
}
}

View file

@ -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)

View 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
View 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
View file

@ -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",

View file

@ -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"

View file

@ -1,2 +1,5 @@
[registries.crates-io]
protocol = "sparse"
protocol = "sparse"
[alias]
prisma = "run -p prisma-cli --"

View file

@ -2,3 +2,4 @@
# will have compiled files and executables
/target/
src/prisma.rs

2116
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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!!

View file

@ -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,

View 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"]

View file

@ -0,0 +1,3 @@
fn main() {
prisma_client_rust_cli::run();
}

View file

@ -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?
}

View 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()
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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>
);
}

View file

@ -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>
);

View file

@ -5,4 +5,6 @@ body,
padding: 0;
width: 100%;
height: 100%;
background-color: var(--chakra-colors-chakra-body-bg);
}