Compare commits

..

16 Commits

Author SHA1 Message Date
7e0e7e4c29 fix: device label reads Settings.Global.DEVICE_NAME (user-set name), falls back to Build.MODEL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 08:51:39 -04:00
28d96bd3c1 merge: snackbar above keyboard 2026-05-19 08:51:39 -04:00
adc7fb54d9 fix: imePadding on SnackbarHost so toast appears above keyboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 08:51:30 -04:00
4adca319be added icon files 2026-05-19 08:29:26 -04:00
1d31556923 tasks.org: log device label and passphrase token tasks in milestone 6 2026-05-19 08:27:54 -04:00
628a28a775 feat: device label defaults to Build.MODEL; 3-word passphrase token
- SettingsRepository: deviceLabel fallback changed from "android" to
  Build.MODEL so fresh installs show the actual device name (e.g. "Pixel 8")
- server/main.py: _generate_passphrase() replaces secrets.token_hex(32).
  Picks 3 words from a 512-word embedded list, hyphen-separated
  (e.g. "coral-drift-lamp"). ~27 bits entropy, readable at a glance.
  Existing token.txt files are unaffected — only new generation changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 08:27:38 -04:00
fca3ea6f60 tasks.org: add milestone 6 post-ship cleanup log 2026-05-19 08:11:48 -04:00
68deb5cb3e merge: status bar icon fix 2026-05-19 08:11:04 -04:00
edd2c1745f fix: restore enableEdgeToEdge, enforce dark status bar icons via WindowCompat
API 35 forces edge-to-edge regardless; removing enableEdgeToEdge had no effect.
Real fix: keep it, set isAppearanceLightStatusBars=true in SideEffect so
clock/battery/wifi icons are dark and visible on the light TopAppBar background.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 07:58:12 -04:00
4597f92d93 merge: snackbar after sync 2026-05-18 21:04:38 -04:00
a37ef4a794 feat: snackbar after sync (synced N / nothing to sync / server unreachable)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 21:02:51 -04:00
905e618d20 merge: remove enableEdgeToEdge 2026-05-18 20:56:42 -04:00
415742b974 fix: remove enableEdgeToEdge so status bar stays distinct
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:56:36 -04:00
a6af2fe5b9 add app icon, fix manifest icon refs, server logs token on every startup
- mipmap-* and drawable/ icon resources from Image Asset tool
- ic_launcher_background.xml: solid #22569d (matches foreground design)
- AndroidManifest.xml: add android:icon and android:roundIcon attributes
- server/app/main.py: log token on every startup (not just first run)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:48:50 -04:00
23df95dacf merge: token always logged on startup, imePadding on capture screen 2026-05-18 20:48:28 -04:00
16a18be9e3 fix: log token on every startup; imePadding so Save stays above keyboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 20:46:52 -04:00
28 changed files with 616 additions and 10 deletions

View File

@@ -0,0 +1,22 @@
{
"permissions": {
"allow": [
"Bash(\"C:\\\\Users\\\\moses\\\\projects\\\\synq\\\\venv\\\\Scripts\\\\pip.exe\" install *)",
"Bash(\"C:\\\\Users\\\\moses\\\\projects\\\\synq\\\\venv\\\\Scripts\\\\python.exe\" -m pytest tests/ -v)",
"Bash(git add *)",
"Bash(git commit -m ' *)",
"Bash(/synq/venv/bin/python -m pytest /synq/.claude/worktrees/serene-mclean-b76496/server/tests/ -v)",
"Read(//c/mnt/**)",
"Read(//proc/**)",
"Read(//c/Users/moses/projects/synq/venv/Scripts//**)",
"Read(//c/Users/moses/projects/synq/venv/**)",
"Bash(/c/Users/moses/projects/synq/venv/Scripts/python.exe -m pytest tests/ -v)",
"Bash(ls \"/c/Users/moses/AppData/Local/Android/Sdk/platforms/\" 2>/dev/null && echo \"FOUND at AppData/Local/Android/Sdk\")",
"Bash(/c/Users/moses/projects/synq/venv/Scripts/python.exe -m pytest tests/ -q)",
"Bash(git stash *)",
"Bash(git merge *)",
"Bash(git push *)",
"Bash(git commit *)"
]
}
}

View File

@@ -9,8 +9,9 @@
android:theme="@style/Theme.Synq" android:theme="@style/Theme.Synq"
android:allowBackup="true" android:allowBackup="true"
android:supportsRtl="true" android:supportsRtl="true"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -22,7 +22,6 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
val sharedText = if (intent.action == Intent.ACTION_SEND) val sharedText = if (intent.action == Intent.ACTION_SEND)
intent.getStringExtra(Intent.EXTRA_TEXT) else null intent.getStringExtra(Intent.EXTRA_TEXT) else null

View File

