remove build

This commit is contained in:
micialware committed 2026-07-03 13:07:18 +03:00
1 parent e00e524b7a
commit ef32e0ba47
15 files changed
-282

No files matched your search

-44
View File
@@ -1,44 +0,0 @@
(function(){let e=9223372036854775807n,t=-9223372036854775808n;var n=class extends Error{constructor(e,t){super(e),this.code=t}};function r(r){let a={},o=r._getSqliteFree(),s=r._malloc(8),c=[s,s+4];function l(e){if(typeof e!=`string`)return 0;let t=r.lengthBytesUTF8(e),n=r._sqlite3_malloc(t+1);return r.stringToUTF8(e,n,t+1),n}function u(e,t){return BigInt(t)<<32n|BigInt(e)&4294967295n}let d=(function(){let e=BigInt(2**53-1)>>32n,t=BigInt(-(2**53-1))>>32n;return function(n,r){return r>e||r<t?u(n,r):r*4294967296+(n&2147483647)-(n&2147483648)}})(),f=new Set;function p(e){if(!f.has(e))throw new n(`not a database`,21)}let m=new Map;function h(e){if(!m.has(e))throw new n(`not a statement`,21)}a.bind_collection=function(e,t){h(e);let n=Array.isArray(t),r=a.bind_parameter_count(e);for(let i=1;i<=r;++i){let r=t[n?i-1:a.bind_parameter_name(e,i)];r!==void 0&&a.bind(e,i,r)}return 0},a.bind=function(e,t,n){switch(h(e),typeof n){case`number`:return n===(n|0)?a.bind_int(e,t,n):a.bind_double(e,t,n);case`string`:return a.bind_text(e,t,n);default:return n instanceof Uint8Array||Array.isArray(n)?a.bind_blob(e,t,n):n===null?a.bind_null(e,t):typeof n==`bigint`?a.bind_int64(e,t,n):n===void 0?27:(console.warn(`unknown binding converted to null`,n),a.bind_null(e,t))}},a.bind_blob=(function(){let e=`sqlite3_bind_blob`,t=r.cwrap(e,...i(`nnnnn:n`));return function(n,i,a){h(n);let s=a.byteLength??a.length,c=r._sqlite3_malloc(s);return r.HEAPU8.subarray(c).set(a),v(e,t(n,i,c,s,o),m.get(n))}})(),a.bind_parameter_count=(function(){let e=r.cwrap(`sqlite3_bind_parameter_count`,...i(`n:n`));return function(t){return h(t),e(t)}})(),a.bind_double=(function(){let e=`sqlite3_bind_double`,t=r.cwrap(e,...i(`nnn:n`));return function(n,r,i){return h(n),v(e,t(n,r,i),m.get(n))}})(),a.bind_int=(function(){let e=`sqlite3_bind_int`,t=r.cwrap(e,...i(`nnn:n`));return function(n,r,i){return h(n),i>2147483647||i<-2147483648?25:v(e,t(n,r,i),m.get(n))}})(),a.bind_int64=(function(){let n=`sqlite3_bind_int64`,a=r.cwrap(n,...i(`nnnn:n`));return function(r,i,o){if(h(r),o>e||o<t)return 25;let s=o&4294967295n,c=o>>32n;return v(n,a(r,i,Number(s),Number(c)),m.get(r))}})(),a.bind_null=(function(){let e=`sqlite3_bind_null`,t=r.cwrap(e,...i(`nn:n`));return function(n,r){return h(n),v(e,t(n,r),m.get(n))}})(),a.bind_parameter_name=(function(){let e=r.cwrap(`sqlite3_bind_parameter_name`,...i(`n:s`));return function(t,n){return h(t),e(t,n)}})(),a.bind_text=(function(){let e=`sqlite3_bind_text`,t=r.cwrap(e,...i(`nnnnn:n`));return function(n,r,i){return h(n),v(e,t(n,r,l(i),-1,o),m.get(n))}})(),a.changes=(function(){let e=r.cwrap(`sqlite3_changes`,...i(`n:n`));return function(t){return p(t),e(t)}})(),a.close=(function(){let e=`sqlite3_close`,t=r.cwrap(e,...i(`n:n`),{async:!0});return async function(n){p(n);let r=await t(n);return f.delete(n),v(e,r,n)}})(),a.column=function(e,t){h(e);let i=a.column_type(e,t);switch(i){case 4:return a.column_blob(e,t);case 2:return a.column_double(e,t);case 1:return d(a.column_int(e,t),r.getTempRet0());case 5:return null;case 3:return a.column_text(e,t);default:throw new n(`unknown type`,i)}},a.column_blob=(function(){let e=r.cwrap(`sqlite3_column_blob`,...i(`nn:n`));return function(t,n){h(t);let i=a.column_bytes(t,n),o=e(t,n);return r.HEAPU8.subarray(o,o+i)}})(),a.column_bytes=(function(){let e=r.cwrap(`sqlite3_column_bytes`,...i(`nn:n`));return function(t,n){return h(t),e(t,n)}})(),a.column_count=(function(){let e=r.cwrap(`sqlite3_column_count`,...i(`n:n`));return function(t){return h(t),e(t)}})(),a.column_double=(function(){let e=r.cwrap(`sqlite3_column_double`,...i(`nn:n`));return function(t,n){return h(t),e(t,n)}})(),a.column_int=(function(){let e=r.cwrap(`sqlite3_column_int64`,...i(`nn:n`));return function(t,n){return h(t),e(t,n)}})(),a.column_int64=(function(){let e=r.cwrap(`sqlite3_column_int64`,...i(`nn:n`));return function(t,n){return h(t),u(e(t,n),r.getTempRet0())}})(),a.column_name=(function(){let e=r.cwrap(`sqlite3_column_name`,...i(`nn:s`));return function(t,n){return h(t),e(t,n)}})(),a.column_names=function(e){let t=[],n=a.column_count(e);for(let r=0;r<n;++r)t.push(a.column_name(e,r));return t},a.column_text=(function(){let e=r.cwrap(`sqlite3_column_text`,...i(`nn:s`));return function(t,n){return h(t),e(t,n)}})(),a.column_type=(function(){let e=r.cwrap(`sqlite3_column_type`,...i(`nn:n`));return function(t,n){return h(t),e(t,n)}})(),a.create_function=function(e,t,i,a,o,s,c,l){if(p(e),s&&!c&&!l)return v(`sqlite3_create_function`,r.createFunction(e,t,i,a,o,s),e);if(!s&&c&&l)return v(`sqlite3_create_function`,r.createAggregate(e,t,i,a,o,c,l),e);throw new n(`invalid function combination`,21)},a.create_module=function(e,t,n,i){return p(e),v(`sqlite3_create_module`,r.createModule(e,t,n,i),e)},a.data_count=(function(){let e=r.cwrap(`sqlite3_data_count`,...i(`n:n`));return function(t){return h(t),e(t)}})(),a.declare_vtab=(function(){let e=r.cwrap(`sqlite3_declare_vtab`,...i(`ns:n`));return function(t,n){return v(`sqlite3_declare_vtab`,e(tLine truncated
`)):typeof readline==`function`&&(e=readline(),e!==null&&(e+=`
`)),!e){var t=null;break a}t=Array(N(e)+1),e=P(e,t,0,t.length),t.length=e,Ee=t}t=Ee.shift()}return t},tc(e,t){t===null||t===10?(f(j(e.Tb,0)),e.Tb=[]):t!=0&&e.Tb.push(t)},kc(e){e.Tb&&0<e.Tb.length&&(f(j(e.Tb,0)),e.Tb=[])},$c(){return{Wc:25856,Yc:5,Vc:191,Xc:35387,Uc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ad(){return 0},bd(){return[24,80]}},je={tc(e,t){t===null||t===10?(p(j(e.Tb,0)),e.Tb=[]):t!=0&&e.Tb.push(t)},kc(e){e.Tb&&0<e.Tb.length&&(p(j(e.Tb,0)),e.Tb=[])}};function Me(e,t){var n=e.Pb?e.Pb.length:0;n>=t||(t=Math.max(t,n*(1048576>n?2:1.125)>>>0),n!=0&&(t=Math.max(t,256)),n=e.Pb,e.Pb=new Uint8Array(t),0<e.Rb&&e.Pb.set(n.subarray(0,e.Rb),0))}var F={Xb:null,Wb(){return F.createNode(null,`/`,16895,0)},createNode(e,t,n,r){if((n&61440)==24576||(n&61440)==4096)throw new R(63);return F.Xb||={dir:{node:{Vb:F.Eb.Vb,Sb:F.Eb.Sb,ec:F.Eb.ec,lc:F.Eb.lc,Dc:F.Eb.Dc,qc:F.Eb.qc,oc:F.Eb.oc,Cc:F.Eb.Cc,pc:F.Eb.pc},stream:{ac:F.Ob.ac}},file:{node:{Vb:F.Eb.Vb,Sb:F.Eb.Sb},stream:{ac:F.Ob.ac,read:F.Ob.read,write:F.Ob.write,wc:F.Ob.wc,mc:F.Ob.mc,nc:F.Ob.nc}},link:{node:{Vb:F.Eb.Vb,Sb:F.Eb.Sb,ic:F.Eb.ic},stream:{}},xc:{node:{Vb:F.Eb.Vb,Sb:F.Eb.Sb},stream:Je}},n=He(e,t,n,r),V(n.mode)?(n.Eb=F.Xb.dir.node,n.Ob=F.Xb.dir.stream,n.Pb={}):(n.mode&61440)==32768?(n.Eb=F.Xb.file.node,n.Ob=F.Xb.file.stream,n.Rb=0,n.Pb=null):(n.mode&61440)==40960?(n.Eb=F.Xb.link.node,n.Ob=F.Xb.link.stream):(n.mode&61440)==8192&&(n.Eb=F.Xb.xc.node,n.Ob=F.Xb.xc.stream),n.timestamp=Date.now(),e&&(e.Pb[t]=n,e.timestamp=n.timestamp),n},Zc(e){return e.Pb?e.Pb.subarray?e.Pb.subarray(0,e.Rb):new Uint8Array(e.Pb):new Uint8Array},Eb:{Vb(e){var t={};return t.Jc=(e.mode&61440)==8192?e.id:1,t.Ac=e.id,t.mode=e.mode,t.Pc=1,t.uid=0,t.Mc=0,t.hc=e.hc,V(e.mode)?t.size=4096:(e.mode&61440)==32768?t.size=e.Rb:(e.mode&61440)==40960?t.size=e.link.length:t.size=0,t.Fc=new Date(e.timestamp),t.Oc=new Date(e.timestamp),t.Ic=new Date(e.timestamp),t.Gc=4096,t.Hc=Math.ceil(t.size/t.Gc),t},Sb(e,t){if(t.mode!==void 0&&(e.mode=t.mode),t.timestamp!==void 0&&(e.timestamp=t.timestamp),t.size!==void 0&&(t=t.size,e.Rb!=t))if(t==0)e.Pb=null,e.Rb=0;else{var n=e.Pb;e.Pb=new Uint8Array(t),n&&e.Pb.set(n.subarray(0,Math.min(t,e.Rb))),e.Rb=t}},ec(){throw Re[44]},lc(e,t,n,r){return F.createNode(e,t,n,r)},Dc(e,t,n){if(V(e.mode)){try{var r=B(t,n)}catch{}if(r)for(var i in r.Pb)throw new R(55)}delete e.parent.Pb[e.name],e.parent.timestamp=Date.now(),e.name=n,t.Pb[n]=e,t.timestamp=e.parent.timestamp,e.parent=t},qc(e,t){delete e.Pb[t],e.timestamp=Date.now()},oc(e,t){var n=B(e,t),r;for(r in n.Pb)throw new R(55);delete e.Pb[t],e.timestamp=Date.now()},Cc(e){var t=[`.`,`..`],n;for(n in e.Pb)e.Pb.hasOwnProperty(n)&&t.push(n);return t},pc(e,t,n){return e=F.createNode(e,t,41471,0),e.link=n,e},ic(e){if((e.mode&61440)!=40960)throw new R(28);return e.link}},Ob:{read(e,t,n,r,i){var a=e.node.Pb;if(i>=e.node.Rb)return 0;if(e=Math.min(e.node.Rb-i,r),8<e&&a.subarray)t.set(a.subarray(i,i+e),n);else for(r=0;r<e;r++)t[n+r]=a[i+r];return e},write(e,t,n,r,i,a){if(t.buffer===y.buffer&&(a=!1),!r)return 0;if(e=e.node,e.timestamp=Date.now(),t.subarray&&(!e.Pb||e.Pb.subarray)){if(a)return e.Pb=t.subarray(n,n+r),e.Rb=r;if(e.Rb===0&&i===0)return e.Pb=t.slice(n,n+r),e.Rb=r;if(i+r<=e.Rb)return e.Pb.set(t.subarray(n,n+r),i),r}if(Me(e,i+r),e.Pb.subarray&&t.subarray)e.Pb.set(t.subarray(n,n+r),i);else for(a=0;a<r;a++)e.Pb[i+a]=t[n+a];return e.Rb=Math.max(e.Rb,i+r),r},ac(e,t,n){if(n===1?t+=e.position:n===2&&(e.node.mode&61440)==32768&&(t+=e.node.Rb),0>t)throw new R(28);return t},wc(e,t,n){Me(e.node,t+n),e.node.Rb=Math.max(e.node.Rb,t+n)},mc(e,t,n,r,i){if((e.node.mode&61440)!=32768)throw new R(43);if(e=e.node.Pb,i&2||e.buffer!==y.buffer){if((0<n||n+t<e.length)&&(e=e.subarray?e.subarray(n,n+t):Array.prototype.slice.call(e,n,n+t)),n=!0,t=65536*Math.ceil(t/65536),(i=An(65536,t))?(b.fill(0,i,i+t),t=i):t=0,!t)throw new R(48);y.set(e,t)}else n=!1,t=e.byteOffset;return{Qc:t,Ec:n}},nc(e,t,n,r){return F.Ob.write(e,t,0,r,n,!1),0}}},Ne=(e,t)=>{var n=0;return e&&(n|=365),t&&(n|=146),n},Pe=null,Fe={},I=[],Ie=1,L=null,Le=!0,R=null,Re={};function z(e,t={}){if(e=Te(e),!e)return{path:``,node:null};if(t=Object.assign({yc:!0,uc:0},t),8<t.uc)throw new R(32);e=e.split(`/`).filter(e=>!!e);for(var n=Pe,r=`/`,i=0;i<e.length;i++){var a=i===e.length-1;if(a&&t.parent)break;if(n=B(n,e[i]),r=M(r+`/`+e[i]),n.bc&&(!a||a&&t.yc)&&(n=n.bc.root),!a||t.$b){for(a=0;(n.mode&61440)==40960;)if(n=tt(r),r=Te(xe(r),n),n=z(r,{uc:t.uc+1}).node,40<a++)throw new R(32)}}return{path:r,node:n}}function ze(e){for(var t;;){if(e===e.parent)return e=e.Wb.Bc,t?e[e.length-1]===`/`?e+t:`${e}/${t}`:e;t=t?`${e.name}/${t}`:e.name,e=e.parent}}function Be(e,t){for(var n=0,r=0;r<t.length;r++)n=(n<<5)-n+t.charCodeAt(r)|0;return(e+n>>>0)%L.length}function Ve(e){var t=Be(e.parent.id,e.name);if(L[t]===e)L[t]=e.cc;else for(t=L[t];t;){if(t.cc===e){t.cc=e.cc;break}t=t.cc}}function B(e,t){var n;if(n=(n=H(e,`x`))?n:e.Eb.ec?0:2)throw new R(n,e);for(n=L[Be(e.id,t)];n;n=n.cc){var Line truncated
create table if not exists card_set
(
id INTEGER primary key autoincrement,
name TEXT not null,
forward TEXT not null,
backward TEXT not null,
filter TEXT not null
);
create table if not exists settings
(
id text primary key,
value text
);
create table if not exists word_group
(
id INTEGER primary key autoincrement,
name TEXT not null
);
create table if not exists words
(
id INTEGER primary key autoincrement,
key TEXT not null,
value TEXT not null,
tags TEXT not null,
more TEXT,
group_id integer default 1 not null
constraint words_word_group_id_fk references word_group on update cascade on delete cascade
);
create table if not exists card_stats
(
id INTEGER primary key autoincrement,
word_id INTEGER not null references words on delete cascade,
set_id TEXT not null references card_set on delete cascade,
score INTEGER default 1 not null,
last_opened integer not null
);
`.split(`;`).map(e=>e.trim()).filter(e=>e.length>0);for(let t of e)await d.exec(f,t+`;`)}self.onmessage=async e=>{let{type:t,id:n,sql:r,params:i}=e.data;try{switch(t){case`init`:await h(),await g(),self.postMessage({type:`result`,id:n,ok:!0,data:null});break;case`execute`:{if(!d||f===null)throw Error(`БД не инициализирована`);let e=await d.execWithParams(f,r,i);self.postMessage({type:`result`,id:n,ok:!0,data:e.rows});break}case`executeVoid`:if(!d||f===null)throw Error(`БД не инициализирована`);await d.run(f,r,i),self.postMessage({type:`result`,id:n,ok:!0,data:null});break;case`closeDb`:d&&f!==null&&(await d.close(f),f=null),p&&=(await p.close(),null),m=!1,self.postMessage({type:`result`,id:n,ok:!0,data:null});break;case`getFile`:{let e=await(await(await(await navigator.storage.getDirectory()).getFileHandle(`japanese_app.db`)).getFile()).arrayBuffer();self.postMessage({type:`result`,id:n,ok:!0,data:e},{transfer:[e]});break}case`replaceFile`:{d&&f!==null&&(await d.close(f),f=null),p&&=(await p.close(),null),m=!1;let e=await(await(await navigator.storage.getDirectory()).getFileHandle(`japanese_app.db`,{create:!0})).createSyncAccessHandle();try{let t=new Uint8Array(i);e.truncate(0),e.write(t,{at:0}),e.flush()}finally{e.close()}await h(),self.postMessage({type:`result`,id:n,ok:!0,data:null});break}default:throw Error(`Неизвестный тип сообщения: ${t}`)}}catch(e){self.postMessage({type:`result`,id:n,ok:!1,error:e.message})}}})();
-143
View File
@@ -1,143 +0,0 @@
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=null,t=null,n=0,r=new Map;function i(t,i){return new Promise((a,o)=>{if(!e){o(Error(`Worker не создан`));return}let s=++n;r.set(s,{resolve:a,reject:o}),e.postMessage({type:t,id:s,...i})})}function a(){return t||(t=new Promise((t,i)=>{e=new Worker(new URL(`/assets/db-worker-DEWOyJFk.js`,``+import.meta.url),{type:`module`}),e.onmessage=e=>{let t=e.data;if(t.type!==`ready`&&t.type===`result`){let e=r.get(t.id);if(!e)return;r.delete(t.id),t.ok?e.resolve(t.data):(console.log(t),e.reject(Error(t.error||`Неизвестная ошибка Worker`)))}},e.onerror=e=>{console.error(`[SQLite Worker] Ошибка:`,e),i(Error(`Ошибка Worker`))};let a=++n;r.set(a,{resolve:t,reject:i}),e.postMessage({type:`init`,id:a})}),t)}async function o(){await a(),console.log(`[SQLite] База данных инициализирована.`)}async function s(e,t){return await i(`execute`,{sql:e,params:t})}async function c(e,t){await i(`executeVoid`,{sql:e,params:t})}async function l(){return await i(`getFile`)}async function u(){e&&(await i(`closeDb`),e.terminate(),e=null,t=null)}async function ee(e){await te(`replaceFile`,e)}async function te(t,i){return new Promise((a,o)=>{if(!e){o(Error(`Worker не создан`));return}let s=++n;r.set(s,{resolve:a,reject:o}),e.postMessage({type:t,id:s,params:i},[i])})}var d=d===void 0?{}:d,f={},p;for(p in d)d.hasOwnProperty(p)&&(f[p]=d[p]);var m=d.printErr||console.warn.bind(console);for(p in f)f.hasOwnProperty(p)&&(d[p]=f[p]);var h=(e,t)=>{throw t};f=null,d.arguments&&d.arguments,d.thisProgram&&(thisProgram=d.thisProgram),d.quit&&(h=d.quit),typeof WebAssembly!=`object`&&w(`no native wasm support detected`);var g,_=!1,v,ne;function y(){var e=g.buffer;d.HEAP8=new Int8Array(e),d.HEAPU8=ne=new Uint8Array(e)}var b=[],x=[],re=[];function ie(){if(d.preRun)for(typeof d.preRun==`function`&&(d.preRun=[d.preRun]);d.preRun.length;)se(d.preRun.shift());E(b)}function ae(){E(x)}function oe(){if(d.postRun)for(typeof d.postRun==`function`&&(d.postRun=[d.postRun]);d.postRun.length;)le(d.postRun.shift());E(re)}function se(e){b.unshift(e)}function ce(e){x.unshift(e)}function le(e){re.unshift(e)}var S=0,C=null;function ue(e){var t;S++,(t=d.monitorRunDependencies)==null||t.call(d,S)}function de(e){var t;if(S--,(t=d.monitorRunDependencies)==null||t.call(d,S),S==0&&C){var n=C;C=null,n()}}function w(e){var t;throw(t=d.onAbort)==null||t.call(d,e),e=`Aborted(`+e+`)`,m(e),_=!0,e+=`. Build with -sASSERTIONS for more info.`,new WebAssembly.RuntimeError(e)}function fe(){return{a:Te}}function pe(e){return fetch(e,{credentials:`same-origin`}).then(function(t){if(!t.ok)throw`failed to load wasm binary file at '`+e+`'`;return t.arrayBuffer()})}function me(e){var t=fe();function n(e,t){return N=e.exports,g=N.f,y(),ce(N.g),de(`wasm-instantiate`),N}ue(`wasm-instantiate`);function r(e){n(e.instance)}function i(n){return pe(e).then(function(e){return WebAssembly.instantiate(e,t)}).then(n,function(e){m(`failed to asynchronously prepare wasm: `+e),w(e)})}function a(){return e&&e.byteLength>0?WebAssembly.instantiate(e,t).then(r,function(e){m(`wasm compile failed: `+e)}):typeof WebAssembly.instantiateStreaming==`function`&&typeof e==`string`&&typeof fetch==`function`?fetch(e,{credentials:`same-origin`}).then(function(e){return WebAssembly.instantiateStreaming(e,t).then(r,function(e){return m(`wasm streaming compile failed: `+e),m(`falling back to ArrayBuffer instantiation`),i(r)})}):i(r)}if(d.instantiateWasm)try{return d.instantiateWasm(t,n)}catch(e){return m(`Module.instantiateWasm callback failed with error: `+e),!1}return a(),{}}var T=class{constructor(e){this.name=`ExitStatus`,this.message=`Program terminated with exit(${e})`,this.status=e}},E=e=>{for(;e.length>0;)e.shift()(d)},D=d.noExitRuntime||!0,he=()=>w(``),O=0,ge=()=>{D=!1,O=0},k={},A=e=>{if(e instanceof T||e==`unwind`)return v;h(1,e)},j=()=>D||O>0,M=e=>{var t;v=e,j()||((t=d.onExit)==null||t.call(d,e),_=!0),h(e,new T(e))},_e=(e,t)=>{v=e,M(e)},ve=()=>{if(!j())try{_e(v)}catch(e){A(e)}},ye=e=>{if(!_)try{e(),ve()}catch(e){A(e)}},be=()=>performance.now(),xe=(e,t)=>(k[e]&&(clearTimeout(k[e].id),delete k[e]),t&&(k[e]={id:setTimeout(()=>{delete k[e],ye(()=>ze(e,be()))},t),timeout_ms:t}),0),Se=()=>2147483648,Ce=(e,t)=>Math.ceil(e/t)*t,we=e=>{var t=(e-g.buffer.byteLength+65535)/Line truncated
SELECT id, name, forward, backward, filter
FROM card_set
ORDER BY id ASC
`)).map(e=>({id:e[0],name:e[1],forward:e[2],backward:e[3],filter:e[4]}))}async function Qe(){return(await s(`
SELECT id, key, value, tags, more, group_id
FROM words
ORDER BY id ASC
`)).map(e=>({id:e[0],key:e[1],value:e[2],tags:e[3],more:e[4],group_id:e[5]}))}async function $e(e){let t=await s(`SELECT w.id, w.key, w.value, w.tags, w.more, w.group_id
FROM words w
INNER JOIN card_stats cs ON cs.word_id = w.id
WHERE cs.set_id = ?
ORDER BY w.id ASC`,[e]),n=await s(`SELECT id, word_id, set_id, score, last_opened
FROM card_stats
WHERE set_id = ?
ORDER BY word_id ASC`,[e]);return{words:t.map(e=>({id:e[0],key:e[1],value:e[2],tags:e[3],more:e[4],group_id:e[5]})),stats:n.map(e=>({id:e[0],word_id:e[1],set_id:e[2],score:e[3],last_opened:Ze(e[4])}))}}async function R(e,t,n){await c(`UPDATE card_stats SET score = ?, last_opened = ? WHERE id = ?`,[Math.max(1,Math.round(t||1)),n,e])}async function et(){return(await s(`
SELECT id, name FROM word_group ORDER BY id ASC
`)).map(e=>({id:e[0],name:e[1]}))}async function tt(){let e=await et(),t=await Qe(),n=[];for(let r of e){let e=t.filter(e=>e.group_id===r.id),i=new Map;for(let t of e){if(!t.tags||t.tags.trim()===``){let e=`(без тега)`;i.has(e)||i.set(e,[]),i.get(e).push(t);continue}let e=t.tags.split(`,`).map(e=>e.trim()).filter(Boolean);for(let n of e)i.has(n)||i.set(n,[]),i.get(n).push(t)}let a=[];for(let[e,t]of i)a.push({tag:e,words:t});a.sort((e,t)=>t.words.length-e.words.length),n.push({group:r,tags:a})}return n}var nt=20,rt=.33,z=25,it=.95,B={Default:`Default`,TrainWorstFirst:`TrainWorstFirst`,FullRandom:`FullRandom`};function V(e){let t=(Date.now()-e.last_open)/(1e3*60*60*24),n=it**+Math.max(0,t);return e.score*n}function at(e,t){let n=V(e);switch(t){case`easy`:e.score=Math.round(n+5);break;case`ok`:e.score=Math.round(n+2);break;case`hard`:e.score=Math.round(n-1);break;case`none`:e.score=Math.round(n*.5);break}e.score<1?e.score=1:e.score>z&&(e.score=z),e.last_open=Date.now()}var ot=class{weights;constructor(e){this.weights=[...e]}sample(){let e=this.weights.reduce((e,t)=>e+t,0),t=Math.random()*e;for(let e=0;e<this.weights.length;e++)if(t-=this.weights[e],t<=0)return e;return this.weights.length-1}updateWeight(e,t){this.weights[e]=t}},st=class{backet=[];initialized=!1;next(e){return this.backet.length===0&&(this.backet=Array.from({length:e.words.length},(e,t)=>t),this.shuffle(this.backet)),this.backet.pop()}open(e,t,n){}init(e){this.initialized=!0,this.backet=Array.from({length:e.words.length},(e,t)=>t),this.shuffle(this.backet)}shuffle(e){for(let t=e.length-1;t>0;t--){let n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}}},ct=class{history=[];lastWeights;initialized=!1;next(e){let t=this.historyLen(e.set.length);for(let n=0;n<100;n++){let n=this.lastWeights.sample();if(!this.history.includes(n)||t>=e.set.length)return this.history.length>=t&&this.history.shift(),this.history.push(n),n}this.history=[];let n=this.lastWeights.sample();return this.history.push(n),n}open(e,t,n){let r=(100/Math.max(.1,V(n)))**2;this.lastWeights.updateWeight(t,r)}init(e){this.initialized=!0;let t=e.set.map(e=>(100/Math.max(.1,V(e)))**2*2);this.lastWeights=new ot(t)}historyLen(e){return Math.min(nt,Math.max(0,Math.floor(e*rt)))}},lt=class{initialized=!1;next(e){throw Error(`Not implemented`)}open(e,t,n){throw Error(`Not implemented`)}init(e){throw Error(`Not implemented`)}},ut=class{words;set;currentWordIndex=null;orderModule;constructor(e,t,n){this.words=t,this.set=n;let r=e.open_mode;this.orderModule=r===B.FullRandom?{type:`RandomSRS`,module:new st}:r===B.TrainWorstFirst?{type:`WorstWordsSRS`,module:new lt}:{type:`SemiRandomSRS`,module:new ct}}getOrderContext(){return{words:this.words,set:this.set}}next(){let e=this.getOrderContext(),t,n=this.orderModule;switch(n.type){case`SemiRandomSRS`:n.module.initialized||n.module.init(e),t=n.module.next(e);break;case`RandomSRS`:n.module.initialized||n.module.init(e),t=n.module.next(e);break;case`WorstWordsSRS`:n.module.initialized||n.module.init(e),t=n.module.next(e);break}return this.currentWordIndex=t,{word:this.words[t],stat:this.set[t]}}open(e){if(this.currentWordIndex===null)return;let t=this.currentWordIndex,n=this.set[t];at(n,e);let r=this.orderModule;switch(r.type){case`SemiRandomSRS`:r.module.open(e,t,n);break;case`RandomSRS`:r.module.open(e,t,n);break;case`WorstWordsSRS`:r.module.open(e,t,n);break}}get length(){return this.set.length}};function H(e,t){let n={key:e.key,value:e.value,tags:e.tags};return t.split(/\s+/).filter(Boolean).map(t=>t in n&&n[t]?n[t]:t in e.more&&e.more[t]?e.more[t]:``).filter(Boolean)}var dt=class{container;cardSet;words;callbacks;totalCards;forwardFields;backwardFields;seenWords=new Set;shownCount=0;constructor(e,t,n,r,i){this.container=e,this.callbacks=i,this.totalCards=r.length,this.forwardFields=t.forward||`key`,this.backwardFields=t.backward||`value`;let a=n.map(e=>({id:e.id,key:e.key,value:e.value,tags:e.tags,additional:e.more,group_id:e.group_id}));this.words=n,this.cardSet=new ut(t,a,r),this.render()}render(){let e=this.totalCards;this.container.innerHTML=`
<div class="training-header">
<button id="btnTrainingBack" class="btn" style="flex:none; padding:0.4rem 0.8rem;">← Назад</button>
<span style="font-size:0.85rem;color:var(--text);">уникальных: 0 / ${e}</span>
</div>
<div id="training-card" class="training-card">
<div class="training-content" id="training-content">
</div>
</div>
<div id="training-actions" class="training-actions">
</div>
`,this.renderCard(),document.getElementById(`btnTrainingBack`)?.addEventListener(`click`,()=>{this.callbacks.onFinish()})}updateCounter(){let e=this.totalCards,t=this.container.querySelector(`.training-header span`);t&&(t.textContent=`уникальных: ${this.seenWords.size} / ${e}`)}renderCard(){let e=this.cardSet.next(),t=this.words.find(t=>t.id===e.word.id);if(!t)return;this.shownCount++,this.seenWords.has(e.word.id)||this.seenWords.add(e.word.id),this.updateCounter();let n=this.cardSet.set[this.cardSet.currentWordIndex];if(n){let e=Math.floor(Date.now()/1e3);n.last_open=Date.now(),R(n.id,n.score,e).catch(e=>console.error(`[Training] Ошибка сохранения last_opened:`,e))}this.renderSide(t,this.forwardFields,this.backwardFields,!1,n),this.renderActions(!1)}renderSide(e,t,n,r,i){let a=document.getElementById(`training-content`);if(!a)return;let o=``;if(i&&(o=`⚡ ${V(i).toFixed(1)}`),r){let r=H(e,t),i=H(e,n);a.innerHTML=`
<div class="training-side">
${r.length>0?r.map(e=>`<div class="training-field">${this.escapeHtml(e)}</div>`).join(``):`<div class="training-field" style="opacity:0.4;">—</div>`}
</div>
<div class="training-divider"></div>
<div class="training-side training-side-back">
${i.length>0?i.map(e=>`<div class="training-field">${this.escapeHtml(e)}</div>`).join(``):`<div class="training-field" style="opacity:0.4;">—</div>`}
</div>
${o?`<div class="training-score">${o}</div>`:``}
`}else{let n=H(e,t);if(n.length===0){a.innerHTML=`
<div class="training-field" style="opacity:0.4;">Нет данных для отображения</div>
${o?`<div class="training-score">${o}</div>`:``}
`;return}a.innerHTML=`
${n.map(e=>`<div class="training-field">${this.escapeHtml(e)}</div>`).join(``)}
${o?`<div class="training-score">${o}</div>`:``}
`}}renderActions(e){let t=document.getElementById(`training-actions`);t&&(e?(t.innerHTML=`
<div class="training-vote">
<button class="btn vote-btn vote-easy" data-mode="easy">🟢 Легко</button>
<button class="btn vote-btn vote-ok" data-mode="ok">🟡 Нормально</button>
<button class="btn vote-btn vote-hard" data-mode="hard">🟠 Тяжело</button>
<button class="btn vote-btn vote-none" data-mode="none">🔴 Не знаю</button>
</div>
`,t.querySelectorAll(`.vote-btn`).forEach(e=>{e.addEventListener(`click`,()=>{let t=e.dataset.mode;this.vote(t)})})):(t.innerHTML=`
<button id="btnReveal" class="btn btn-primary" style="flex:none; width:100%;">Показать перевод</button>
`,document.getElementById(`btnReveal`)?.addEventListener(`click`,()=>this.reveal())))}reveal(){let e=this.cardSet.currentWordIndex;if(e===null)return;let t=this.words[e];if(!t)return;let n=this.cardSet.set[e];this.renderSide(t,this.forwardFields,this.backwardFields,!0,n),this.renderActions(!0)}async vote(e){this.cardSet.open(e);let t=this.cardSet.currentWordIndex;if(t===null)return;let n=this.cardSet.set[t];if(n)try{await R(n.id,n.score,Math.floor(n.last_open/1e3))}catch(e){console.error(`[Training] Ошибка сохранения:`,e)}this.renderCard()}escapeHtml(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}};function ft(e){if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function pt(e,t,n){let r=ft(t.more);e.innerHTML=`
<div class="word-view-header">
<button id="btnViewBack" class="btn" style="flex:none; padding:0.4rem 0.8rem;">← Назад</button>
</div>
<div class="word-view-card">
<div class="word-view-section">
<div class="word-view-label">Японское слово</div>
<div class="word-view-field">${U(t.key)}</div>
</div>
<div class="word-view-divider"></div>
<div class="word-view-section">
<div class="word-view-label">Перевод</div>
<div class="word-view-field">${U(t.value)}</div>
</div>
<div class="word-view-divider"></div>
<div class="word-view-section">
<div class="word-view-label">Теги</div>
<div class="word-view-field" style="font-size:0.9rem;color:var(--accent);">
${t.tags?U(t.tags):`—`}
</div>
</div>
${Object.keys(r).length>0?`
<div class="word-view-divider"></div>
<div class="word-view-section">
<div class="word-view-label">Дополнительные данные</div>
${Object.entries(r).map(([e,t])=>`
<div class="word-view-more-row">
<span class="word-view-more-key">${U(e)}</span>
<span class="word-view-more-value">${U(t)}</span>
</div>
`).join(``)}
</div>
`:``}
</div>
`,document.getElementById(`btnViewBack`)?.addEventListener(`click`,()=>{n.onBack()})}function U(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}var W=`https://learning.micialware.ru`,G=`japlearn_sync_id`,K=document.getElementById(`page-container`),q=document.querySelectorAll(`.nav-btn`),J=`words`;function Y(e){let t=document.createElement(`div`);return t.textContent=e,t.innerHTML}function X(e,t=!1){let n=document.getElementById(`status`);n&&(n.textContent=e,n.className=`status-bar`,t?n.classList.add(`error`):n.classList.add(`success`)),console.log(`[App] ${e}`)}function mt(){let e=document.getElementById(`syncId`)?.value.trim();if(!e)throw Error(`Введите ID синхронизации`);return localStorage.setItem(G,e),e}function ht(){let e=localStorage.getItem(G);if(e){let t=document.getElementById(`syncId`);t&&(t.value=e)}}function gt(){K.innerHTML=`
<div class="card">
<h2 class="card-title">📡 Синхронизация</h2>
<div class="sync-input-group">
<input type="text" id="syncId" placeholder="Введите ID синхронизации" autocomplete="off" spellcheck="false" />
<div class="sync-buttons">
<button id="btnDownload" class="btn">📥 Скачать</button>
<button id="btnUpload" class="btn btn-primary">📤 Загрузить</button>
</div>
</div>
</div>
<div class="card" style="flex: 1;">
<h2 class="card-title">📚 Слова</h2>
<div id="word-groups"></div>
</div>
<div id="status" class="status-bar">Загрузка...</div>
`}function _t(e){let t=document.getElementById(`word-groups`);if(!t)return;if(t.innerHTML=``,e.length===0){t.innerHTML=`<p style="text-align:center;opacity:0.6;padding:1rem;">Нет слов 🎉</p>`;return}let n=document.createElement(`div`);n.className=`group-tabs`;let r=document.createElement(`div`);r.className=`group-panels`;for(let t=0;t<e.length;t++){let i=e[t],a=document.createElement(`button`);a.className=`group-tab`+(t===0?` active`:``);let o=i.tags.reduce((e,t)=>e+t.words.length,0);a.innerHTML=`${Y(i.group.name)} <span class="group-tab-count">${o}</span>`;let s=document.createElement(`div`);s.className=`group-panel`+(t===0?` active`:``);for(let e of i.tags){let t=document.createElement(`div`);t.className=`word-tag-block`;let n=document.createElement(`div`);n.className=`word-tag-header`,n.innerHTML=`
<span class="word-tag-label">#${Y(e.tag)}</span>
<span class="word-tag-count">${e.words.length}</span>
`;let r=!0,i=document.createElement(`div`);i.className=`word-tag-words`,i.style.display=`none`,n.addEventListener(`click`,()=>{r=!r,i.style.display=r?`none`:``,n.classList.toggle(`collapsed`,r)});for(let t of e.words){let e=document.createElement(`div`);e.className=`word-item`,e.innerHTML=`
<span class="word-key">${Y(t.key)}</span>
<span class="word-value">${Y(t.value)}</span>
`,e.addEventListener(`click`,e=>{e.stopPropagation(),xt(t)}),i.appendChild(e)}t.appendChild(n),t.appendChild(i),s.appendChild(t)}a.addEventListener(`click`,()=>{n.querySelectorAll(`.group-tab`).forEach(e=>e.classList.remove(`active`)),r.querySelectorAll(`.group-panel`).forEach(e=>e.classList.remove(`active`)),a.classList.add(`active`),s.classList.add(`active`)}),n.appendChild(a),r.appendChild(s)}t.appendChild(n),t.appendChild(r)}function vt(){K.innerHTML=`
<div class="card" style="flex:1; display:flex; flex-direction:column; padding:0; overflow:hidden;">
<div class="sets-layout" style="flex:1;">
<ul id="sets-list" class="sets-list"></ul>
<div id="sets-detail" class="sets-detail">
<div class="placeholder-area">
<div class="placeholder-icon">🗂️</div>
<div>Выберите колоду</div>
</div>
</div>
</div>
</div>
<div id="status" class="status-bar">Загрузка...</div>
`}function yt(e){let t=document.getElementById(`sets-list`);if(!t)return;if(t.innerHTML=``,e.length===0){t.innerHTML=`<li style="opacity:0.5;text-align:center;padding:20px;">Нет колод</li>`;return}for(let n of e){let e=document.createElement(`li`);e.textContent=n.name,e.dataset.setId=String(n.id),e.addEventListener(`click`,()=>bt(n,e)),t.appendChild(e)}let n=t.querySelector(`li`);if(n){n.classList.add(`selected`);let t=Number(n.dataset.setId),r=e.find(e=>e.id===t);r&&bt(r,n)}}function bt(e,t){document.querySelectorAll(`#sets-list li`).forEach(e=>e.classList.remove(`selected`)),t.classList.add(`selected`);let n=document.getElementById(`sets-detail`);n&&(n.innerHTML=`
<h3>${Y(e.name)}</h3>
<p><strong>Прямой порядок:</strong> ${Y(e.forward)}</p>
<p><strong>Обратный порядок:</strong> ${Y(e.backward)}</p>
<p><strong>Фильтр:</strong> ${Y(e.filter)}</p>
<label style="font-size:0.8rem;color:var(--text);display:block;margin-top:12px;">Режим тренировки</label>
<select id="mode-select" class="mode-select">
<option value="${B.Default}">${B.Default} — взвешенная случайность</option>
<option value="${B.TrainWorstFirst}">${B.TrainWorstFirst} — слабые слова</option>
<option value="${B.FullRandom}">${B.FullRandom} — полная случайность</option>
</select>
<button class="btn btn-primary btn-start-training" data-set-id="${e.id}">▶ Начать тренировку</button>
`)}function xt(e){Ct(),J=`wordview`,pt(K,e,{onBack:()=>{Q(),Z(`words`)}})}async function St(e){let t=document.getElementById(`mode-select`)?.value||B.Default,n=(await L()).find(t=>t.id===e);if(!n){X(`Колода не найдена`,!0);return}let{words:r,stats:i}=await $e(e);if(r.length===0){X(`Нет слов для тренировки`,!0);return}console.log(`[Training] Запуск тренировки: ${r.length} слов, ${i.length} записей, режим: ${t}`);let a={id:n.id,name:n.name,forward:n.forward,backward:n.backward,filter:n.filter,count:null,worst_words_list:null,open_mode:t},o=r.map(e=>{let t={};if(e.more)try{t=JSON.parse(e.more)}catch{}return{id:e.id,key:e.key,value:e.value,tags:e.tags,more:t,group_id:e.group_id}}),s=i.map(e=>({id:e.id,word_id:e.word_id,set_id:e.set_id,last_open:e.last_opened*1e3,score:e.score}));Ct(),J=`training`,K.innerHTML=`<div id="training-root" style="flex:1;display:flex;flex-direction:column;"></div>`,new dt(document.getElementById(`training-root`),a,o,s,{onFinish:()=>{Q(),Z(`sets`)}})}function Z(e){J=e,Q(),q.forEach(t=>{t.classList.toggle(`active`,t.dataset.page===e)}),e===`words`?(gt(),$()):(vt(),wt())}function Ct(){document.getElementById(`app-nav`).style.display=`none`}function Q(){document.getElementById(`app-nav`).style.display=`flex`}async function $(){try{let e=await tt();_t(e),X(`Слов: ${e.reduce((e,t)=>e+t.tags.reduce((e,t)=>e+t.words.length,0),0)}`)}catch(e){X(`Ошибка загрузки слов`,!0),console.error(e)}}async function wt(){try{let e=await L();yt(e),X(`Колод: ${e.length}`)}catch(e){X(`Ошибка загрузки колод`,!0),console.error(e)}}async function Tt(){try{let e=mt(),t=document.getElementById(`btnDownload`);t&&(t.disabled=!0),X(`Скачивание...`),await Xe(e,W,ee,o),J===`words`&&await $(),X(`База данных скачана.`)}catch{}finally{let e=document.getElementById(`btnDownload`);e&&(e.disabled=!1)}}async function Et(){try{let e=mt(),t=document.getElementById(`btnUpload`);t&&(t.disabled=!0),X(`Загрузка...`),await Ye(e,W,u,l),await o(),J===`words`&&await $(),X(`База данных загружена.`)}catch{}finally{let e=document.getElementById(`btnUpload`);e&&(e.disabled=!1)}}async function Dt(){try{await o(),console.log(`[App] База данных инициализирована.`),q.forEach(e=>{e.addEventListener(`click`,()=>{J!==`training`&&Z(e.dataset.page)})}),Z(`words`),ht(),K.addEventListener(`click`,async e=>{let t=e.target;t.id===`btnDownload`&&await Tt(),t.id===`btnUpload`&&await Et();let n=t.closest(`.btn-start-training`);n&&await St(Number(n.dataset.setId))})}catch(e){console.error(`[App] Ошибка инициализации:`,e)}}Dt();
-1
View File
@@ -1 +0,0 @@
:root{--text:#6b6375;--text-h:#08060d;--bg:#fff;--card-bg:#f8f7f9;--border:#e5e4e7;--accent:#7c3aed;--accent-light:#7c3aed1a;--success:#16a34a;--error:#dc2626;--shadow:#00000014 0 4px 12px;--sans:-apple-system, "Segoe UI", system-ui, Roboto, sans-serif;--mono:ui-monospace, Consolas, monospace;font:16px/1.5 var(--sans);color-scheme:light dark;color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){:root{--text:#9ca3af;--text-h:#f3f4f6;--bg:#0f0f13;--card-bg:#1a1b23;--border:#2e303a;--accent:#a78bfa;--accent-light:#a78bfa26;--shadow:#0006 0 4px 12px}}*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}body{overscroll-behavior:none;-webkit-overflow-scrolling:touch;touch-action:manipulation;flex-direction:column;min-height:100svh;margin:0;display:flex}#app{width:100%;max-width:480px;padding:0;padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right);padding-bottom:env(safe-area-inset-bottom);flex-direction:column;flex:1;margin:0 auto;display:flex}.app-header{padding:1.5rem 1.25rem .5rem;padding-top:calc(1.5rem + env(safe-area-inset-top));text-align:center}.app-header h1{font-family:var(--sans);color:var(--text-h);letter-spacing:-.5px;margin:0;font-size:1.75rem;font-weight:700}.app-header p{color:var(--text);margin:.25rem 0 0;font-size:.9rem}.app-content{flex-direction:column;flex:1;gap:1rem;padding:1rem 1.25rem;display:flex}.card{background:var(--card-bg);border:1px solid var(--border);box-shadow:var(--shadow);border-radius:12px;padding:1.25rem}.card-title{color:var(--text);text-transform:uppercase;letter-spacing:.5px;margin:0 0 .75rem;font-size:.85rem;font-weight:600}.sync-input-group{flex-direction:column;gap:.5rem;display:flex}.sync-input-group input{font-family:var(--mono);border:1px solid var(--border);background:var(--bg);color:var(--text-h);border-radius:8px;outline:none;width:100%;padding:.65rem .75rem;font-size:.9rem;transition:border-color .2s}.sync-input-group input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-light)}.sync-buttons{gap:.5rem;display:flex}.btn{font-family:var(--sans);border:1px solid var(--border);background:var(--card-bg);min-height:48px;color:var(--text-h);cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;user-select:none;border-radius:10px;flex:1;justify-content:center;align-items:center;gap:.4rem;padding:.75rem 1rem;font-size:.9rem;font-weight:600;transition:all .2s;display:flex}.btn:hover{border-color:var(--accent);background:var(--accent-light)}.btn:active{opacity:.9;transform:scale(.97)}.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}.btn-primary:hover{filter:brightness(1.1)}.status-bar{text-align:center;background:var(--accent-light);color:var(--text);border-radius:8px;min-height:36px;margin-top:auto;padding:.5rem .75rem;font-size:.85rem}.status-bar.error{color:var(--error);background:#dc26261a}.status-bar.success{color:var(--success);background:#16a34a1a}.app-footer{padding:1rem 1.25rem;padding-bottom:calc(1rem + env(safe-area-inset-bottom));text-align:center;color:var(--text);opacity:.6;font-size:.75rem}.divider{border:none;border-top:1px solid var(--border);margin:.25rem 0}.placeholder-area{color:var(--text);opacity:.4;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:.5rem;min-height:100px;font-size:.85rem;display:flex}.word-list{margin:0;padding:0;list-style:none}.word-item{background:var(--bg);border:1px solid var(--border);box-shadow:var(--shadow);border-radius:12px;flex-wrap:wrap;align-items:center;margin-bottom:10px;padding:14px 16px;transition:border-color .2s;display:flex}.word-item:hover{border-color:var(--accent)}.word-item-empty{text-align:center;color:var(--text);opacity:.6;align-items:center;padding:24px 16px;font-size:.9rem}.word-key{color:var(--text-h);letter-spacing:.5px;flex-shrink:0;font-size:1.2rem;font-weight:600}.word-value{color:var(--text);text-align:right;flex:1;font-size:.95rem}.word-tags{color:var(--accent);background:var(--accent-light);border-radius:4px;flex-basis:100%;order:1;align-self:flex-start;margin-top:2px;padding:2px 8px;font-size:.7rem;font-weight:500}.placeholder-icon{opacity:.6;font-size:2rem}.group-tabs{border-bottom:1px solid var(--border);-webkit-overflow-scrolling:touch;scrollbar-width:none;gap:0;display:flex;overflow-x:auto}.group-tabs::-webkit-scrollbar{display:none}.group-tab{min-width:0;font-family:var(--sans);color:var(--text);cursor:pointer;-webkit-tap-highlight-color:transparent;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;flex:1;align-items:center;gap:.5rem;padding:.65rem .5rem;font-size:.85rem;font-weight:600;transition:all .2s;display:flex}.group-tab.active{color:var(--accent);border-bottom-color:var(--accent);background:var(--accent-light)}.group-tab-count{background:var(--accent-light);color:var(--accent);borLine truncated
Binary file not shown.
Binary file not shown.
-11
View File
@@ -1,11 +0,0 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_8)">
<rect width="256" height="256" rx="50" fill="#00C8B3"/>
<path d="M134.6 179.85C134.9 178.65 135.1 177.35 135.2 175.95C135.4 174.45 135.5 172.95 135.5 171.45C135.5 170.45 135.5 168.25 135.5 164.85C135.5 161.45 135.5 157.3 135.5 152.4C135.5 147.4 135.5 142 135.5 136.2C135.5 130.4 135.5 124.6 135.5 118.8C135.5 112.9 135.5 107.4 135.5 102.3C135.5 97.2 135.5 92.8 135.5 89.1C135.5 85.4 135.5 82.9 135.5 81.6C135.5 78.7 135.4 76.3 135.2 74.4C135 72.4 134.85 71.1 134.75 70.5H148.55C148.55 71.1 148.45 72.4 148.25 74.4C148.05 76.4 147.95 78.8 147.95 81.6C147.95 83 147.95 85.5 147.95 89.1C147.95 92.6 147.95 96.9 147.95 102C147.95 107 147.95 112.4 147.95 118.2C147.95 123.9 147.95 129.55 147.95 135.15C147.95 140.75 147.95 145.95 147.95 150.75C147.95 155.45 147.95 159.35 147.95 162.45C147.95 165.55 147.95 167.45 147.95 168.15C152.65 165.95 157.65 163.05 162.95 159.45C168.25 155.85 173.4 151.65 178.4 146.85C183.5 142.05 187.95 136.85 191.75 131.25L198.8 141.45C194.5 147.25 189.45 152.8 183.65 158.1C177.95 163.4 172 168.15 165.8 172.35C159.6 176.55 153.55 180.1 147.65 183C146.45 183.7 145.4 184.35 144.5 184.95C143.7 185.55 143.05 186.05 142.55 186.45L134.6 179.85ZM65.9 179.1C72.2 174.6 77.55 169.15 81.95 162.75C86.35 156.35 89.7 149.5 92 142.2C93.1 138.8 93.95 134.55 94.55 129.45C95.15 124.35 95.6 118.95 95.9 113.25C96.2 107.45 96.35 101.85 96.35 96.45C96.45 90.95 96.5 86.1 96.5 81.9C96.5 79.7 96.4 77.75 96.2 76.05C96 74.25 95.75 72.55 95.45 70.95H109.25C109.15 71.55 109.05 72.45 108.95 73.65C108.85 74.75 108.75 76 108.65 77.4C108.55 78.8 108.5 80.25 108.5 81.75C108.5 85.95 108.45 90.9 108.35 96.6C108.25 102.2 108 108.1 107.6 114.3C107.3 120.4 106.85 126.2 106.25 131.7C105.75 137.2 104.95 141.8 103.85 145.5C101.65 153.5 98.2 161.05 93.5 168.15C88.9 175.25 83.45 181.45 77.15 186.75L65.9 179.1Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_1_8">
<rect width="256" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

-24
View File
@@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="bluesky-icon" viewBox="0 0 16 17">
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
</symbol>
<symbol id="discord-icon" viewBox="0 0 20 19">
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
</symbol>
<symbol id="documentation-icon" viewBox="0 0 21 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
</symbol>
<symbol id="github-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
</symbol>
<symbol id="social-icon" viewBox="0 0 20 20">
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
</symbol>
<symbol id="x-icon" viewBox="0 0 19 19">
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

-33
View File
@@ -1,33 +0,0 @@
<!doctype html>
<html lang="en" translate="no">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/icon-192.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="theme-color" content="#7c3aed" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="mobile-web-app-capable" content="yes" />
<title>JapLearn</title>
<script type="module" crossorigin src="/assets/index-CzXGdRG9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-XX_s0Oss.css">
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
<body>
<div id="app">
<header class="app-header">
<h1>JapLearn</h1>
<p>Интервальные повторения японских слов</p>
</header>
<div id="page-container" class="app-content">
<!-- Страницы рендерятся сюда динамически -->
</div>
<nav id="app-nav" class="app-nav">
<button class="nav-btn active" data-page="words">📚 Слова</button>
<button class="nav-btn" data-page="sets">🗂️ Колоды</button>
</nav>
</div>
</body>
</html>
-22
View File
@@ -1,22 +0,0 @@
{
"name": "JapLearn",
"short_name": "JapLearn",
"description": "Интервальные повторения японских слов",
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#ffffff",
"theme_color": "#7c3aed",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
-1
View File
@@ -1 +0,0 @@
{"name":"JapLearn","short_name":"JapLearn","description":"Интервальные повторения японских слов","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#7c3aed","lang":"en","scope":"/","orientation":"portrait","icons":[{"src":"icon-192.png","sizes":"192x192","type":"image/png"},{"src":"icon-512.png","sizes":"512x512","type":"image/png"},{"src":"icon-512.png","sizes":"512x512","type":"image/png","purpose":"any maskable"}]}
-1
View File
@@ -1 +0,0 @@
if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/sw.js', { scope: '/' })})}
-1
View File
@@ -1 +0,0 @@
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const a=e||("document"in self?document.currentScript.src:"")||location.href;if(s[a])return;let o={};const c=e=>i(e,a),l={module:{uri:a},exports:o,require:c};s[a]=Promise.all(n.map(e=>l[e]||c(e))).then(e=>(r(...e),o))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"index.html",revision:"f63f75d4eabe50ba7027246e2712fd0f"},{url:"icons.svg",revision:"3b4fcfcf393eca4d264dca4a4663bc37"},{url:"icon-512.png",revision:"742f835d951738a122569da26fd5436c"},{url:"icon-192.png",revision:"ab94a1a8a8b47289c8a62e56929aa22c"},{url:"favicon.svg",revision:"94b5c7ec23607198a93dfd22a617c192"},{url:"assets/zstd-CV7N7xSg.wasm",revision:null},{url:"assets/wa-sqlite-async-DY3_ptqa.wasm",revision:null},{url:"assets/index-XX_s0Oss.css",revision:null},{url:"assets/index-CzXGdRG9.js",revision:null},{url:"assets/db-worker-DEWOyJFk.js",revision:null},{url:"favicon.svg",revision:"94b5c7ec23607198a93dfd22a617c192"},{url:"icon-192.png",revision:"ab94a1a8a8b47289c8a62e56929aa22c"},{url:"icon-512.png",revision:"742f835d951738a122569da26fd5436c"},{url:"manifest.webmanifest",revision:"a772ac7a933e1fc8361edca90e19a887"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
-1
View File
@@ -1 +0,0 @@
define(["exports"],function(t){"use strict";try{self["workbox:core:7.4.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.4.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class o{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let o=r&&r.handler;const c=t.method;if(!o&&this.i.has(c)&&(o=this.i.get(c)),!o)return;let a;try{a=o.handle({url:s,request:t,event:e,params:i})}catch(t){a=Promise.reject(t)}const h=r&&r.catchHandler;return a instanceof Promise&&(this.o||h)&&(a=a.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),a}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const o=r.match({url:t,sameOrigin:e,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&0===i.length||o.constructor===Object&&0===Object.keys(o).length||"boolean"==typeof o)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let c;const a=()=>(c||(c=new o,c.addFetchListener(),c.addCacheListener()),c);function h(t,e,n){let o;if("string"==typeof t){const s=new URL(t,location.href);o=new i(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)o=new r(t,e,n);else if("function"==typeof t)o=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});o=t}return a().registerRoute(o),o}const u={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},l=t=>[u.prefix,t,u.suffix].filter(t=>t&&t.length>0).join("-"),f=t=>t||l(u.precache),w=t=>t||l(u.runtime);function d(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.4.0"]&&_()}catch(t){}function p(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class y{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class g{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.h.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.h=t}}let R;async function m(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=function(){if(void 0===R){const t=new Response("");if("body"in t)try{new Response(t.body),R=!0}catch(t){R=!1}R=!1}return R}()?i.body:await i.blob();return new Response(o,r)}function v(t,e){const s=new URL(t);for(const t of e)s.searchLine truncated