update web

This commit is contained in:
2026-07-28 14:03:56 +07:00
parent a8bdd49072
commit 3a95456917
15 changed files with 575 additions and 171 deletions

84
web/src/lib.rs Normal file
View File

@@ -0,0 +1,84 @@
use gpui::*;
use ui::{Root, v_flex};
use universal_time::{
Duration, Instant, MonotonicClock, SystemTime, WallClock, define_time_provider,
};
use wasm_bindgen::prelude::*;
struct CustomTimeProvider;
impl WallClock for CustomTimeProvider {
fn system_time(&self) -> SystemTime {
SystemTime::from_unix_duration(Duration::from_secs(0))
}
}
impl MonotonicClock for CustomTimeProvider {
fn instant(&self) -> Instant {
Instant::from_ticks(Duration::from_secs(0))
}
}
define_time_provider!(CustomTimeProvider);
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
console_error_panic_hook::set_once();
// Initialize logging to browser console
console_log::init_with_level(log::Level::Info).expect("Failed to initialize logger");
// Also initialize tracing for WASM
tracing_wasm::set_as_global_default();
#[cfg(target_family = "wasm")]
gpui_platform::web_init();
#[cfg(not(target_family = "wasm"))]
let app = gpui_platform::application();
#[cfg(target_family = "wasm")]
let app = {
let app = gpui_platform::single_threaded_web();
// Temporary fix: intentionally leak the `Rc<AppCell>` to keep the application alive
struct WasmApplication(std::rc::Rc<AppCell>);
let wasm_app = unsafe { std::mem::transmute::<Application, WasmApplication>(app) };
std::mem::forget(wasm_app.0.clone());
unsafe { std::mem::transmute::<WasmApplication, Application>(wasm_app) }
};
app.run(|cx| {
// Initialize components
ui::init(cx);
// Initialize theme registry
theme::init(cx);
// Open the root window
cx.open_window(WindowOptions::default(), |window, cx| {
let view = cx.new(HelloWeb::new);
cx.new(|cx| Root::new(view.into(), window, cx))
})
.expect("Failed to open window. Please restart the application.");
cx.activate(true);
});
Ok(())
}
struct HelloWeb {}
impl HelloWeb {
fn new(_cx: &mut Context<Self>) -> Self {
Self {}
}
}
impl Render for HelloWeb {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.size_full()
.items_center()
.justify_center()
.child("Hello web")
}
}

View File

@@ -1,77 +0,0 @@
use gpui::*;
use ui::Root;
use universal_time::{
Duration, Instant, MonotonicClock, SystemTime, WallClock, define_time_provider,
};
use wasm_bindgen::prelude::*;
struct CustomTimeProvider;
impl WallClock for CustomTimeProvider {
fn system_time(&self) -> SystemTime {
SystemTime::from_unix_duration(Duration::from_secs(0))
}
}
impl MonotonicClock for CustomTimeProvider {
fn instant(&self) -> Instant {
Instant::from_ticks(Duration::from_secs(0))
}
}
define_time_provider!(CustomTimeProvider);
fn main() {
// Binary entry point, actual initialization happens in `run()` below,
// which is called explicitly from JavaScript after the WASM module loads.
}
#[wasm_bindgen]
pub fn run() -> Result<(), JsValue> {
console_error_panic_hook::set_once();
// Initialize logging to browser console
console_log::init_with_level(log::Level::Info).expect("Failed to initialize logger");
// Also initialize tracing for WASM
tracing_wasm::set_as_global_default();
#[cfg(target_family = "wasm")]
gpui_platform::web_init();
gpui_platform::application().run(|cx| {
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
// Initialize components
ui::init(cx);
// Initialize theme registry
theme::init(cx);
// Initialize settings
settings::init(window, cx);
// Initialize the nostr client
state::init(window, cx);
// Initialize person registry
person::init(window, cx);
// Initialize device signer
//
// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
device::init(window, cx);
// Initialize app registry
chat::init(window, cx);
// Root Entity
Root::new(workspace::init(window, cx).into(), window, cx)
})
})
.expect("Failed to open window. Please restart the application.");
cx.activate(true);
});
Ok(())
}

View File

@@ -1,77 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Coop</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
sans-serif;
overflow: hidden;
width: 100vw;
height: 100vh;
}
#app {
width: 100%;
height: 100%;
position: relative;
}
#canvas {
display: block;
width: 100%;
height: 100%;
}
#loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #000;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 16px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error {
color: #d32f2f;
padding: 20px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<canvas id="canvas"></canvas>
<div id="loading">
<div class="spinner"></div>
<p>Loading Coop...</p>
</div>
</div>
<script type="module" src="main.js"></script>
</body>
</html>

View File

@@ -1,35 +0,0 @@
async function init() {
const loadingEl = document.getElementById('loading');
const appEl = document.getElementById('app');
try {
// Import the WASM module
const wasm = await import('./wasm/coop_web.js');
await wasm.default();
// Initialize the story gallery
await wasm.run();
// Hide loading indicator
if (appEl) {
appEl.remove();
}
} catch (error) {
console.error('Failed to initialize:', error);
// Show error message
if (loadingEl) {
loadingEl.innerHTML = `
<div class="error">
<h2>Failed to load the application</h2>
<p>${error.message || error}</p>
<p style="margin-top: 10px; font-size: 14px;">
Please check the console for more details.
</p>
</div>
`;
}
}
}
init();

View File

@@ -1,17 +0,0 @@
{
"name": "coop-web",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"wasm": "cargo build --target wasm32-unknown-unknown --release && wasm-bindgen ../../../target/wasm32-unknown-unknown/release/gpui_component_story_web.wasm --out-dir ./src/wasm --target web --no-typescript"
},
"devDependencies": {
"vite": "^8",
"vite-plugin-static-copy": "^3.2.0",
"vite-plugin-wasm": "^3.3.0"
}
}

View File

@@ -1,75 +0,0 @@
import { defineConfig } from "vite";
import wasm from "vite-plugin-wasm";
import { viteStaticCopy } from "vite-plugin-static-copy";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
plugins: [
wasm(),
viteStaticCopy({
targets: [
{
src: path.resolve(__dirname, "../../../assets/icons"),
dest: "assets",
},
],
}),
{
name: "serve-assets",
configureServer(server) {
server.middlewares.use(
"/coop/assets",
(req, res, next) => {
const assetsPath = path.resolve(__dirname, "../../../assets");
const filePath = path.join(
assetsPath,
req.url.replace("/assets", ""),
);
// Try to serve the file
import("fs").then(({ default: fs }) => {
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
res.setHeader("Access-Control-Allow-Origin", "*");
if (filePath.endsWith(".svg")) {
res.setHeader("Content-Type", "image/svg+xml");
}
fs.createReadStream(filePath).pipe(res);
} else {
next();
}
});
},
);
},
},
],
build: {
target: "esnext",
minify: true,
sourcemap: false,
rollupOptions: {
output: {
manualChunks: undefined,
},
},
},
server: {
port: 3000,
open: true,
fs: {
strict: false,
allow: [".."],
},
headers: {
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin",
},
},
optimizeDeps: {
exclude: ["./src/wasm"],
},
base: "/",
});