Set up the Kifu PWA project with decent Makefiles #41
1
Makefile
1
Makefile
|
@ -55,6 +55,7 @@ kifu-pwa:
|
||||||
|
|
||||||
kifu-pwa/dev:
|
kifu-pwa/dev:
|
||||||
pushd kifu/ffi/wasm && make && popd
|
pushd kifu/ffi/wasm && make && popd
|
||||||
|
pushd kifu/core-types && make && popd
|
||||||
pushd kifu/kifu-pwa && make dev
|
pushd kifu/kifu-pwa && make dev
|
||||||
|
|
||||||
kifu-pwa/test-server:
|
kifu-pwa/test-server:
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
SOURCES = $(shell find ../kifu-core -name "*.rs")
|
||||||
|
dist/core.d.ts: $(SOURCES)
|
||||||
|
mkdir -p dist
|
||||||
|
typeshare ../kifu-core --lang=typescript --output-file=dist/core.d.ts
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "core-types",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"types": "dist/core.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "make",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Savanni D'Gerinel <savanni@luminescent-dreams.com>",
|
||||||
|
"license": "GPL-3.0-or-later"
|
||||||
|
}
|
|
@ -172,6 +172,8 @@ name = "kifu-wasm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kifu-core",
|
"kifu-core",
|
||||||
|
"serde",
|
||||||
|
"serde-wasm-bindgen",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
]
|
]
|
||||||
|
@ -270,6 +272,17 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-wasm-bindgen"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.160"
|
version = "1.0.160"
|
||||||
|
|
|
@ -9,9 +9,11 @@ edition = "2021"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kifu-core = { path = "../../kifu-core" }
|
kifu-core = { path = "../../kifu-core" }
|
||||||
wasm-bindgen = "0.2"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
wasm-bindgen-futures = "*"
|
serde-wasm-bindgen = "0.5.0"
|
||||||
|
wasm-bindgen = "0.2"
|
||||||
|
wasm-bindgen-futures = "*"
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.release]
|
[package.metadata.wasm-pack.profile.release]
|
||||||
wasm-opt = false
|
wasm-opt = false
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
// use serde::{Serialize, Deserialize};
|
||||||
|
use kifu_core::{CoreRequest, CoreResponse};
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -8,8 +10,11 @@ extern "C" {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
savanni marked this conversation as resolved
Outdated
|
|||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CoreRequest(kifu_core::Request);
|
pub struct CoreRequest(kifu_core::Request);
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
savanni marked this conversation as resolved
Outdated
savanni
commented
Remove Remove
|
|||||||
impl CoreRequest {
|
impl CoreRequest {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
|
@ -22,22 +27,26 @@ impl CoreRequest {
|
||||||
/* Somehow uncommenting this code actually causes the module to not load. Maybe a name conflict?
|
/* Somehow uncommenting this code actually causes the module to not load. Maybe a name conflict?
|
||||||
savanni marked this conversation as resolved
Outdated
savanni
commented
Comment is now obsolete Comment is now obsolete
|
|||||||
* Don't know.
|
* Don't know.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
savanni marked this conversation as resolved
Outdated
savanni
commented
Remove Remove
|
|||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CoreResponse(kifu_core::Response);
|
pub struct CoreResponse(kifu_core::Response);
|
||||||
|
*/
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CoreApp;
|
pub struct CoreApp(kifu_core::CoreApp);
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl CoreApp {
|
impl CoreApp {
|
||||||
#[wasm_bindgen(constructor)]
|
#[wasm_bindgen(constructor)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self
|
Self(kifu_core::CoreApp::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn dispatch(&self, param: &JsValue) {
|
pub async fn dispatch(&self, param: &JsValue) -> JsValue {
|
||||||
log(&format!("disptach! {:?}", param));
|
let request: CoreRequest = serde_wasm_bindgen::from_value(param.clone()).unwrap();
|
||||||
|
serde_wasm_bindgen::to_value(&self.0.dispatch(request).await).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use typeshare::typeshare;
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum Request {
|
pub enum CoreRequest {
|
||||||
PlayingField,
|
PlayingField,
|
||||||
PlayStoneRequest(PlayStoneRequest),
|
PlayStoneRequest(PlayStoneRequest),
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,14 @@ pub enum Request {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct PlayStoneRequest {
|
pub struct PlayStoneRequest {
|
||||||
pub column: usize,
|
pub column: u8,
|
||||||
pub row: usize,
|
pub row: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum Response {
|
pub enum CoreResponse {
|
||||||
PlayingFieldView(PlayingFieldView),
|
PlayingFieldView(PlayingFieldView),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,19 +38,19 @@ impl CoreApp {
|
||||||
Self { state }
|
Self { state }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn dispatch(&self, request: Request) -> Response {
|
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||||
match request {
|
match request {
|
||||||
Request::PlayingField => {
|
CoreRequest::PlayingField => {
|
||||||
let app_state = self.state.read().unwrap();
|
let app_state = self.state.read().unwrap();
|
||||||
let game = app_state.game.as_ref().unwrap();
|
let game = app_state.game.as_ref().unwrap();
|
||||||
Response::PlayingFieldView(playing_field(game))
|
CoreResponse::PlayingFieldView(playing_field(game))
|
||||||
}
|
}
|
||||||
Request::PlayStoneRequest(request) => {
|
CoreRequest::PlayStoneRequest(request) => {
|
||||||
let mut app_state = self.state.write().unwrap();
|
let mut app_state = self.state.write().unwrap();
|
||||||
app_state.place_stone(request);
|
app_state.place_stone(request);
|
||||||
|
|
||||||
let game = app_state.game.as_ref().unwrap();
|
let game = app_state.game.as_ref().unwrap();
|
||||||
Response::PlayingFieldView(playing_field(game))
|
CoreResponse::PlayingFieldView(playing_field(game))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,8 +73,8 @@ impl Board {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
|
||||||
pub struct Coordinate {
|
pub struct Coordinate {
|
||||||
pub column: usize,
|
pub column: u8,
|
||||||
pub row: usize,
|
pub row: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Board {
|
impl Board {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod api;
|
mod api;
|
||||||
pub use api::{CoreApp, Request, Response};
|
pub use api::{CoreApp, CoreRequest, CoreResponse};
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
pub use types::{BoardError, Color, Size};
|
pub use types::{BoardError, Color, Size};
|
||||||
|
|
|
@ -27,8 +27,8 @@ pub enum Color {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
pub width: usize,
|
pub width: u8,
|
||||||
pub height: usize,
|
pub height: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Size {
|
impl Default for Size {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::types::{Color, Size};
|
use crate::types::{Color, Size};
|
||||||
use crate::{
|
use crate::{
|
||||||
api::{PlayStoneRequest, Request},
|
api::{CoreRequest, PlayStoneRequest},
|
||||||
Board, Coordinate,
|
Board, Coordinate,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -36,7 +36,7 @@ impl StoneElement {
|
||||||
#[serde(tag = "type", content = "content")]
|
#[serde(tag = "type", content = "content")]
|
||||||
pub enum IntersectionElement {
|
pub enum IntersectionElement {
|
||||||
Unplayable,
|
Unplayable,
|
||||||
Empty(Request),
|
Empty(CoreRequest),
|
||||||
Filled(StoneElement),
|
Filled(StoneElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +53,9 @@ impl BoardElement {
|
||||||
.map(|row| {
|
.map(|row| {
|
||||||
(0..size.width)
|
(0..size.width)
|
||||||
.map(|column| {
|
.map(|column| {
|
||||||
IntersectionElement::Empty(Request::PlayStoneRequest(PlayStoneRequest {
|
IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
||||||
column,
|
PlayStoneRequest { column, row },
|
||||||
row,
|
))
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<IntersectionElement>>()
|
.collect::<Vec<IntersectionElement>>()
|
||||||
})
|
})
|
||||||
|
@ -86,7 +85,7 @@ impl From<&Board> for BoardElement {
|
||||||
liberties: None,
|
liberties: None,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
None => IntersectionElement::Empty(Request::PlayStoneRequest(
|
None => IntersectionElement::Empty(CoreRequest::PlayStoneRequest(
|
||||||
PlayStoneRequest { column, row },
|
PlayStoneRequest { column, row },
|
||||||
)),
|
)),
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,3 +4,4 @@ dev:
|
||||||
|
|
||||||
test-server:
|
test-server:
|
||||||
npx http-server ./dist
|
npx http-server ./dist
|
||||||
|
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
Generated by typeshare 1.5.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface PlayStoneRequest {
|
|
||||||
column: number;
|
|
||||||
row: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Size {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntersectionElement =
|
|
||||||
| { type: "Unplayable", content?: undefined }
|
|
||||||
| { type: "Empty", content: Request }
|
|
||||||
| { type: "Filled", content: StoneElement };
|
|
||||||
|
|
||||||
export interface BoardElement {
|
|
||||||
size: Size;
|
|
||||||
spaces: IntersectionElement[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Color {
|
|
||||||
Black = "Black",
|
|
||||||
White = "White",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlayerCardElement {
|
|
||||||
color: Color;
|
|
||||||
name: string;
|
|
||||||
rank: string;
|
|
||||||
clock: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChatElement {
|
|
||||||
messages: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextFieldElement {
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlayingFieldView {
|
|
||||||
board: BoardElement;
|
|
||||||
player_card_black: PlayerCardElement;
|
|
||||||
player_card_white: PlayerCardElement;
|
|
||||||
chat: ChatElement;
|
|
||||||
message: TextFieldElement;
|
|
||||||
current_player: Color;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Jitter {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoneElement {
|
|
||||||
color: Color;
|
|
||||||
jitter: Jitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Request =
|
|
||||||
| { type: "PlayingField", content?: undefined }
|
|
||||||
| { type: "PlayStoneRequest", content: PlayStoneRequest };
|
|
||||||
|
|
||||||
export type Response =
|
|
||||||
| { type: "PlayingFieldView", content: PlayingFieldView };
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
import init, { CoreApp } from "kifu-wasm";
|
|
||||||
|
|
||||||
const inputField = document.getElementById("input-temp");
|
|
||||||
const fromUnitField = document.getElementById("input-unit");
|
|
||||||
const toUnitField = document.getElementById("output-unit");
|
|
||||||
const outputField = document.getElementById("output-temp");
|
|
||||||
const form = document.getElementById("converter");
|
|
||||||
|
|
||||||
function convertTemp(value, fromUnit, toUnit) {
|
|
||||||
if (fromUnit === "c") {
|
|
||||||
if (toUnit === "f") {
|
|
||||||
return (value * 9) / 5 + 32;
|
|
||||||
} else if (toUnit === "k") {
|
|
||||||
return value + 273.15;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (fromUnit === "f") {
|
|
||||||
if (toUnit === "c") {
|
|
||||||
return ((value - 32) * 5) / 9;
|
|
||||||
} else if (toUnit === "k") {
|
|
||||||
return ((value + 459.67) * 5) / 9;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (fromUnit === "k") {
|
|
||||||
if (toUnit === "c") {
|
|
||||||
return value - 273.15;
|
|
||||||
} else if (toUnit === "f") {
|
|
||||||
return (value * 9) / 5 - 459.67;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
throw new Error("Invalid unit");
|
|
||||||
}
|
|
||||||
|
|
||||||
form.addEventListener("input", () => {
|
|
||||||
const inputTemp = parseFloat(inputField.value);
|
|
||||||
const fromUnit = fromUnitField.value;
|
|
||||||
const toUnit = toUnitField.value;
|
|
||||||
|
|
||||||
const outputTemp = convertTemp(inputTemp, fromUnit, toUnit);
|
|
||||||
outputField.value =
|
|
||||||
Math.round(outputTemp * 100) / 100 + " " + toUnit.toUpperCase();
|
|
||||||
});
|
|
||||||
|
|
||||||
init().then(async () => {
|
|
||||||
let app = new CoreApp();
|
|
||||||
console.log("app: ", app, CoreApp);
|
|
||||||
await app.dispatch({ type: "PlayingField" });
|
|
||||||
console.log("kifu_wasm successfully initted");
|
|
||||||
});
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import init, { CoreApp } from "kifu-wasm";
|
||||||
|
import { CoreResponse, CoreRequest, PlayingFieldView } from "core-types";
|
||||||
|
|
||||||
|
export class CoreApi {
|
||||||
|
core: CoreApp;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let app = new CoreApp();
|
||||||
|
this.core = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispatch(request: CoreRequest): Promise<CoreResponse> {
|
||||||
|
return await this.core.dispatch(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
async playingField(): Promise<PlayingFieldView> {
|
||||||
|
return (await this.dispatch({ type: "PlayingField" })).content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initCore = async (): Promise<CoreApi> => {
|
||||||
|
await init();
|
||||||
|
return new CoreApi();
|
||||||
|
};
|
|
@ -1,10 +1,8 @@
|
||||||
import init, { CoreApp } from "kifu-wasm";
|
import { CoreApi, initCore } from "./coreApi";
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
await init();
|
let coreApi = await initCore();
|
||||||
let coreApi = new CoreApp();
|
let response = await coreApi.playingField();
|
||||||
|
|
||||||
let response = await coreApi.dispatch({ type: "PlayingField" });
|
|
||||||
console.log("playing field response: ", response);
|
console.log("playing field response: ", response);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ module.exports = {
|
||||||
"kifu-bundle": "./src/main.ts",
|
"kifu-bundle": "./src/main.ts",
|
||||||
sw: "./src/sw.js",
|
sw: "./src/sw.js",
|
||||||
},
|
},
|
||||||
loader: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{ test: /.ts$/, use: "ts-loader", exclude: /node_modules/ },
|
{ test: /\.ts$/, use: "ts-loader", exclude: /node_modules/ },
|
||||||
{ test: /\.wasm$/, type: "asset/inline" }
|
{ test: /\.wasm$/, type: "asset/inline" }
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -19,5 +19,8 @@ module.exports = {
|
||||||
{ from: "src/converter.css" }
|
{ from: "src/converter.css" }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
]
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts'],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
"name": "tools",
|
"name": "tools",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"kifu/ffi/wasm/pkg",
|
"kifu/ffi/wasm/pkg",
|
||||||
|
"kifu/core-types",
|
||||||
"kifu/kifu-pwa"
|
"kifu/kifu-pwa"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"kifu/core-types": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "GPL-3.0-or-later"
|
||||||
|
},
|
||||||
"kifu/ffi/wasm/pkg": {
|
"kifu/ffi/wasm/pkg": {
|
||||||
|
"name": "kifu-wasm",
|
||||||
"version": "0.1.0"
|
"version": "0.1.0"
|
||||||
},
|
},
|
||||||
"kifu/kifu-pwa": {
|
"kifu/kifu-pwa": {
|
||||||
|
@ -616,6 +622,10 @@
|
||||||
"webpack": "^5.1.0"
|
"webpack": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/core-types": {
|
||||||
|
"resolved": "kifu/core-types",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"name": "tools",
|
"name": "tools",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"kifu/ffi/wasm/pkg",
|
"kifu/ffi/wasm/pkg",
|
||||||
|
"kifu/core-types",
|
||||||
"kifu/kifu-pwa"
|
"kifu/kifu-pwa"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Remove