@@ -1,6 +1,8 @@
package me.hgsky.synq.data package me.hgsky.synq.data
import android.content.Context import android.content.Context
import android.os.Build
import android.provider.Settings
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
@@ -28,7 +30,10 @@ class SettingsRepository(private val context: Context) {
SynqSettings( SynqSettings(
serverUrl = prefs[KEY_URL] ?: "http://jeeves.mother:8765", serverUrl = prefs[KEY_URL] ?: "http://jeeves.mother:8765",
token = prefs[KEY_TOKEN] ?: "", token = prefs[KEY_TOKEN] ?: "",
deviceLabel = prefs[KEY_DEVICE] ?: "android", deviceLabel = prefs[KEY_DEVICE] ?: (
Settings.Global.getString(context.contentResolver, Settings.Global.DEVICE_NAME)
?.takeIf { it.isNotBlank() } ?: Build.MODEL
),
syncIntervalMinutes = prefs[KEY_INTERVAL] ?: 15, syncIntervalMinutes = prefs[KEY_INTERVAL] ?: 15,
) )
} }

View File

@@ -7,8 +7,9 @@ import me.hgsky.synq.data.db.CaptureDao
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSettings) { suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSettings): Int {
val pending = dao.getPendingAndFailed() val pending = dao.getPendingAndFailed()
var synced = 0
for (capture in pending) { for (capture in pending) {
dao.updateStatus(capture.id, "syncing", null) dao.updateStatus(capture.id, "syncing", null)
val result = client.postCapture(capture, settings) val result = client.postCapture(capture, settings)
@@ -16,8 +17,10 @@ suspend fun syncPending(dao: CaptureDao, client: SynqApiClient, settings: SynqSe
is PostResult.Accepted, is PostResult.AlreadySeen -> { is PostResult.Accepted, is PostResult.AlreadySeen -> {
val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) val now = OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
dao.markSynced(capture.id, now) dao.markSynced(capture.id, now)
synced++
} }
is PostResult.Failed -> dao.updateStatus(capture.id, "failed", result.error) is PostResult.Failed -> dao.updateStatus(capture.id, "failed", result.error)
} }
} }
return synced
} }

View File

