fix speech view

This commit is contained in:
micialware committed 2026-07-03 13:06:14 +03:00
1 parent 731951ca75
commit e00e524b7a
19 files changed
+462 -15

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
(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
@@ -0,0 +1,143 @@
(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
@@ -0,0 +1 @@
: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.