wasm first steps

This commit is contained in:
vcoppe
2026-07-14 18:36:30 +02:00
parent 86f4fc3394
commit ed6264444f
11 changed files with 398 additions and 3 deletions
+2
View File
@@ -0,0 +1,2 @@
target/
pkg/
+114
View File
@@ -0,0 +1,114 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "gpx-rs"
version = "0.1.0"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
dependencies = [
"unicode-ident",
]
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "gpx-rs"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
+27
View File
@@ -0,0 +1,27 @@
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use crate::{stack::Stack, types::GPXFile};
#[wasm_bindgen]
pub struct Controller {
stack: Stack,
}
#[wasm_bindgen]
impl Controller {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
stack: Stack::default(),
}
}
#[wasm_bindgen]
pub fn create_file(&mut self, name: &str) {
let id = self.stack.get_new_file_id();
let file = Rc::new(GPXFile::new(id, name));
self.stack.update(&[file]);
}
}
+3
View File
@@ -0,0 +1,3 @@
mod controller;
mod stack;
mod types;
+101
View File
@@ -0,0 +1,101 @@
use std::{collections::HashMap, rc::Rc};
use crate::types::{GPXFile, GPXFileId};
#[derive(Default)]
pub struct Stack {
entries: Vec<StackEntry>,
index: Option<usize>,
}
impl Stack {
pub fn current(&self) -> Option<&StackEntry> {
match self.index {
Some(i) => Some(&self.entries[i]),
None => None,
}
}
pub fn get_new_file_id(&self) -> GPXFileId {
if let Some(current) = self.current() {
for id in 0..current.len() {
if !current.contains_key(&id) {
return id;
}
}
current.len() as GPXFileId
} else {
0
}
}
pub fn update(&mut self, files: &[Rc<GPXFile>]) {
let mut next = match self.current() {
Some(current) => current.clone(),
None => StackEntry::default(),
};
for file in files {
next.insert(file.id, file.clone());
}
self.push(next);
}
pub fn delete(&mut self, files: &[GPXFileId]) {
if let Some(current) = self.current() {
let mut next = current.clone();
for file in files {
next.remove(file);
}
self.push(next);
}
}
pub fn can_undo(&self) -> bool {
self.index.is_some()
}
pub fn can_redo(&self) -> bool {
match self.index {
Some(i) => i + 1 < self.entries.len(),
None => !self.entries.is_empty(),
}
}
pub fn undo(&mut self) {
if let Some(i) = self.index {
if i == 0 {
self.index = None;
} else {
self.index = Some(i - 1);
}
}
}
pub fn redo(&mut self) {
match self.index {
Some(i) => {
if i + 1 < self.entries.len() {
self.index = Some(i + 1);
}
}
None => {
if !self.entries.is_empty() {
self.index = Some(0);
}
}
}
}
fn push(&mut self, entry: StackEntry) {
if let Some(i) = self.index {
if i + 1 < self.entries.len() {
self.entries.truncate(i + 1);
}
}
self.entries.push(entry);
self.index = Some(self.entries.len() - 1);
}
}
pub type StackEntry = HashMap<GPXFileId, Rc<GPXFile>>;
+106
View File
@@ -0,0 +1,106 @@
use std::rc::Rc;
pub type GPXFileId = usize;
pub struct GPXFile {
pub id: GPXFileId,
pub info: Rc<GPXFileInfo>,
pub trk: Vec<Track>,
pub wpt: Vec<Rc<WaypointChunk>>,
}
impl GPXFile {
pub fn new(id: GPXFileId, name: &str) -> Self {
Self {
id,
info: Rc::new(GPXFileInfo::new(name)),
trk: Vec::new(),
wpt: Vec::new(),
}
}
}
pub struct GPXFileInfo {
pub name: String,
pub desc: Option<String>,
pub author: Option<Author>,
pub link: Option<Link>,
pub time: Option<i64>,
}
impl GPXFileInfo {
pub fn new(name: &str) -> Self {
Self {
name: name.to_owned(),
desc: None,
author: None,
link: None,
time: None,
}
}
}
pub struct Author {
pub name: Option<String>,
pub email: Option<String>,
pub link: Option<Link>,
}
pub struct Link {
pub href: String,
pub text: Option<String>,
pub type_: Option<String>,
}
pub struct Track {
pub info: Rc<TrackInfo>,
pub trkseg: Vec<TrackSegment>,
}
pub struct TrackInfo {
name: Option<String>,
cmt: Option<String>,
desc: Option<String>,
src: Option<String>,
link: Option<Link>,
type_: Option<String>,
color: Option<String>,
opacity: Option<f64>,
width: Option<f64>,
}
pub struct TrackSegment {
pub trkpt: Vec<Rc<TrackPointChunk>>,
}
pub type TrackPointChunk = Vec<TrackPoint>;
pub struct TrackPoint {
pub coordinates: LngLat,
pub ele: f64,
pub time: Option<i64>,
pub hr: Option<u16>,
pub cad: Option<u16>,
pub power: Option<u16>,
pub atemp: Option<i16>,
// TODO OSM data? or store intervals at a higher level?
}
pub type WaypointChunk = Vec<Waypoint>;
pub struct Waypoint {
pub coordinates: LngLat,
pub ele: f64,
pub time: Option<i64>,
pub name: Option<String>,
pub cmt: Option<String>,
pub desc: Option<String>,
pub link: Option<Link>,
pub sym: Option<String>,
pub type_: Option<String>,
}
pub struct LngLat {
pub lng: f64,
pub lat: f64,
}
+21 -1
View File
@@ -17,6 +17,7 @@
"dexie": "^4.0.11", "dexie": "^4.0.11",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"gpx": "file:../gpx", "gpx": "file:../gpx",
"gpx-rs": "file:../gpx-rs/pkg",
"immer": "^10.1.1", "immer": "^10.1.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"mapillary-js": "^4.1.2", "mapillary-js": "^4.1.2",
@@ -70,7 +71,8 @@
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vaul-svelte": "^1.0.0-next.7", "vaul-svelte": "^1.0.0-next.7",
"vite": "^6.3.5" "vite": "^6.3.5",
"vite-plugin-wasm": "^3.6.0"
} }
}, },
"../gpx": { "../gpx": {
@@ -89,6 +91,10 @@
"typescript": "^5.6.2" "typescript": "^5.6.2"
} }
}, },
"../gpx-rs/pkg": {
"name": "gpx-rs",
"version": "0.1.0"
},
"../gpx/node_modules/@cspotcode/source-map-support": { "../gpx/node_modules/@cspotcode/source-map-support": {
"version": "0.8.1", "version": "0.8.1",
"dev": true, "dev": true,
@@ -5492,6 +5498,10 @@
"resolved": "../gpx", "resolved": "../gpx",
"link": true "link": true
}, },
"node_modules/gpx-rs": {
"resolved": "../gpx-rs/pkg",
"link": true
},
"node_modules/graceful-fs": { "node_modules/graceful-fs": {
"version": "4.2.11", "version": "4.2.11",
"dev": true, "dev": true,
@@ -7716,6 +7726,16 @@
"node": ">=18.0.0" "node": ">=18.0.0"
} }
}, },
"node_modules/vite-plugin-wasm": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.6.0.tgz",
"integrity": "sha512-mL/QPziiIA4RAA6DkaZZzOstdwbW5jO4Vz7Zenj0wieKWBlNvIvX5L5ljum9lcUX0ShNfBgCNLKTjNkRVVqcsw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8"
}
},
"node_modules/vite/node_modules/@esbuild/aix-ppc64": { "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12", "version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+3 -1
View File
@@ -59,7 +59,8 @@
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vaul-svelte": "^1.0.0-next.7", "vaul-svelte": "^1.0.0-next.7",
"vite": "^6.3.5" "vite": "^6.3.5",
"vite-plugin-wasm": "^3.6.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
@@ -72,6 +73,7 @@
"dexie": "^4.0.11", "dexie": "^4.0.11",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"gpx": "file:../gpx", "gpx": "file:../gpx",
"gpx-rs": "file:../gpx-rs/pkg",
"immer": "^10.1.1", "immer": "^10.1.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"mapillary-js": "^4.1.2", "mapillary-js": "^4.1.2",
@@ -20,6 +20,7 @@
import { getURLForGoogleDriveFile } from '$lib/components/embedding/embedding'; import { getURLForGoogleDriveFile } from '$lib/components/embedding/embedding';
import { db } from '$lib/db'; import { db } from '$lib/db';
import { fileStateCollection } from '$lib/logic/file-state'; import { fileStateCollection } from '$lib/logic/file-state';
import { browser } from '$app/environment';
const { const {
treeFileView, treeFileView,
@@ -36,6 +37,14 @@
); );
onMount(async () => { onMount(async () => {
if (browser) {
const wasm = await import('gpx-rs');
const controller = new wasm.Controller();
console.log(controller);
controller.create_file();
}
settings.connectToDatabase(db); settings.connectToDatabase(db);
fileStateCollection.connectToDatabase(db).then(() => { fileStateCollection.connectToDatabase(db).then(() => {
let files: string[] = JSON.parse(page.url.searchParams.get('files') || '[]'); let files: string[] = JSON.parse(page.url.searchParams.get('files') || '[]');
+2 -1
View File
@@ -2,10 +2,11 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { enhancedImages } from '@sveltejs/enhanced-img'; import { enhancedImages } from '@sveltejs/enhanced-img';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
import wasm from 'vite-plugin-wasm';
export default defineConfig({ export default defineConfig({
ssr: { ssr: {
noExternal: ['gpx'], noExternal: ['gpx'],
}, },
plugins: [enhancedImages(), tailwindcss(), sveltekit()], plugins: [enhancedImages(), tailwindcss(), wasm(), sveltekit()],
}); });