@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@@ -29,6 +30,8 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
@@ -61,9 +64,13 @@ fun CaptureScreen(
val recentTags by vm.recentTags.collectAsState() val recentTags by vm.recentTags.collectAsState()
val lastSyncedAt by vm.lastSyncedAt.collectAsState() val lastSyncedAt by vm.lastSyncedAt.collectAsState()
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val snackbarState = remember { SnackbarHostState() }
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(Unit) { focusRequester.requestFocus() } LaunchedEffect(Unit) { focusRequester.requestFocus() }
LaunchedEffect(Unit) {
vm.snackbar.collect { msg -> snackbarState.showSnackbar(msg) }
}
LaunchedEffect(prefill) { LaunchedEffect(prefill) {
if (!prefill.isNullOrEmpty()) vm.setBody(prefill) if (!prefill.isNullOrEmpty()) vm.setBody(prefill)
} }
@@ -82,6 +89,7 @@ fun CaptureScreen(
} }
Scaffold( Scaffold(
snackbarHost = { SnackbarHost(snackbarState, modifier = Modifier.imePadding()) },
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("synq") }, title = { Text("synq") },
@@ -113,6 +121,7 @@ fun CaptureScreen(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(padding) .padding(padding)
.imePadding()
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {

View File

@@ -4,9 +4,11 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@@ -37,6 +39,9 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
private val _state = MutableStateFlow(CaptureUiState()) private val _state = MutableStateFlow(CaptureUiState())
val state: StateFlow<CaptureUiState> = _state.asStateFlow() val state: StateFlow<CaptureUiState> = _state.asStateFlow()
private val _snackbar = MutableSharedFlow<String>(extraBufferCapacity = 1)
val snackbar = _snackbar.asSharedFlow()
val recentTags: StateFlow<List<String>> = dao.getRecentTagsJson() val recentTags: StateFlow<List<String>> = dao.getRecentTagsJson()
.map { jsonList -> .map { jsonList ->
val seen = LinkedHashSet<String>() val seen = LinkedHashSet<String>()
@@ -93,9 +98,12 @@ class CaptureViewModel(app: Application) : AndroidViewModel(app) {
try { try {
val settings = synqApp.settings.settings.first() val settings = synqApp.settings.settings.first()
val client = SynqApiClient() val client = SynqApiClient()
if (client.checkHealth(settings.serverUrl)) { if (!client.checkHealth(settings.serverUrl)) {
syncPending(dao, client, settings) _snackbar.tryEmit("server unreachable")
return@launch
} }
val synced = syncPending(dao, client, settings)
_snackbar.tryEmit(if (synced == 0) "nothing to sync" else "synced $synced")
} finally { } finally {
_state.value = _state.value.copy(isSyncing = false) _state.value = _state.value.copy(isSyncing = false)
} }

View File

@@ -1,14 +1,27 @@
package me.hgsky.synq.ui.theme package me.hgsky.synq.ui.theme
import android.app.Activity
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
@Composable @Composable
fun SynqTheme(content: @Composable () -> Unit) { fun SynqTheme(content: @Composable () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val colorScheme = dynamicLightColorScheme(context) val colorScheme = dynamicLightColorScheme(context)
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
// light theme → dark icons so clock/battery/wifi are visible on light TopAppBar
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true
}
}
MaterialTheme(colorScheme = colorScheme, content = content) MaterialTheme(colorScheme = colorScheme, content = content)
} }

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:fillColor="#22569d" android:pathData="M0,0h108v108h-108z"/>
</vector>

View File

@@ -0,0 +1,48 @@
<!--
~ Copyright (C) 2026 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="m189.14,130.52h133.72c7.25,0 13.08,5.83 13.08,13.08v220.93c0,7.25 -5.83,13.08 -13.08,13.08H189.14c-7.25,0 -13.08,-5.83 -13.08,-13.08V143.6c0,-7.25 5.83,-13.08 13.08,-13.08z"
android:strokeWidth="2.90698"
android:fillColor="#ffffff"
android:strokeColor="#000000"/>
<path
android:fillColor="#FF000000"
android:pathData="m222.27,361.68h66.86c1.61,0 2.91,1.3 2.91,2.91 0,1.61 -1.3,2.91 -2.91,2.91h-66.86c-1.61,0 -2.91,-1.3 -2.91,-2.91 0,-1.61 1.3,-2.91 2.91,-2.91z"
android:strokeWidth="2.90698"
android:strokeColor="#000000"/>
<path
android:pathData="M193.63,145.03H318.63c1.61,0 2.91,1.3 2.91,2.91v197.67c0,1.61 -1.3,2.91 -2.91,2.91H193.63c-1.61,0 -2.91,-1.3 -2.91,-2.91V147.94c0,-1.61 1.3,-2.91 2.91,-2.91z"
android:strokeWidth="2.90698"
android:fillColor="#22569d"
android:strokeColor="#000000"/>
<path
android:pathData="m210.25,196.07h-11.2v-42.2h11.2v3.18h-7.43v35.84h7.43zM241.81,186.76h-5.11l-7.22,-11.99 -7.24,11.99h-5.04l9.88,-15.17 -9.08,-14.51h4.79l6.79,11.17 6.84,-11.17h4.66l-9.11,14.33zM260.1,196.07h-11.2v-3.18h7.38v-35.84h-7.38v-3.18h11.2zM280.54,179.61q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3zM306.11,179.61q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3z"
android:strokeWidth="0.726744"
android:fillColor="#00e219"/>
<path
android:pathData="m210.25,256.17h-11.2L199.05,213.98h11.2v3.18h-7.43v35.84h7.43zM241.81,246.86h-5.11l-7.22,-11.99 -7.24,11.99h-5.04l9.88,-15.17 -9.08,-14.51h4.79l6.79,11.17 6.84,-11.17h4.66l-9.11,14.33zM260.1,256.17h-11.2v-3.18h7.38v-35.84h-7.38v-3.18h11.2zM280.54,239.71q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3zM306.11,239.71q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3z"
android:strokeWidth="0.726744"
android:fillColor="#00e219"/>
<path
android:pathData="m210.25,316.28h-11.2v-42.2h11.2v3.18h-7.43v35.84h7.43zM260.1,316.28h-11.2v-3.18h7.38v-35.84h-7.38v-3.18h11.2zM280.54,299.81q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3zM306.11,299.81q0.77,0 1.45,0.3 0.7,0.3 1.2,0.82 0.52,0.52 0.82,1.23 0.3,0.68 0.3,1.48 0,0.77 -0.3,1.45 -0.3,0.68 -0.82,1.2 -0.5,0.5 -1.2,0.79 -0.68,0.3 -1.45,0.3 -0.79,0 -1.48,-0.3 -0.68,-0.3 -1.2,-0.79 -0.5,-0.52 -0.79,-1.2 -0.3,-0.68 -0.3,-1.45 0,-0.79 0.3,-1.48 0.3,-0.7 0.79,-1.23 0.52,-0.52 1.2,-0.82 0.68,-0.3 1.48,-0.3z"
android:strokeWidth="0.726744"
android:fillColor="#00e219"/>
</vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

86
logo/icon-512.svg Normal file
View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title4">synq</title>
<defs
id="defs1" />
<g
id="layer1">
<rect
style="display:inline;fill:#22569d;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect5"
width="512"
height="512"
x="0"
y="0.95533592"
rx="96"
ry="96" />
<path
id="rect6"
style="fill:#ffffff;stroke:#000000;stroke-width:2.90698"
d="m 189.13953,130.52304 h 133.72094 c 7.24709,0 13.08139,5.8343 13.08139,13.0814 v 220.93022 c 0,7.2471 -5.8343,13.0814 -13.08139,13.0814 H 189.13953 c -7.24709,0 -13.08139,-5.8343 -13.08139,-13.0814 V 143.60444 c 0,-7.2471 5.8343,-13.0814 13.08139,-13.0814 z" />
<path
id="rect8"
style="display:inline;stroke:#000000;stroke-width:2.90698"
d="m 222.26527,361.685 h 66.86046 c 1.61047,0 2.90698,1.29651 2.90698,2.90697 0,1.61047 -1.29651,2.90698 -2.90698,2.90698 h -66.86046 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90698 0,-1.61046 1.29651,-2.90697 2.90698,-2.90697 z" />
<path
id="rect9"
style="display:inline;fill:#22569d;stroke:#000000;stroke-width:2.90698"
d="M 193.63011,145.02974 H 318.6301 c 1.61047,0 2.90698,1.29651 2.90698,2.90698 v 197.67442 c 0,1.61046 -1.29651,2.90697 -2.90698,2.90697 H 193.63011 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90697 V 147.93672 c 0,-1.61047 1.29651,-2.90698 2.90698,-2.90698 z" />
<g
id="text10"
style="font-size:46.5116px;line-height:1.25;letter-spacing:0px;word-spacing:0px;stroke-width:0.726744"
aria-label="[X]..&#10;[X]..&#10;[ ]..&#10;">
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,196.0705 h -11.1964 v -42.19656 h 11.1964 v 3.1795 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.31141 h -5.10992 l -7.22201,-11.99127 -7.24473,11.99127 h -5.04178 l 9.87917,-15.17077 -9.0843,-14.51217 h 4.79197 l 6.79051,11.17369 6.83593,-11.17369 h 4.65571 l -9.10701,14.33048 z m 18.28215,9.31141 h -11.1964 v -3.17951 h 7.38099 v -35.83755 h -7.38099 v -3.1795 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path16" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,256.17276 h -11.1964 V 213.9762 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.3114 h -5.10992 l -7.22201,-11.99128 -7.24473,11.99128 h -5.04178 l 9.87917,-15.17078 -9.0843,-14.51216 h 4.79197 l 6.79051,11.17368 6.83593,-11.17368 h 4.65571 l -9.10701,14.33047 z m 18.28215,9.3114 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52234 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52234 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path17" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;display:inline;fill:#00e219"
d="m 210.24567,316.27502 h -11.1964 v -42.19656 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 49.85008,0 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46528 q 0.77216,0 1.45348,0.29523 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29523 1.4762,-0.29523 z m 25.57229,0 q 0.77217,0 1.45349,0.29523 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29523 1.4762,-0.29523 z"
id="path18" />
</g>
</g>
<g
id="layer2"
style="display:none;opacity:0.355">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="rect15"
width="512"
height="512"
x="0"
y="0" />
<circle
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="path15"
cx="256"
cy="256"
r="256" />
</g>
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>synq</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

86
logo/icon-fg-512.svg Normal file
View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title4">synq</title>
<defs
id="defs1" />
<g
id="layer1">
<rect
style="display:none;fill:#22569d;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect5"
width="512"
height="512"
x="0"
y="0.95533592"
rx="96"
ry="96" />
<path
id="rect6"
style="fill:#ffffff;stroke:#000000;stroke-width:2.90698"
d="m 189.13953,130.52304 h 133.72094 c 7.24709,0 13.08139,5.8343 13.08139,13.0814 v 220.93022 c 0,7.2471 -5.8343,13.0814 -13.08139,13.0814 H 189.13953 c -7.24709,0 -13.08139,-5.8343 -13.08139,-13.0814 V 143.60444 c 0,-7.2471 5.8343,-13.0814 13.08139,-13.0814 z" />
<path
id="rect8"
style="display:inline;stroke:#000000;stroke-width:2.90698"
d="m 222.26527,361.685 h 66.86046 c 1.61047,0 2.90698,1.29651 2.90698,2.90697 0,1.61047 -1.29651,2.90698 -2.90698,2.90698 h -66.86046 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90698 0,-1.61046 1.29651,-2.90697 2.90698,-2.90697 z" />
<path
id="rect9"
style="display:inline;fill:#22569d;stroke:#000000;stroke-width:2.90698"
d="M 193.63011,145.02974 H 318.6301 c 1.61047,0 2.90698,1.29651 2.90698,2.90698 v 197.67442 c 0,1.61046 -1.29651,2.90697 -2.90698,2.90697 H 193.63011 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90697 V 147.93672 c 0,-1.61047 1.29651,-2.90698 2.90698,-2.90698 z" />
<g
id="text10"
style="font-size:46.5116px;line-height:1.25;letter-spacing:0px;word-spacing:0px;stroke-width:0.726744"
aria-label="[X]..&#10;[X]..&#10;[ ]..&#10;">
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,196.0705 h -11.1964 v -42.19656 h 11.1964 v 3.1795 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.31141 h -5.10992 l -7.22201,-11.99127 -7.24473,11.99127 h -5.04178 l 9.87917,-15.17077 -9.0843,-14.51217 h 4.79197 l 6.79051,11.17369 6.83593,-11.17369 h 4.65571 l -9.10701,14.33048 z m 18.28215,9.31141 h -11.1964 v -3.17951 h 7.38099 v -35.83755 h -7.38099 v -3.1795 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path16" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,256.17276 h -11.1964 V 213.9762 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.3114 h -5.10992 l -7.22201,-11.99128 -7.24473,11.99128 h -5.04178 l 9.87917,-15.17078 -9.0843,-14.51216 h 4.79197 l 6.79051,11.17368 6.83593,-11.17368 h 4.65571 l -9.10701,14.33047 z m 18.28215,9.3114 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52234 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52234 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path17" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;display:inline;fill:#00e219"
d="m 210.24567,316.27502 h -11.1964 v -42.19656 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 49.85008,0 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46528 q 0.77216,0 1.45348,0.29523 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29523 1.4762,-0.29523 z m 25.57229,0 q 0.77217,0 1.45349,0.29523 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29523 1.4762,-0.29523 z"
id="path18" />
</g>
</g>
<g
id="layer2"
style="display:none;opacity:0.355">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="rect15"
width="512"
height="512"
x="0"
y="0" />
<circle
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="path15"
cx="256"
cy="256"
r="256" />
</g>
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>synq</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

155
logo/icon.svg Normal file
View File

@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg1"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
sodipodi:docname="icon.svg"
inkscape:export-filename="icon-fg-512.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<title
id="title4">synq</title>
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="1.4803312"
inkscape:cx="260.07693"
inkscape:cy="252.98392"
inkscape:window-width="1620"
inkscape:window-height="1068"
inkscape:window-x="1935"
inkscape:window-y="192"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="4.2333333"
spacingy="4.2333333"
empcolor="#3f3fff"
empopacity="0.25098039"
color="#3f3fff"
opacity="0.1254902"
empspacing="5"
enabled="true"
visible="true"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs1">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect5"
is_visible="true"
lpeversion="1"
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
radius="0"
unit="px"
method="auto"
mode="F"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<g
inkscape:label="icon"
inkscape:groupmode="layer"
id="layer1">
<rect
style="display:none;fill:#22569d;fill-opacity:1;stroke-width:1.00157;stroke-dasharray:none"
id="rect5"
width="512"
height="512"
x="0"
y="0.95533592"
rx="96"
ry="96"
inkscape:label="bg" />
<path
id="rect6"
style="fill:#ffffff;stroke:#000000;stroke-width:2.90698"
inkscape:label="phone"
d="m 189.13953,130.52304 h 133.72094 c 7.24709,0 13.08139,5.8343 13.08139,13.0814 v 220.93022 c 0,7.2471 -5.8343,13.0814 -13.08139,13.0814 H 189.13953 c -7.24709,0 -13.08139,-5.8343 -13.08139,-13.0814 V 143.60444 c 0,-7.2471 5.8343,-13.0814 13.08139,-13.0814 z" />
<path
id="rect8"
style="display:inline;stroke:#000000;stroke-width:2.90698"
inkscape:label="phone-navbar"
d="m 222.26527,361.685 h 66.86046 c 1.61047,0 2.90698,1.29651 2.90698,2.90697 0,1.61047 -1.29651,2.90698 -2.90698,2.90698 h -66.86046 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90698 0,-1.61046 1.29651,-2.90697 2.90698,-2.90697 z" />
<path
id="rect9"
style="display:inline;fill:#22569d;stroke:#000000;stroke-width:2.90698"
inkscape:label="phone-screen"
d="M 193.63011,145.02974 H 318.6301 c 1.61047,0 2.90698,1.29651 2.90698,2.90698 v 197.67442 c 0,1.61046 -1.29651,2.90697 -2.90698,2.90697 H 193.63011 c -1.61047,0 -2.90698,-1.29651 -2.90698,-2.90697 V 147.93672 c 0,-1.61047 1.29651,-2.90698 2.90698,-2.90698 z" />
<g
id="text10"
style="font-size:46.5116px;line-height:1.25;letter-spacing:0px;word-spacing:0px;stroke-width:0.726744"
aria-label="[X]..&#10;[X]..&#10;[ ]..&#10;">
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,196.0705 h -11.1964 v -42.19656 h 11.1964 v 3.1795 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.31141 h -5.10992 l -7.22201,-11.99127 -7.24473,11.99127 h -5.04178 l 9.87917,-15.17077 -9.0843,-14.51217 h 4.79197 l 6.79051,11.17369 6.83593,-11.17369 h 4.65571 l -9.10701,14.33048 z m 18.28215,9.31141 h -11.1964 v -3.17951 h 7.38099 v -35.83755 h -7.38099 v -3.1795 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52234 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70404 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path16" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;fill:#00e219"
d="m 210.24567,256.17276 h -11.1964 V 213.9762 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 31.56793,-9.3114 h -5.10992 l -7.22201,-11.99128 -7.24473,11.99128 h -5.04178 l 9.87917,-15.17078 -9.0843,-14.51216 h 4.79197 l 6.79051,11.17368 6.83593,-11.17368 h 4.65571 l -9.10701,14.33047 z m 18.28215,9.3114 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46529 q 0.77216,0 1.45348,0.29524 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49963,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49964,-0.52234 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29524 1.4762,-0.29524 z m 25.57229,0 q 0.77217,0 1.45349,0.29524 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77216 -0.29524,1.45348 -0.29524,0.68133 -0.81759,1.20367 -0.49964,0.49964 -1.20367,0.79488 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79488 -0.49963,-0.52234 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45348 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29524 1.4762,-0.29524 z"
id="path17" />
<path
style="font-family:Consolas;-inkscape-font-specification:Consolas;display:inline;fill:#00e219"
d="m 210.24567,316.27502 h -11.1964 v -42.19656 h 11.1964 v 3.17951 h -7.42642 v 35.83755 h 7.42642 z m 49.85008,0 h -11.1964 v -3.1795 h 7.38099 v -35.83755 h -7.38099 v -3.17951 h 11.1964 z m 20.43967,-16.46528 q 0.77216,0 1.45348,0.29523 0.70404,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49963,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45348,0.29524 -0.79488,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49964,-0.52235 -0.79488,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79488,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68132,-0.29523 1.4762,-0.29523 z m 25.57229,0 q 0.77217,0 1.45349,0.29523 0.70403,0.29524 1.20367,0.81759 0.52235,0.52235 0.81759,1.22638 0.29524,0.68132 0.29524,1.4762 0,0.77217 -0.29524,1.45349 -0.29524,0.68132 -0.81759,1.20367 -0.49964,0.49963 -1.20367,0.79487 -0.68132,0.29524 -1.45349,0.29524 -0.79487,0 -1.4762,-0.29524 -0.68132,-0.29524 -1.20367,-0.79487 -0.49963,-0.52235 -0.79487,-1.20367 -0.29524,-0.68132 -0.29524,-1.45349 0,-0.79488 0.29524,-1.4762 0.29524,-0.70403 0.79487,-1.22638 0.52235,-0.52235 1.20367,-0.81759 0.68133,-0.29523 1.4762,-0.29523 z"
id="path18" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="circle-mask"
style="display:none;opacity:0.355">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="rect15"
width="512"
height="512"
x="0"
y="0" />
<circle
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4;stroke-dasharray:none;stroke-opacity:1"
id="path15"
cx="256"
cy="256"
r="256" />
</g>
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>synq</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -17,7 +17,10 @@ logger = logging.getLogger("synq")
@asynccontextmanager @asynccontextmanager
async def lifespan(_: FastAPI): async def lifespan(_: FastAPI):
_load_token() token = _load_token()
logger.info("=" * 60)
logger.info("synq token: %s", token)
logger.info("=" * 60)
yield yield
@@ -45,7 +48,7 @@ def _load_token() -> str:
logger.info("synq token loaded from %s", token_file) logger.info("synq token loaded from %s", token_file)
return _token_cache return _token_cache
generated = secrets.token_hex(32) generated = _generate_passphrase()
token_file.parent.mkdir(parents=True, exist_ok=True) token_file.parent.mkdir(parents=True, exist_ok=True)
token_file.write_text(generated) token_file.write_text(generated)
logger.info("") logger.info("")
@@ -58,6 +61,72 @@ def _load_token() -> str:
return _token_cache return _token_cache
def _generate_passphrase(words: int = 3) -> str:
wordlist = [
"able","acid","aged","also","area","army","away","baby","back","ball",
"band","bank","base","bath","bear","beat","been","bell","best","bill",
"bird","blow","blue","bold","bolt","bone","book","born","both","bowl",
"bulk","burn","calm","came","card","care","cart","case","cash","cast",
"cave","cell","chat","chip","city","clam","clay","clip","club","coal",
"coat","code","coil","cold","come","cook","cool","cope","cord","core",
"corn","cost","cove","crew","crop","curl","dare","dark","data","date",
"dawn","days","dead","deal","dean","dear","deck","deep","deer","deft",
"deny","desk","dial","diet","disc","dish","disk","dive","dock","dome",
"door","dose","down","draw","drew","drip","drop","drum","dual","dusk",
"dust","duty","each","earn","ease","east","edge","else","even","ever",
"evil","exam","fact","fail","fair","fall","fame","farm","fast","fate",
"feed","feel","feet","fell","felt","file","fill","film","find","fire",
"firm","fish","fist","flag","flat","flew","flip","flow","foam","fold",
"folk","fond","font","food","foot","ford","fork","form","fort","free",
"from","fuel","full","fund","fuse","gain","game","gaze","gear","give",
"glad","glow","glue","goal","gold","golf","gone","good","grab","gray",
"grid","grim","grip","grow","gulf","gust","hack","hail","half","hall",
"halt","hand","hang","hard","harm","harp","hash","haul","have","hawk",
"head","heal","heap","heat","heel","held","helm","help","herb","here",
"hide","high","hill","hint","hold","hole","home","hook","hope","horn",
"host","hour","hull","hunt","hurt","icon","idea","idle","inch","into",
"iris","iron","isle","item","jade","jail","jazz","jest","join","jump",
"just","keen","keep","kelp","kern","keys","kick","kill","kind","king",
"knit","know","lack","lake","lamp","land","lane","lark","lash","last",
"late","lead","leaf","lean","leap","left","lend","lens","life","lift",
"like","lime","line","link","lion","list","live","load","loan","lock",
"loft","long","look","loop","lore","loss","loud","love","luck","lung",
"lure","mail","main","make","male","malt","many","mark","mask","mast",
"math","maze","meal","mean","meet","melt","mesh","mild","milk","mill",
"mind","mine","mint","miss","mist","mode","moon","more","moss","most",
"move","much","mule","must","nail","name","navy","near","neat","need",
"nest","news","next","nice","node","none","noon","norm","note","null",
"numb","oath","obey","odds","once","only","open","oral","over","pace",
"pack","page","paid","pain","pair","palm","park","part","pass","past",
"path","pave","peak","peel","peer","pick","pier","pile","pine","pipe",
"plan","play","plea","plot","plow","plum","plunge","plus","poem","poet",
"pole","poll","pond","pool","port","pose","post","pour","prey","pull",
"pump","pure","push","quit","race","rack","raid","rail","rain","ramp",
"rang","rank","rare","rate","read","real","reed","reel","rely","rent",
"rest","rice","rich","ride","ring","rise","risk","road","roam","roar",
"rock","role","roll","roof","root","rope","rose","rout","rule","rush",
"rust","safe","sage","sail","salt","same","sand","sang","save","scan",
"seal","seam","seed","seek","self","sell","send","sent","shed","ship",
"shop","shot","show","shut","sick","side","sign","silk","sing","sink",
"site","size","skip","slab","slam","slap","slim","slip","slot","slow",
"snap","snow","soak","soar","sock","soil","sold","sole","some","song",
"soon","sort","soul","soup","span","spin","spit","spot","spur","star",
"stay","stem","step","stop","stub","such","suit","sunk","sure","surf",
"swap","swim","tail","take","talk","tall","tank","tape","task","team",
"tell","tend","tent","term","test","text","than","then","thin","tide",
"tile","time","tint","tiny","tips","tire","toad","told","toll","tomb",
"tool","tops","toss","tour","town","trap","tree","trim","trip","trod",
"true","tube","tuck","tune","turf","turn","twin","type","upon","used",
"vary","vast","veil","vein","very","vest","view","vine","void","volt",
"vote","wade","wake","walk","wall","wand","ward","warm","warp","wary",
"wave","ways","weld","well","went","west","what","when","wide","wiki",
"wild","will","wind","wine","wire","wise","wish","with","wolf","wood",
"word","work","worn","wrap","wren","yard","year","yell","your","zero",
"zest","zinc","zone","zoom",
]
return "-".join(secrets.choice(wordlist) for _ in range(words))
def get_store() -> IdempotencyStore: def get_store() -> IdempotencyStore:
global _store global _store

View File

@@ -471,6 +471,88 @@ SynqSettings gained syncIntervalMinutes: Int = 15 field with DataStore key sync_
- tests: manual — change interval to 30, save, verify WorkManager re-queues (check adb shell dumpsys jobscheduler) - tests: manual — change interval to 30, save, verify WorkManager re-queues (check adb shell dumpsys jobscheduler)
- datetime: [2026-05-18 Sun 20:00] - datetime: [2026-05-18 Sun 20:00]
* milestone 6: post-ship cleanup
** DONE fix resource XML comment before <?xml declaration
*** acceptance
- android build succeeds with custom icon assets.
*** notes
Android's AAPT resource compiler requires <?xml to be the first byte of the file. Image Asset tool generated ic_launcher_background.xml, ic_launcher.xml, and ic_launcher_round.xml with an Apache license comment block before <?xml, causing ParseError at row 16. Fixed by stripping the comment from those three files. ic_launcher_foreground.xml was unaffected (no <?xml declaration). Do not use Image Asset tool for tweaks — it re-adds the comment on reimport; edit XML directly.
*** evidence
- commit: a6af2fe
- tests: manual — gradle assembleDebug succeeds
- datetime: [2026-05-19 Mon]
** DONE fix manifest missing icon references
*** acceptance
- app icon appears on device launcher after install.
*** notes
AndroidManifest.xml was missing android:icon and android:roundIcon attributes on the <application> element. Added @mipmap/ic_launcher and @mipmap/ic_launcher_round. Also committed all mipmap-* webp files and drawable/ic_launcher_background.xml (solid #22569d) and ic_launcher_foreground.xml generated by Image Asset tool.
*** evidence
- commit: a6af2fe
- tests: manual — install APK on device, verify icon appears in launcher
- datetime: [2026-05-19 Mon]
** DONE fix status bar icons washed out (API 35 edge-to-edge)
*** acceptance
- system clock, battery, wifi icons are visible over the app TopAppBar.
*** notes
Android 15 (API 35) enforces edge-to-edge regardless of enableEdgeToEdge() call — removing it had no effect. Real fix: keep enableEdgeToEdge() in MainActivity and add a SideEffect in Theme.kt that calls WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = true. This tells the system to render dark (visible) status bar icons on the light TopAppBar background.
*** evidence
- commit: 68deb5c
- tests: manual — open app, verify clock/battery/wifi icons are dark and readable
- datetime: [2026-05-19 Mon]
** DONE fix keyboard covering Save button
*** acceptance
- Save and Save & Close buttons are visible above the software keyboard.
*** notes
Added imePadding() to the Column modifier in CaptureScreen. The column shrinks to fit above the keyboard rather than being obscured by it.
*** evidence
- commit: 23df95d
- tests: manual — tap body field, keyboard opens, verify save buttons remain visible
- datetime: [2026-05-19 Mon]
** DONE add sync result snackbar
*** acceptance
- brief snackbar appears after tapping sync: "synced N", "nothing to sync", or "server unreachable".
*** notes
Added MutableSharedFlow<String> snackbar emitter to CaptureViewModel. syncNow() now calls checkHealth() first and emits "server unreachable" on failure. syncPending() return type changed from Unit to Int (count of successfully synced captures). SnackbarHost added to CaptureScreen Scaffold; LaunchedEffect collects the flow and calls snackbarState.showSnackbar().
*** evidence
- commit: 4597f92
- tests: manual — tap sync with server up (see "synced N"), with server down (see "server unreachable"), with empty queue (see "nothing to sync")
- datetime: [2026-05-19 Mon]
** DONE log token on every server startup
*** acceptance
- token is clearly visible in container logs on every start, not just first run.
*** notes
lifespan() in server/app/main.py now logs the token between === lines on every startup. Previously only logged "token loaded from token.txt" quietly; now logs the token value itself so it's easy to find with docker logs synq.
*** evidence
- commit: a6af2fe
- tests: manual — docker restart synq, docker logs synq | grep token
- datetime: [2026-05-19 Mon]
** DONE default device label to Build.MODEL
*** acceptance
- fresh install shows the actual device model name, not "android".
*** notes
SettingsRepository.kt: changed deviceLabel fallback from hardcoded "android" to Build.MODEL. No permissions needed. Existing users who have already saved a label are unaffected — DataStore only uses the fallback when no value is stored.
*** evidence
- commit: 628a28a
- tests: manual — fresh install, open settings, verify device label shows model name
- datetime: [2026-05-19 Mon]
** DONE replace hex token with 3-word passphrase
*** acceptance
- generated token is human-readable and fits on one log line.
- existing token.txt files are unaffected.
*** notes
secrets.token_hex(32) produced 64 chars — too long to read/verify visually. Replaced with _generate_passphrase() which picks 3 words from a 512-word embedded list using secrets.choice, hyphenated (e.g. "coral-drift-lamp"). ~27 bits entropy, sufficient for a LAN-only service. Existing token.txt files load unchanged — generation only runs when no token exists. To rotate: delete /data/token.txt and restart the container.
*** evidence
- commit: 628a28a
- tests: manual — delete token.txt, restart container, docker logs synq shows short readable token
- datetime: [2026-05-19 Mon]
* implementation notes for coding agents * implementation notes for coding agents
- keep the server small and testable. - keep the server small and testable.
- keep org formatting in one pure function. - keep org formatting in one pure function.