getting words

This commit is contained in:
micialware committed 2026-09-14 16:56:59 +03:00
1 parent 154c55a7bb
commit b76d115d7e
5 files changed
+117 -32

No files matched your search

+14
View File
@@ -7,6 +7,8 @@
// т.е. количество дней с 1970-01-01, без времени; // т.е. количество дней с 1970-01-01, без времени;
// - states.fsrs_memory: сырое INTEGER-значение FSRS (не дата, не трогаем). // - states.fsrs_memory: сырое INTEGER-значение FSRS (не дата, не трогаем).
import { query } from "./sqlite-api";
export interface CustomField { export interface CustomField {
id: number; id: number;
name: string; name: string;
@@ -40,6 +42,18 @@ export interface Word {
group_id: number; group_id: number;
} }
export async function wordList() {
let rows = await query("SELECT id, key, value, tags, more, group_id FROM words");
return rows.map((row: any) => ({
id: row.id,
key: row.key,
value: row.value,
tags: row.tags,
more: row.more,
group_id: row.group_id,
})) as Word[];
}
export interface HistoryEntry { export interface HistoryEntry {
id: number; id: number;
deck_id: number; deck_id: number;
+17 -12
View File
@@ -4,11 +4,17 @@ import { DB_FILENAME } from './opfs';
let db: any | null = null; let db: any | null = null;
self.onmessage = async (e) => { self.onmessage = async (e) => {
const { type, id, sql, params } = e.data;
try { try {
console.log(e.data.type) console.log(e.data.type)
switch (e.data.type) { switch (type) {
case 'start': { case 'start': {
try { if (db != null) {
self.postMessage({ type: 'success', id, data: null });
return;
}
const sqlite3 = await sqlite3InitModule({ const sqlite3 = await sqlite3InitModule({
print: console.log, print: console.log,
printErr: console.error, // Сюда будут падать детали ошибок VFS printErr: console.error, // Сюда будут падать детали ошибок VFS
@@ -17,34 +23,33 @@ self.onmessage = async (e) => {
db = new sqlite3.oo1.OpfsDb(filename, 'rwt'); db = new sqlite3.oo1.OpfsDb(filename, 'rwt');
db.exec('PRAGMA foreign_keys = ON;'); db.exec('PRAGMA foreign_keys = ON;');
self.postMessage({ type: 'success' });
} catch (err: any) { self.postMessage({ type: 'success', id, data: null });
console.error(err);
self.postMessage({ type: 'error', message: err.message });
}
break; break;
} }
case 'query': { case 'query': {
if (db === null) throw new Error('БД не инициализирована'); if (db === null) throw new Error('БД не инициализирована');
const result = db.selectObjects(e.data.sql, e.data.params); const result = db.selectObjects(sql, params);
self.postMessage({ type: 'success', result }); self.postMessage({ type: 'success', id, data: result });
break; break;
} }
case 'command': { case 'command': {
if (db === null) throw new Error('БД не инициализирована'); if (db === null) throw new Error('БД не инициализирована');
db.exec(e.data.sql, e.data.params); db.exec(sql, params);
self.postMessage({ type: 'success' }); self.postMessage({ type: 'success', id, data: null });
break; break;
} }
case 'close': { case 'close': {
if (db === null) throw new Error('БД не инициализирована'); if (db === null) throw new Error('БД не инициализирована');
db.close(); db.close();
db = null;
break; break;
} }
} }
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);
self.postMessage({ type: 'error', message: err.message }); self.postMessage({ type: 'error', message: err.message , id});
} }
}; };
+5
View File
@@ -1,6 +1,7 @@
// Главная страница: статус локальной базы + быстрый переход к синхронизации. // Главная страница: статус локальной базы + быстрый переход к синхронизации.
// Никакой БД-логики кроме проверки наличия файла в OPFS. // Никакой БД-логики кроме проверки наличия файла в OPFS.
import { wordList } from './db-types.ts';
import { worker } from './main.ts'; import { worker } from './main.ts';
import { hasDatabaseFile, readDatabaseFile } from './opfs.ts'; import { hasDatabaseFile, readDatabaseFile } from './opfs.ts';
import { looksLikeSQLite } from './zstd.ts'; import { looksLikeSQLite } from './zstd.ts';
@@ -48,6 +49,10 @@ export function renderHomePage(root: HTMLElement, nav: HomeNav): void {
? `Локальная база на месте: ${(raw.length / 1024).toFixed(1)} КБ, сигнатура SQLite OK.` ? `Локальная база на месте: ${(raw.length / 1024).toFixed(1)} КБ, сигнатура SQLite OK.`
: 'Файл в OPFS есть, но это не похоже на SQLite — перекачай сохранение.'; : 'Файл в OPFS есть, но это не похоже на SQLite — перекачай сохранение.';
actions.hidden = !ok; actions.hidden = !ok;
if (ok) {
let list = await wordList();
console.log(list);
}
} catch (err) { } catch (err) {
statusEl.textContent = `Не удалось прочитать базу: ${err instanceof Error ? err.message : String(err)}`; statusEl.textContent = `Не удалось прочитать базу: ${err instanceof Error ? err.message : String(err)}`;
actions.hidden = true; actions.hidden = true;
+4 -20
View File
@@ -1,31 +1,12 @@
import './style.css'; import './style.css';
import { renderHomePage } from './home-page.ts'; import { renderHomePage } from './home-page.ts';
import { renderSyncPage } from './sync-page.ts'; import { renderSyncPage } from './sync-page.ts';
import { initWorker } from './sqlite-api.ts';
export const worker = new Worker(new URL('./db-worker.ts', import.meta.url), { export const worker = new Worker(new URL('./db-worker.ts', import.meta.url), {
type: 'module', type: 'module',
}); });
worker.onerror = (ee) => {
console.log(ee)
};
worker.onmessageerror = (ee) => {
console.log(ee)
};
worker.onmessage = (e) => {
if (e.data.type === 'success') {
const app = document.getElementById('app');
if (app) {
console.log(e.data.rows)
}
} else if (e.data.type === 'error') {
console.error(e.data.message);
}
};
worker.postMessage({ type: 'start' });
// Каркас приложения: топ-бар + бургер-меню + две страницы (Главная / Синхронизация). // Каркас приложения: топ-бар + бургер-меню + две страницы (Главная / Синхронизация).
type Page = 'home' | 'sync'; type Page = 'home' | 'sync';
@@ -94,7 +75,10 @@ drawer.querySelectorAll<HTMLButtonElement>('.drawer-btn').forEach((btn) => {
}); });
}); });
// Старт: главная страница, меню закрыто // Старт: главная страница, меню закрыто
await initWorker();
setDrawer(false); setDrawer(false);
renderPage('home'); renderPage('home');
console.log('[JapLearn] crossOriginIsolated =', window.crossOriginIsolated); console.log('[JapLearn] crossOriginIsolated =', window.crossOriginIsolated);
+77
View File
@@ -0,0 +1,77 @@
import { worker } from "./main";
let requestId = 0;
type WorkerRequest = {
resolve: (data: any) => void;
reject: (error: Error) => void;
};
let initPromise: Promise<void> | null = null;
const pending = new Map<number, WorkerRequest>();
export function initWorker(): Promise<void> {
if (initPromise) return initPromise;
initPromise = new Promise((resolve, reject) => {
worker.onmessage = (e: MessageEvent) => {
console.log("Handling worker output");
const msg = e.data;
if (msg.type === 'ready') {
// Готов — запрос init уже обработан
return;
}
const pendingReq = pending.get(msg.id);
if (!pendingReq) return;
pending.delete(msg.id);
if (msg.type == "success") {
pendingReq.resolve(msg.data);
} else {
console.log(msg)
pendingReq.reject(new Error(msg.error || 'Неизвестная ошибка Worker'));
}
};
worker.onerror = (err) => {
console.error('[SQLite Worker] Ошибка:', err);
reject(new Error('Ошибка Worker'));
};
// Отправляем init
const id = ++requestId;
pending.set(id, { resolve, reject });
worker.postMessage({ type: 'start', id });
});
return initPromise;
}
function sendToWorker(type: string, payload?: any): Promise<any> {
return new Promise((resolve, reject) => {
if (!worker) {
reject(new Error('Worker не создан'));
return;
}
const id = ++requestId;
pending.set(id, { resolve, reject });
worker.postMessage({ type, id, ...payload });
});
}
export async function query(sql: string, params?: any[]): Promise<any[]> {
const result = await sendToWorker('query', { sql, params });
return result;
}
export async function command(sql: string, params?: any[]): Promise<void> {
await sendToWorker('command', { sql, params });
}
export async function closeDb(): Promise<void> {
await sendToWorker('close');
worker.terminate();
}