Upload files to "/"
This commit is contained in:
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Life Manager
|
||||
Categorize and manage your life, with calendar and instant lexical search of titles, tags, and the description content of each card.
|
||||
|
||||
Select a category, enter info, track info/tasks/projects, timely calendar included.
|
||||
|
||||
Built web only version for Netlify deploy and hosting with Nginx etc. try it out: https://lifeman.mhn.lol
|
||||
Supports backing up and loading data to/from device. *Note clearing cookie or site data will clear the database as it is stored in browser localstorage. So, BACKUP! Your database will persist as long as you don't delete browser cookies/site data. Source is in web folder. Template for JS instant lexical search including title, description, and tags of cards. Included calendar system tied to search, along with the topical tagging system(multi-selected). <-- *nts work on description of this..
|
||||
|
||||
## Quick Start🏁
|
||||
glibc 2.35+ (Ubuntu 22+) binary ready
|
||||
- ```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/mrhappynice/lifeman/main/install.sh | bash
|
||||
```
|
||||
enter ```lifeman``` folder and run: ```./lifeman``` - sample cards included, delete data.json and restart app for clean db
|
||||
- windows install just:
|
||||
```sh
|
||||
git clone https://github.com/mrhappynice/lifeman.git
|
||||
```
|
||||
and download exe, put in lifeman folder. run ```.\lifeman.exe``` in terminal (or double click exe *shrugs* lol) connect http://localhost:3030
|
||||
|
||||
Build:
|
||||
```sh
|
||||
cargo build --release
|
||||
```
|
||||
62
install.sh
Normal file
62
install.sh
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
BINARY_URL="https://github.com/mrhappynice/lifeman/releases/download/v0.2/lifeman"
|
||||
JSON_URL="https://github.com/mrhappynice/lifeman/raw/refs/heads/main/data.json"
|
||||
INSTALL_DIR="lifeman"
|
||||
BINARY_NAME="lifeman"
|
||||
JSON_NAME="data.json"
|
||||
INDEX_NAME="index.html"
|
||||
SCRIPT_NAME="script.js"
|
||||
CSS_NAME="style.css"
|
||||
INDEX_URL="https://github.com/mrhappynice/lifeman/raw/refs/heads/main/static/index.html"
|
||||
SCRIPT_URL="https://github.com/mrhappynice/lifeman/raw/refs/heads/main/static/script.js"
|
||||
CSS_URL="https://github.com/mrhappynice/lifeman/raw/refs/heads/main/static/style.css"
|
||||
|
||||
|
||||
|
||||
# --- helpers ---
|
||||
have() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
download() {
|
||||
# $1 = url, $2 = output file
|
||||
if have curl; then
|
||||
curl -fL --proto '=https' --tlsv1.2 --retry 3 --retry-delay 1 --progress-bar -o "$2" "$1"
|
||||
elif have wget; then
|
||||
wget --https-only --tries=3 -O "$2" "$1"
|
||||
else
|
||||
echo "Error: need 'curl' or 'wget' to download files." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- work ---
|
||||
echo "Creating '${INSTALL_DIR}' (if needed)…"
|
||||
mkdir -p "${INSTALL_DIR}"
|
||||
cd "${INSTALL_DIR}"
|
||||
|
||||
echo "Downloading binary -> ${BINARY_NAME}"
|
||||
download "${BINARY_URL}" "${BINARY_NAME}"
|
||||
|
||||
echo "Downloading json -> ${JSON_NAME}"
|
||||
download "${JSON_URL}" "${JSON_NAME}"
|
||||
|
||||
echo "Making '${BINARY_NAME}' executable…"
|
||||
chmod +x "${BINARY_NAME}"
|
||||
|
||||
echo "Getting UI files.."
|
||||
mkdir static
|
||||
cd static
|
||||
download "${INDEX_URL}" "${INDEX_NAME}"
|
||||
download "${SCRIPT_URL}" "${SCRIPT_NAME}"
|
||||
download "${CSS_URL}" "${CSS_NAME}"
|
||||
|
||||
echo "Done ✅"
|
||||
echo
|
||||
echo "Files installed to: $(pwd)"
|
||||
echo " - ${BINARY_NAME}"
|
||||
echo " - ${JSON_NAME}"
|
||||
echo
|
||||
echo "Run it with:"
|
||||
echo " ./$(printf %q "${BINARY_NAME}")"
|
||||
425
pwa-notes.md
Normal file
425
pwa-notes.md
Normal file
@@ -0,0 +1,425 @@
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Part 1 – Turn your current app into a PWA
|
||||
|
||||
You already have:
|
||||
|
||||
* `index.html`
|
||||
* `style.css`
|
||||
* `script.js`
|
||||
|
||||
We’ll add:
|
||||
|
||||
* `manifest.webmanifest`
|
||||
* `sw.js`
|
||||
* A couple of small changes to `index.html`
|
||||
|
||||
I’ll assume your app is served from the site root (e.g. `https://mydomain.com/`) and that your icon is a PNG. You can keep everything inside `web/` in your repo; when you deploy, make sure these files end up in the same directory as `index.html`.
|
||||
|
||||
---
|
||||
|
||||
### 1. Create icons from your existing icon file
|
||||
|
||||
Chrome expects at least:
|
||||
|
||||
* **192×192** PNG
|
||||
* **512×512** PNG
|
||||
|
||||
If your icon file is large enough (e.g. 1024×1024), generate:
|
||||
|
||||
* `icons/icon-192.png`
|
||||
* `icons/icon-512.png`
|
||||
|
||||
(Use something like GIMP, Figma, or an online “PWA icon generator”.)
|
||||
|
||||
Folder structure (in `web/`):
|
||||
|
||||
```text
|
||||
web/
|
||||
index.html
|
||||
style.css
|
||||
script.js
|
||||
sw.js
|
||||
manifest.webmanifest
|
||||
icons/
|
||||
icon-192.png
|
||||
icon-512.png
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Add `manifest.webmanifest`
|
||||
|
||||
Create `web/manifest.webmanifest`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Life Manager",
|
||||
"short_name": "LifeMgr",
|
||||
"description": "Offline-first life organizer with calendar, tags, and backups.",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#0f1014",
|
||||
"theme_color": "#0f1014",
|
||||
"orientation": "portrait",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> `start_url` and `scope` assume the app is at the root. If you serve from `/life-manager/`, change both to `"/life-manager/"`.
|
||||
|
||||
---
|
||||
|
||||
### 3. Wire manifest + theme color into `index.html`
|
||||
|
||||
Modify your `<head>` in `web/index.html` to include the manifest and theme color.
|
||||
|
||||
Right now you have:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>life-manager</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
```
|
||||
|
||||
Change to:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>life-manager</title>
|
||||
|
||||
<!-- PWA: theme color (matches manifest) -->
|
||||
<meta name="theme-color" content="#0f1014">
|
||||
|
||||
<!-- PWA: manifest -->
|
||||
<link rel="manifest" href="manifest.webmanifest">
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Register a service worker from `index.html`
|
||||
|
||||
Just before `</body>` you already have:
|
||||
|
||||
```html
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Change to:
|
||||
|
||||
```html
|
||||
<script src="script.js"></script>
|
||||
|
||||
<!-- PWA: Service worker registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js')
|
||||
.catch(err => {
|
||||
console.error('SW registration failed', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
> If you host the app under a path (e.g. `/life-manager/`), change the registration path to `'/life-manager/sw.js'`.
|
||||
|
||||
---
|
||||
|
||||
### 5. Add `sw.js` (service worker)
|
||||
|
||||
Create `web/sw.js`:
|
||||
|
||||
```js
|
||||
// sw.js - simple cache-first service worker
|
||||
|
||||
const CACHE_NAME = 'lifemgr-cache-v1';
|
||||
|
||||
// List everything needed for offline
|
||||
const ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/style.css',
|
||||
'/script.js',
|
||||
'/manifest.webmanifest',
|
||||
'/icons/icon-192.png',
|
||||
'/icons/icon-512.png'
|
||||
];
|
||||
|
||||
// Install: cache core assets
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(ASSETS);
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
// Activate: cleanup old caches if you bump the version
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) =>
|
||||
Promise.all(
|
||||
keys.map((key) => {
|
||||
if (key !== CACHE_NAME) {
|
||||
return caches.delete(key);
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
// Fetch: cache-first strategy
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const request = event.request;
|
||||
|
||||
// Only handle GET
|
||||
if (request.method !== 'GET') return;
|
||||
|
||||
event.respondWith(
|
||||
caches.match(request).then((cached) => {
|
||||
// Serve from cache if available, else network
|
||||
return (
|
||||
cached ||
|
||||
fetch(request).catch(() => {
|
||||
// Optional: custom offline response for HTML requests
|
||||
if (request.headers.get('accept')?.includes('text/html')) {
|
||||
return caches.match('/index.html');
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
> Again, if hosted under a sub-path, prepend `/life-manager` in `ASSETS`.
|
||||
|
||||
Now when you deploy over **HTTPS**, Chrome will see:
|
||||
|
||||
* a valid manifest,
|
||||
* a service worker controlling the scope,
|
||||
* and will treat it as a PWA with install capability.
|
||||
|
||||
---
|
||||
|
||||
### 6. Deploy and sanity-check
|
||||
|
||||
1. Deploy `web/` to somewhere HTTPS (Netlify, Vercel, etc.).
|
||||
|
||||
2. Open it in Chrome (Android or desktop).
|
||||
|
||||
3. In DevTools → Application → Manifest/Service Workers:
|
||||
|
||||
* Confirm manifest is found,
|
||||
* service worker is active,
|
||||
* and pages are controlled.
|
||||
|
||||
4. On Android Chrome, you should see **“Install app” / “Add to Home screen”** in the menu.
|
||||
|
||||
Once this works, you’re ready for the **Trusted Web Activity** step.
|
||||
|
||||
---
|
||||
|
||||
## Part 2 – Wrap your PWA in a Trusted Web Activity (TWA) with Bubblewrap
|
||||
|
||||
Now you have a real PWA live at (for example):
|
||||
|
||||
```text
|
||||
https://your-domain.com/
|
||||
```
|
||||
|
||||
We’ll use **Bubblewrap**, which takes your hosted `manifest.webmanifest` and generates an Android project that starts your PWA as a TWA.
|
||||
|
||||
### 1. Prereqs checklist
|
||||
|
||||
You need:
|
||||
|
||||
* Node.js (v14+)
|
||||
|
||||
* Java JDK
|
||||
|
||||
* Android SDK
|
||||
|
||||
* Your PWA served via **HTTPS** with:
|
||||
|
||||
* manifest,
|
||||
* service worker,
|
||||
* and working offline (which we just did).
|
||||
|
||||
And of course, a **Google Play developer account** for publishing later.
|
||||
|
||||
---
|
||||
|
||||
### 2. Install Bubblewrap CLI
|
||||
|
||||
```bash
|
||||
npm install -g @bubblewrap/cli
|
||||
bubblewrap --version
|
||||
```
|
||||
|
||||
(Just to confirm it’s installed.)
|
||||
|
||||
---
|
||||
|
||||
### 3. Initialize a Bubblewrap project from your manifest
|
||||
|
||||
Pick a new folder for the Android project:
|
||||
|
||||
```bash
|
||||
mkdir life-manager-twa
|
||||
cd life-manager-twa
|
||||
```
|
||||
|
||||
Run the init command, pointing to your **hosted** manifest:
|
||||
|
||||
```bash
|
||||
bubblewrap init --manifest https://your-domain.com/manifest.webmanifest
|
||||
```
|
||||
|
||||
Bubblewrap will:
|
||||
|
||||
* Download the web manifest.
|
||||
|
||||
* Ask you about:
|
||||
|
||||
* **Package ID** (e.g. `com.yourdomain.lifemanager`)
|
||||
* **App name** (can reuse `"Life Manager"`)
|
||||
* **Colors, icons, orientation** (it can reuse values from the manifest)
|
||||
* **Signing key** (create a new keystore or use an existing one)
|
||||
|
||||
It will generate:
|
||||
|
||||
* An Android project (Gradle, Java/Kotlin code that opens your URL as a TWA).
|
||||
* A `twa-manifest.json` with config about your app.
|
||||
* An `assetlinks.json` file template for your website.
|
||||
|
||||
---
|
||||
|
||||
### 4. Build the APK/App Bundle
|
||||
|
||||
Inside that new project folder:
|
||||
|
||||
```bash
|
||||
bubblewrap build
|
||||
```
|
||||
|
||||
This compiles the Android project and outputs a release build (APK/App Bundle).
|
||||
|
||||
You can also install directly on a connected device:
|
||||
|
||||
```bash
|
||||
bubblewrap install
|
||||
```
|
||||
|
||||
At this stage, when you open the app on the device:
|
||||
|
||||
* It will usually open as a **Custom Tab** (with a little browser UI) until we set up the trust link (Digital Asset Links).
|
||||
|
||||
---
|
||||
|
||||
### 5. Set up Digital Asset Links (`assetlinks.json`)
|
||||
|
||||
To get true **Trusted Web Activity** (full screen, no browser bar), Android needs to verify that:
|
||||
|
||||
* The app signing key
|
||||
* And your website’s domain
|
||||
|
||||
belong to the same owner, via **Digital Asset Links**.
|
||||
|
||||
Bubblewrap will give you an `assetlinks.json` snippet. If you need a template, it looks like this:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.yourdomain.lifemanager",
|
||||
"sha256_cert_fingerprints": [
|
||||
"AA:BB:CC:DD:EE:FF:..."
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
* Replace `com.yourdomain.lifemanager` with your actual package ID.
|
||||
* Replace the fingerprint with the SHA-256 of the keystore you **actually sign the Play build with** (Bubblewrap/Android Studio docs show how to get this).
|
||||
|
||||
Host this file at:
|
||||
|
||||
```text
|
||||
https://your-domain.com/.well-known/assetlinks.json
|
||||
```
|
||||
|
||||
Once this is accessible and valid:
|
||||
|
||||
* Opening your app on device will show your PWA **full-screen** as a Trusted Web Activity, not a normal Chrome tab.
|
||||
|
||||
---
|
||||
|
||||
### 6. Open in Android Studio (optional but recommended)
|
||||
|
||||
You can open the generated Android project in Android Studio for:
|
||||
|
||||
* Changing app name, icon, version code, etc.
|
||||
* Building signed App Bundles for the Play Store.
|
||||
* Running on emulators.
|
||||
|
||||
Bubblewrap’s `build` and `install` commands already use Gradle under the hood, so this is optional, but very handy for Play Store upload.
|
||||
|
||||
---
|
||||
|
||||
### 7. Upload to Google Play
|
||||
|
||||
Once:
|
||||
|
||||
* PWA runs well over HTTPS.
|
||||
* Asset Links are configured and verified.
|
||||
* The app works as full-screen TWA on a device.
|
||||
|
||||
Then:
|
||||
|
||||
1. In the **Google Play Console**, create a new app.
|
||||
2. Upload the signed **App Bundle/APK** from your Bubblewrap/Android Studio build.
|
||||
3. Fill in listing details (screenshots, description, etc.).
|
||||
4. Roll out a test track or production release.
|
||||
|
||||
---
|
||||
|
||||
|
||||
1
rebuild.sh
Normal file
1
rebuild.sh
Normal file
@@ -0,0 +1 @@
|
||||
cargo clean && cargo build --release
|
||||
Reference in New Issue
Block a user