Compare commits

...

4 Commits

Author SHA1 Message Date
Brieuc Dubois d67ca7fa03 Desktop workflow 2024-01-11 18:55:17 +01:00
Brieuc Dubois dc2a9b0f05 Tauri desktop app 2024-01-11 17:24:47 +01:00
Brieuc Dubois 3aad7f716e Fix dev & build differences 2024-01-11 16:57:13 +01:00
Brieuc Dubois 1a76019f1e SvelteKit static adapter 2024-01-11 16:32:48 +01:00
35 changed files with 4070 additions and 79 deletions

View File

@ -0,0 +1,55 @@
name: Tauri based desktop app
on:
push:
tags:
- "v*"
jobs:
publish-tauri-desktop:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Extract tag
uses: olegtarasov/get-tag@v2.1.2
id: tagName
with:
tagRegex: "v(.*)"
- name: Set up node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.0-dev libgtk-3-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install frontend dependencies
run: |
cd frontend
npm install
cd ..
- name: Build desktop app
run: |
cd frontend
npm run tauri build
cd ..
- name: Publish desktop app
uses: akkuman/gitea-release-action@v1
with:
name: Focus ${{ steps.tagName.outputs.tag }}
files: |-
frontend/src-tauri/target/release/focus
frontend/src-tauri/target/release/bundle/deb/*.deb
frontend/src-tauri/target/release/bundle/appimage/*.AppImage

View File

@ -2,17 +2,21 @@ FROM node:20 as frontend-builder
WORKDIR /app WORKDIR /app
COPY . . COPY package.json .
COPY package-lock.json .
RUN npm install RUN npm install
COPY . .
RUN npm run build RUN npm run build
FROM node:20-alpine FROM nginx:alpine
COPY --from=frontend-builder /app . COPY --from=frontend-builder /app/build /usr/share/nginx/html
COPY run.sh .
RUN chmod +x run.sh
EXPOSE 4173 EXPOSE 80
ENV PUBLIC_BACKEND_URL=http://localhost:3000 ENV PUBLIC_BACKEND_URL=http://localhost:3000
CMD ["npm", "run", "preview", "--", "--port", "4173", "--host", "0.0.0.0"] CMD ["./run.sh"]

View File

@ -15,9 +15,10 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.28.1", "@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tauri-apps/cli": "^1.5.9",
"@types/eslint": "8.56.0", "@types/eslint": "8.56.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",
@ -824,14 +825,11 @@
"win32" "win32"
] ]
}, },
"node_modules/@sveltejs/adapter-auto": { "node_modules/@sveltejs/adapter-static": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.1.tgz",
"integrity": "sha512-OpilmvRN136lUgOa9F0zpSI6g+PouOmk+YvJQrB+/hAtllLghjjYuoyfUsrF7U6oJ52cxCtAJTPXgZdyyCffrQ==", "integrity": "sha512-6lMvf7xYEJ+oGeR5L8DFJJrowkefTK6ZgA4JiMqoClMkKq0s6yvsd3FZfCFvX1fQ0tpCD7fkuRVHsnUVgsHyNg==",
"dev": true, "dev": true,
"dependencies": {
"import-meta-resolve": "^4.0.0"
},
"peerDependencies": { "peerDependencies": {
"@sveltejs/kit": "^2.0.0" "@sveltejs/kit": "^2.0.0"
} }
@ -906,6 +904,194 @@
"vite": "^5.0.0" "vite": "^5.0.0"
} }
}, },
"node_modules/@tauri-apps/cli": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-1.5.9.tgz",
"integrity": "sha512-knSt/9AvCTeyfC6wkyeouF9hBW/0Mzuw+5vBKEvzaGPQsfFJo1ZCp5FkdiZpGBBfnm09BhugasGRTGofzatfqQ==",
"dev": true,
"bin": {
"tauri": "tauri.js"
},
"engines": {
"node": ">= 10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/tauri"
},
"optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "1.5.9",
"@tauri-apps/cli-darwin-x64": "1.5.9",
"@tauri-apps/cli-linux-arm-gnueabihf": "1.5.9",
"@tauri-apps/cli-linux-arm64-gnu": "1.5.9",
"@tauri-apps/cli-linux-arm64-musl": "1.5.9",
"@tauri-apps/cli-linux-x64-gnu": "1.5.9",
"@tauri-apps/cli-linux-x64-musl": "1.5.9",
"@tauri-apps/cli-win32-arm64-msvc": "1.5.9",
"@tauri-apps/cli-win32-ia32-msvc": "1.5.9",
"@tauri-apps/cli-win32-x64-msvc": "1.5.9"
}
},
"node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-1.5.9.tgz",
"integrity": "sha512-7C2Jf8f0gzv778mLYb7Eszqqv1bm9Wzews81MRTqKrUIcC+eZEtDXLex+JaEkEzFEUrgIafdOvMBVEavF030IA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-darwin-x64": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-1.5.9.tgz",
"integrity": "sha512-LHKytpkofPYgH8RShWvwDa3hD1ws131x7g7zNasJPfOiCWLqYVQFUuQVmjEUt8+dpHe/P/err5h4z+YZru2d0A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-1.5.9.tgz",
"integrity": "sha512-teGK20IYKx+dVn8wFq/Lg57Q9ce7foq1KHSfyHi464LVt1T0V1rsmULSgZpQPPj/NYPF5BG78PcWYv64yH86jw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-1.5.9.tgz",
"integrity": "sha512-onJ/DW5Crw38qVx+wquY4uBbfCxVhzhdJmlCYqnYyXsZZmSiPUfSyhV58y+5TYB0q1hG8eYdB5x8VAwzByhGzw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.9.tgz",
"integrity": "sha512-23AYoLD3acakLp9NtheKQDJl8F66eTOflxoPzdJNRy13hUSxb+W9qpz4rRA+CIzkjICFvO2i3UWjeV9QwDVpsQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-1.5.9.tgz",
"integrity": "sha512-9PQA1rE7gh41W2ylyKd5qOGOds55ymaYPml9KOpM0g+cxmCXa+8Wf9K5NKvACnJldJJ6cekWzIyB4eN6o5T+yQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.9.tgz",
"integrity": "sha512-5hdbNFeDsrJ/pXZ4cSQV4bJwUXPPxXxN3/pAtNUqIph7q+vLcBXOXIMoS64iuyaluJC59lhEwlWZFz+EPv0Hqg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-1.5.9.tgz",
"integrity": "sha512-O18JufjSB3hSJYu5WWByONouGeX7DraLAtXLErsG1r/VS3zHd/zyuzycrVUaObNXk5bfGlIP0Ypt+RvZJILN2w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-1.5.9.tgz",
"integrity": "sha512-FQxtxTZu0JVBihfd/lmpxo7jyMOesjWQehfyVUqtgMfm5+Pvvw0Y+ZioeDi1TZkFVrT3QDYy8R4LqDLSZVMQRA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "1.5.9",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-1.5.9.tgz",
"integrity": "sha512-EeI1+L518cIBLKw0qUFwnLIySBeSmPQjPLIlNwSukHSro4tAQPHycEVGgKrdToiCWgaZJBA0e5aRSds0Du2TWg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@types/cookie": { "node_modules/@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@ -2242,16 +2428,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/import-meta-resolve": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
"integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==",
"dev": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/imurmurhash": { "node_modules/imurmurhash": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",

View File

@ -11,13 +11,15 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write ." "format": "prettier --write .",
"tauri": "tauri"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.28.1", "@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0",
"@tauri-apps/cli": "^1.5.9",
"@types/eslint": "8.56.0", "@types/eslint": "8.56.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",

5
frontend/run.sh Normal file
View File

@ -0,0 +1,5 @@
PUBLIC_BACKEND_URL=${PUBLIC_BACKEND_URL:-http://localhost:3000}
find /usr/share/nginx/html -type f -exec sed -i "s|http://localhost:3000|$PUBLIC_BACKEND_URL|g" {} +
nginx -g 'daemon off;'

3
frontend/src-tauri/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

3673
frontend/src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.5.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.5.4", features = [] }
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
# DO NOT REMOVE!!
custom-protocol = [ "tauri/custom-protocol" ]

View File

@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -0,0 +1,66 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:5173",
"distDir": "../static"
},
"package": {
"productName": "Focus",
"version": "0.1.0"
},
"tauri": {
"allowlist": {
"all": false
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"identifier": "com.bhasher.focus",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 600,
"resizable": true,
"title": "Focus",
"width": 800
}
]
}
}

View File

@ -40,10 +40,10 @@
{:else} {:else}
<div <div
class="title" class="title"
on:click={() => (location.href = `/project/${project.id}`)} on:click={() => (location.href = location.origin + `/project/?id=${project.id}`)}
on:keydown={(e) => { on:keydown={(e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
location.href = `/project/${project.id}`; location.href = location.origin + `/project/?id=${project.id}`;
} }
}} }}
tabindex="0" tabindex="0"

View File

@ -1,9 +1,10 @@
import axios, { Axios, type AxiosResponse } from 'axios'; import axios, { Axios, type AxiosResponse } from 'axios';
import { toastAlert } from './toasts'; import { toastAlert } from './toasts';
import { setupCache } from 'axios-cache-interceptor'; import { setupCache } from 'axios-cache-interceptor';
import { env } from '$env/dynamic/public'; // import { env } from '$env/dynamic/public';
const backend = env.PUBLIC_BACKEND_URL || 'http://localhost:3000'; // const backend = env.PUBLIC_BACKEND_URL || 'http://localhost:3000';
const backend = 'http://localhost:3000';
export default setupCache( export default setupCache(
new Axios({ new Axios({

View File

@ -0,0 +1,3 @@
export const prerender = true;
export const ssr = false;
export const trailingSlash = 'always';

View File

@ -1 +0,0 @@
export const ssr = false;

View File

@ -1,45 +0,0 @@
import projectsApi from '$lib/api/projectsApi';
interface Shortcut {
name: string;
description: string;
url: string;
icons: {
src: string;
sizes: string;
}[];
}
export async function GET() {
const icon = {
src: '/img/icon.svg',
type: 'image/svg+xml',
sizes: 'any'
};
const projects = await projectsApi.getAll();
const shortcuts: Shortcut[] = projects.map((project) => {
return {
name: `Project ${project.title}`,
description: `Shortcut for project ${project.title}`,
url: `/${project.id}`,
icons: [icon]
};
});
const manifest = {
short_name: 'Focus',
name: 'Focus',
start_url: '/',
display: 'standalone',
icons: [icon],
shortcuts
};
return new Response(JSON.stringify(manifest), {
headers: {
'Content-Type': 'application/manifest+json'
}
});
}

View File

@ -1,17 +1,16 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores';
import projectsApi from '$lib/api/projectsApi'; import projectsApi from '$lib/api/projectsApi';
import Sidebar from '$lib/components/project/Sidebar.svelte'; import Sidebar from '$lib/components/project/Sidebar.svelte';
import type Project from '$lib/types/Project'; import type Project from '$lib/types/Project';
import { SvelteToast } from '@zerodevx/svelte-toast'; import { SvelteToast } from '@zerodevx/svelte-toast';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import ProjectComponent from '$lib/components/project/Project.svelte'; import ProjectComponent from '$lib/components/project/Project.svelte';
import { page } from '$app/stores';
let projectId: number = +$page.params.project;
let project: Project; let project: Project;
onMount(async () => { onMount(async () => {
const projectId = parseInt($page.url.searchParams.get('id') || '0');
const res = await projectsApi.get(projectId); const res = await projectsApi.get(projectId);
if (!res) return; if (!res) return;

View File

@ -0,0 +1,13 @@
{
"short_name": "Focus",
"name": "Focus",
"start_url": "/",
"display": "standalone",
"icons": [
{
"src": "/img/icon.svg",
"type": "image/svg+xml",
"sizes": "any"
}
]
}

View File

@ -1,4 +1,4 @@
import adapter from '@sveltejs/adapter-auto'; import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */ /** @type {import('@sveltejs/kit').Config} */