├── .editorconfig ├── .env ├── .env.production ├── .gitignore ├── .prettierrc ├── .vscode └── extensions.json ├── .yarnrc.yml ├── LICENSE ├── README.md ├── build.sh ├── components.d.ts ├── env.d.ts ├── eslint.config.js ├── index.html ├── package.json ├── public ├── broken-image.png ├── favicon.ico ├── ficons │ ├── 3g2.svg │ ├── 3ga.svg │ ├── 3gp.svg │ ├── 7z.svg │ ├── aa.svg │ ├── aac.svg │ ├── ac.svg │ ├── accdb.svg │ ├── accdt.svg │ ├── ace.svg │ ├── adn.svg │ ├── ai.svg │ ├── aif.svg │ ├── aifc.svg │ ├── aiff.svg │ ├── ait.svg │ ├── amr.svg │ ├── ani.svg │ ├── apk.svg │ ├── app.svg │ ├── applescript.svg │ ├── asax.svg │ ├── asc.svg │ ├── ascx.svg │ ├── asf.svg │ ├── ash.svg │ ├── ashx.svg │ ├── asm.svg │ ├── asmx.svg │ ├── asp.svg │ ├── aspx.svg │ ├── asx.svg │ ├── au.svg │ ├── aup.svg │ ├── avi.svg │ ├── axd.svg │ ├── aze.svg │ ├── bak.svg │ ├── bash.svg │ ├── bat.svg │ ├── bin.svg │ ├── blank.svg │ ├── bmp.svg │ ├── bowerrc.svg │ ├── bpg.svg │ ├── browser.svg │ ├── bz2.svg │ ├── bzempty.svg │ ├── c.svg │ ├── cab.svg │ ├── cad.svg │ ├── caf.svg │ ├── cal.svg │ ├── catalog.json │ ├── cd.svg │ ├── cdda.svg │ ├── cer.svg │ ├── cfg.svg │ ├── cfm.svg │ ├── cfml.svg │ ├── cgi.svg │ ├── chm.svg │ ├── class.svg │ ├── cmd.svg │ ├── code-workspace.svg │ ├── codekit.svg │ ├── coffee.svg │ ├── coffeelintignore.svg │ ├── com.svg │ ├── compile.svg │ ├── conf.svg │ ├── config.svg │ ├── cpp.svg │ ├── cptx.svg │ ├── cr2.svg │ ├── crdownload.svg │ ├── crt.svg │ ├── crypt.svg │ ├── cs.svg │ ├── csh.svg │ ├── cson.svg │ ├── csproj.svg │ ├── css.svg │ ├── csv.svg │ ├── cue.svg │ ├── cur.svg │ ├── dart.svg │ ├── dat.svg │ ├── data.svg │ ├── db.svg │ ├── dbf.svg │ ├── deb.svg │ ├── default.svg │ ├── dgn.svg │ ├── dist.svg │ ├── diz.svg │ ├── dll.svg │ ├── dmg.svg │ ├── dng.svg │ ├── doc.svg │ ├── docb.svg │ ├── docm.svg │ ├── docx.svg │ ├── dot.svg │ ├── dotm.svg │ ├── dotx.svg │ ├── download.svg │ ├── dpj.svg │ ├── ds_store.svg │ ├── dsn.svg │ ├── dtd.svg │ ├── dwg.svg │ ├── dxf.svg │ ├── editorconfig.svg │ ├── el.svg │ ├── elf.svg │ ├── eml.svg │ ├── enc.svg │ ├── eot.svg │ ├── eps.svg │ ├── epub.svg │ ├── eslintignore.svg │ ├── exe.svg │ ├── f4v.svg │ ├── fax.svg │ ├── fb2.svg │ ├── fla.svg │ ├── flac.svg │ ├── flv.svg │ ├── fnt.svg │ ├── folder.svg │ ├── fon.svg │ ├── gadget.svg │ ├── gdp.svg │ ├── gem.svg │ ├── gif.svg │ ├── gitattributes.svg │ ├── gitignore.svg │ ├── go.svg │ ├── gpg.svg │ ├── gpl.svg │ ├── gradle.svg │ ├── gz.svg │ ├── h.svg │ ├── handlebars.svg │ ├── hbs.svg │ ├── heic.svg │ ├── hlp.svg │ ├── hs.svg │ ├── hsl.svg │ ├── htm.svg │ ├── html.svg │ ├── ibooks.svg │ ├── icns.svg │ ├── ico.svg │ ├── ics.svg │ ├── idx.svg │ ├── iff.svg │ ├── ifo.svg │ ├── image.svg │ ├── img.svg │ ├── iml.svg │ ├── in.svg │ ├── inc.svg │ ├── indd.svg │ ├── inf.svg │ ├── info.svg │ ├── ini.svg │ ├── inv.svg │ ├── iso.svg │ ├── j2.svg │ ├── jar.svg │ ├── java.svg │ ├── jpe.svg │ ├── jpeg.svg │ ├── jpg.svg │ ├── js.svg │ ├── json.svg │ ├── jsp.svg │ ├── jsx.svg │ ├── key.svg │ ├── kf8.svg │ ├── kmk.svg │ ├── ksh.svg │ ├── kt.svg │ ├── kts.svg │ ├── kup.svg │ ├── less.svg │ ├── lex.svg │ ├── licx.svg │ ├── lisp.svg │ ├── lit.svg │ ├── lnk.svg │ ├── lock.svg │ ├── log.svg │ ├── lua.svg │ ├── m.svg │ ├── m2v.svg │ ├── m3u.svg │ ├── m3u8.svg │ ├── m4.svg │ ├── m4a.svg │ ├── m4r.svg │ ├── m4v.svg │ ├── map.svg │ ├── master.svg │ ├── mc.svg │ ├── md.svg │ ├── mdb.svg │ ├── mdf.svg │ ├── me.svg │ ├── mi.svg │ ├── mid.svg │ ├── midi.svg │ ├── mk.svg │ ├── mkv.svg │ ├── mm.svg │ ├── mng.svg │ ├── mo.svg │ ├── mobi.svg │ ├── mod.svg │ ├── mov.svg │ ├── mp2.svg │ ├── mp3.svg │ ├── mp4.svg │ ├── mpa.svg │ ├── mpd.svg │ ├── mpe.svg │ ├── mpeg.svg │ ├── mpg.svg │ ├── mpga.svg │ ├── mpp.svg │ ├── mpt.svg │ ├── msg.svg │ ├── msi.svg │ ├── msu.svg │ ├── nef.svg │ ├── nes.svg │ ├── nfo.svg │ ├── nix.svg │ ├── npmignore.svg │ ├── ocx.svg │ ├── odb.svg │ ├── ods.svg │ ├── odt.svg │ ├── ogg.svg │ ├── ogv.svg │ ├── ost.svg │ ├── otf.svg │ ├── ott.svg │ ├── ova.svg │ ├── ovf.svg │ ├── p12.svg │ ├── p7b.svg │ ├── pages.svg │ ├── part.svg │ ├── pcd.svg │ ├── pdb.svg │ ├── pdf.svg │ ├── pem.svg │ ├── pfx.svg │ ├── pgp.svg │ ├── ph.svg │ ├── phar.svg │ ├── php.svg │ ├── pid.svg │ ├── pkg.svg │ ├── pl.svg │ ├── plist.svg │ ├── pm.svg │ ├── png.svg │ ├── po.svg │ ├── pom.svg │ ├── pot.svg │ ├── potx.svg │ ├── pps.svg │ ├── ppsx.svg │ ├── ppt.svg │ ├── pptm.svg │ ├── pptx.svg │ ├── prop.svg │ ├── ps.svg │ ├── ps1.svg │ ├── psd.svg │ ├── psp.svg │ ├── pst.svg │ ├── pub.svg │ ├── py.svg │ ├── pyc.svg │ ├── qt.svg │ ├── ra.svg │ ├── ram.svg │ ├── rar.svg │ ├── raw.svg │ ├── rb.svg │ ├── rdf.svg │ ├── rdl.svg │ ├── reg.svg │ ├── resx.svg │ ├── retry.svg │ ├── rm.svg │ ├── rom.svg │ ├── rpm.svg │ ├── rpt.svg │ ├── rsa.svg │ ├── rss.svg │ ├── rst.svg │ ├── rtf.svg │ ├── ru.svg │ ├── rub.svg │ ├── sass.svg │ ├── scss.svg │ ├── sdf.svg │ ├── sed.svg │ ├── sh.svg │ ├── sit.svg │ ├── sitemap.svg │ ├── skin.svg │ ├── sldm.svg │ ├── sldx.svg │ ├── sln.svg │ ├── sol.svg │ ├── sphinx.svg │ ├── sql.svg │ ├── sqlite.svg │ ├── step.svg │ ├── stl.svg │ ├── svg.svg │ ├── swd.svg │ ├── swf.svg │ ├── swift.svg │ ├── swp.svg │ ├── sys.svg │ ├── tar.svg │ ├── tax.svg │ ├── tcsh.svg │ ├── tex.svg │ ├── tfignore.svg │ ├── tga.svg │ ├── tgz.svg │ ├── tif.svg │ ├── tiff.svg │ ├── tmp.svg │ ├── tmx.svg │ ├── torrent.svg │ ├── tpl.svg │ ├── ts.svg │ ├── tsv.svg │ ├── ttf.svg │ ├── twig.svg │ ├── txt.svg │ ├── udf.svg │ ├── vb.svg │ ├── vbproj.svg │ ├── vbs.svg │ ├── vcd.svg │ ├── vcf.svg │ ├── vcs.svg │ ├── vdi.svg │ ├── vdx.svg │ ├── vmdk.svg │ ├── vob.svg │ ├── vox.svg │ ├── vscodeignore.svg │ ├── vsd.svg │ ├── vss.svg │ ├── vst.svg │ ├── vsx.svg │ ├── vtx.svg │ ├── war.svg │ ├── wav.svg │ ├── wbk.svg │ ├── webinfo.svg │ ├── webm.svg │ ├── webp.svg │ ├── wma.svg │ ├── wmf.svg │ ├── wmv.svg │ ├── woff.svg │ ├── woff2.svg │ ├── wps.svg │ ├── wsf.svg │ ├── xaml.svg │ ├── xcf.svg │ ├── xfl.svg │ ├── xlm.svg │ ├── xls.svg │ ├── xlsm.svg │ ├── xlsx.svg │ ├── xlt.svg │ ├── xltm.svg │ ├── xltx.svg │ ├── xml.svg │ ├── xpi.svg │ ├── xps.svg │ ├── xrb.svg │ ├── xsd.svg │ ├── xsl.svg │ ├── xspf.svg │ ├── xz.svg │ ├── yaml.svg │ ├── yml.svg │ ├── z.svg │ ├── zip.svg │ └── zsh.svg ├── icons │ ├── logo-114.png │ ├── logo-120.png │ ├── logo-144.png │ ├── logo-152.png │ ├── logo-180.png │ ├── logo-192.png │ ├── logo-512.png │ ├── logo-57.png │ ├── logo-60.png │ ├── logo-72.png │ └── logo-76.png ├── manifest.json └── sw.js ├── src ├── App.vue ├── assets │ ├── logo.svg │ ├── mobile-warning.svg │ └── touch-phone.svg ├── components │ ├── AddFeedModal.vue │ ├── AddToTagsModal.vue │ ├── AllCheckedAlert.vue │ ├── AudioPlayer.vue │ ├── Breadcrumb.vue │ ├── BucketFilter.vue │ ├── ChangeScreenMirrorQualityModal.vue │ ├── ConfirmModal.vue │ ├── DeleteConfirm.vue │ ├── DeleteFileConfirm.vue │ ├── DeleteItemsConfirm.vue │ ├── Dropdown.vue │ ├── DropdownMenu.vue │ ├── EditContactModal.vue │ ├── EditToolbar.vue │ ├── EditValueModal.vue │ ├── FeatureButton.vue │ ├── FieldId.vue │ ├── FileSearchInput.vue │ ├── HeaderActions.vue │ ├── IconButton.vue │ ├── ImageVideoListSkeleton.vue │ ├── ItemTags.vue │ ├── LeftSidebar.vue │ ├── MediaSidebar.vue │ ├── MonacoEditor.vue │ ├── Multiselect.vue │ ├── OutlinedTextField.vue │ ├── PChat.vue │ ├── PNotifications.vue │ ├── PromptModal.vue │ ├── SearchInput.vue │ ├── TagFilter.vue │ ├── TaskItem.vue │ ├── TaskList.vue │ ├── ThemeChanger.vue │ ├── UpdateTagRelationsModal.vue │ ├── VPagination.vue │ ├── buttons │ │ ├── FilledButton.vue │ │ ├── OutlinedButton.vue │ │ └── index.ts │ ├── chat │ │ ├── ChatFiles.vue │ │ ├── ChatImages.vue │ │ └── ChatLinkPreviews.vue │ ├── contextmenu │ │ ├── ContextMenu.vue │ │ ├── ContextMenuDefine.ts │ │ ├── ContextSubMenu.vue │ │ └── index.ts │ ├── jsonviewer │ │ ├── json-box.vue │ │ ├── json-viewer.vue │ │ ├── types │ │ │ ├── json-array.vue │ │ │ ├── json-boolean.vue │ │ │ ├── json-date.vue │ │ │ ├── json-function.vue │ │ │ ├── json-number.vue │ │ │ ├── json-object.vue │ │ │ ├── json-regexp.vue │ │ │ ├── json-string.vue │ │ │ └── json-undefined.vue │ │ └── utils.js │ ├── lightbox │ │ ├── LightboxInfo.vue │ │ ├── lightbox.vue │ │ ├── types.ts │ │ └── utils │ │ │ ├── hooks.ts │ │ │ └── index.ts │ ├── modal │ │ ├── ModalContainer.vue │ │ ├── ModalContainerItem.vue │ │ ├── index.ts │ │ ├── methods.ts │ │ ├── onBeforeModalClose.ts │ │ ├── router.ts │ │ └── utils │ │ │ ├── Modal.ts │ │ │ ├── ModalError.ts │ │ │ ├── config.ts │ │ │ ├── dto-modal-options.ts │ │ │ ├── event-close.ts │ │ │ ├── guards.ts │ │ │ ├── initialize.ts │ │ │ ├── instances.ts │ │ │ ├── state.ts │ │ │ └── types.ts │ ├── popper │ │ ├── Arrow.vue │ │ ├── Popper.vue │ │ ├── use-content.ts │ │ └── use-popper.ts │ ├── toaster │ │ ├── PToaster.vue │ │ ├── index.ts │ │ └── timer.ts │ └── virtualscroll │ │ ├── index.ts │ │ ├── item.tsx │ │ ├── props.ts │ │ ├── virtual-list.tsx │ │ └── virtual.ts ├── hooks │ ├── audios.ts │ ├── chat.ts │ ├── feed-entries.ts │ ├── feeds.ts │ ├── files.ts │ ├── key-events.ts │ ├── list.ts │ ├── markdown.ts │ ├── media-trash.ts │ ├── media.ts │ ├── notes.ts │ ├── search.ts │ ├── sidebar.ts │ ├── tags.ts │ ├── text-selection.ts │ └── upload.ts ├── lib │ ├── agent │ │ ├── agent.ts │ │ ├── presets.ts │ │ ├── types.ts │ │ ├── userAgent.ts │ │ ├── userAgentData.ts │ │ └── utils.ts │ ├── api │ │ ├── api.ts │ │ ├── create-http-link.ts │ │ ├── crypto.ts │ │ ├── file.ts │ │ ├── fragments.ts │ │ ├── mutation.ts │ │ ├── query.ts │ │ ├── sha512.ts │ │ └── sjcl-arraybuffer.ts │ ├── array.ts │ ├── contact │ │ └── contact.ts │ ├── data.ts │ ├── effect.ts │ ├── feature.ts │ ├── file.ts │ ├── format.ts │ ├── interfaces.ts │ ├── list.ts │ ├── sdk-version.ts │ ├── search.ts │ ├── strutil.ts │ ├── tag.ts │ ├── theme │ │ ├── apply-theme-string.ts │ │ ├── material-color-helpers.ts │ │ └── theme.ts │ ├── timeago │ │ ├── format.ts │ │ ├── index.ts │ │ ├── interface.ts │ │ ├── lang │ │ │ ├── ar.ts │ │ │ ├── be.ts │ │ │ ├── bg.ts │ │ │ ├── bn_IN.ts │ │ │ ├── ca.ts │ │ │ ├── cs.ts │ │ │ ├── da.ts │ │ │ ├── de.ts │ │ │ ├── el.ts │ │ │ ├── en_US.ts │ │ │ ├── en_short.ts │ │ │ ├── es.ts │ │ │ ├── eu.ts │ │ │ ├── fa.ts │ │ │ ├── fi.ts │ │ │ ├── fr.ts │ │ │ ├── gl.ts │ │ │ ├── he.ts │ │ │ ├── hi_IN.ts │ │ │ ├── hu.ts │ │ │ ├── id_ID.ts │ │ │ ├── index.ts │ │ │ ├── it.ts │ │ │ ├── ja.ts │ │ │ ├── ka.ts │ │ │ ├── ko.ts │ │ │ ├── ml.ts │ │ │ ├── my.ts │ │ │ ├── nb_NO.ts │ │ │ ├── nl.ts │ │ │ ├── nn_NO.ts │ │ │ ├── oc.ts │ │ │ ├── pl.ts │ │ │ ├── pt_BR.ts │ │ │ ├── ro.ts │ │ │ ├── ru.ts │ │ │ ├── sq.ts │ │ │ ├── sr.ts │ │ │ ├── sv.ts │ │ │ ├── ta.ts │ │ │ ├── th.ts │ │ │ ├── tk.ts │ │ │ ├── tr.ts │ │ │ ├── uk.ts │ │ │ ├── vi.ts │ │ │ ├── zh_CN.ts │ │ │ └── zh_TW.ts │ │ ├── realtime.ts │ │ ├── register.ts │ │ └── utils │ │ │ ├── date.ts │ │ │ └── dom.ts │ ├── upload │ │ ├── upload-queue.ts │ │ └── upload.ts │ └── validator.ts ├── locales │ ├── bn.ts │ ├── de.ts │ ├── en-US.ts │ ├── es.ts │ ├── fr.ts │ ├── hi.ts │ ├── it.ts │ ├── ja.ts │ ├── ko.ts │ ├── nl.ts │ ├── pt.ts │ ├── ru.ts │ ├── ta.ts │ ├── tr.ts │ ├── vi.ts │ ├── zh-CN.ts │ └── zh-TW.ts ├── main.ts ├── plugins │ ├── apollo.ts │ ├── clickaway.ts │ ├── eventbus.ts │ ├── i18n.ts │ ├── monacoworker.ts │ ├── router.ts │ ├── tapphone.ts │ └── tooltip.ts ├── registerServiceWorker.ts ├── stores │ ├── files.ts │ ├── main.ts │ └── temp.ts ├── styles │ ├── _alert.scss │ ├── _animation.scss │ ├── _audio.scss │ ├── _base.scss │ ├── _buttons.scss │ ├── _card.scss │ ├── _chat.scss │ ├── _files.scss │ ├── _form.scss │ ├── _katex.scss │ ├── _markdown.scss │ ├── _md.scss │ ├── _media.scss │ ├── _mobile.scss │ ├── _pagination.scss │ ├── _print.scss │ ├── _selectable.scss │ ├── fonts │ │ ├── KaTeX_AMS-Regular.ttf │ │ ├── KaTeX_AMS-Regular.woff │ │ ├── KaTeX_AMS-Regular.woff2 │ │ ├── KaTeX_Caligraphic-Bold.ttf │ │ ├── KaTeX_Caligraphic-Bold.woff │ │ ├── KaTeX_Caligraphic-Bold.woff2 │ │ ├── KaTeX_Caligraphic-Regular.ttf │ │ ├── KaTeX_Caligraphic-Regular.woff │ │ ├── KaTeX_Caligraphic-Regular.woff2 │ │ ├── KaTeX_Fraktur-Bold.ttf │ │ ├── KaTeX_Fraktur-Bold.woff │ │ ├── KaTeX_Fraktur-Bold.woff2 │ │ ├── KaTeX_Fraktur-Regular.ttf │ │ ├── KaTeX_Fraktur-Regular.woff │ │ ├── KaTeX_Fraktur-Regular.woff2 │ │ ├── KaTeX_Main-Bold.ttf │ │ ├── KaTeX_Main-Bold.woff │ │ ├── KaTeX_Main-Bold.woff2 │ │ ├── KaTeX_Main-BoldItalic.ttf │ │ ├── KaTeX_Main-BoldItalic.woff │ │ ├── KaTeX_Main-BoldItalic.woff2 │ │ ├── KaTeX_Main-Italic.ttf │ │ ├── KaTeX_Main-Italic.woff │ │ ├── KaTeX_Main-Italic.woff2 │ │ ├── KaTeX_Main-Regular.ttf │ │ ├── KaTeX_Main-Regular.woff │ │ ├── KaTeX_Main-Regular.woff2 │ │ ├── KaTeX_Math-BoldItalic.ttf │ │ ├── KaTeX_Math-BoldItalic.woff │ │ ├── KaTeX_Math-BoldItalic.woff2 │ │ ├── KaTeX_Math-Italic.ttf │ │ ├── KaTeX_Math-Italic.woff │ │ ├── KaTeX_Math-Italic.woff2 │ │ ├── KaTeX_SansSerif-Bold.ttf │ │ ├── KaTeX_SansSerif-Bold.woff │ │ ├── KaTeX_SansSerif-Bold.woff2 │ │ ├── KaTeX_SansSerif-Italic.ttf │ │ ├── KaTeX_SansSerif-Italic.woff │ │ ├── KaTeX_SansSerif-Italic.woff2 │ │ ├── KaTeX_SansSerif-Regular.ttf │ │ ├── KaTeX_SansSerif-Regular.woff │ │ ├── KaTeX_SansSerif-Regular.woff2 │ │ ├── KaTeX_Script-Regular.ttf │ │ ├── KaTeX_Script-Regular.woff │ │ ├── KaTeX_Script-Regular.woff2 │ │ ├── KaTeX_Size1-Regular.ttf │ │ ├── KaTeX_Size1-Regular.woff │ │ ├── KaTeX_Size1-Regular.woff2 │ │ ├── KaTeX_Size2-Regular.ttf │ │ ├── KaTeX_Size2-Regular.woff │ │ ├── KaTeX_Size2-Regular.woff2 │ │ ├── KaTeX_Size3-Regular.ttf │ │ ├── KaTeX_Size3-Regular.woff │ │ ├── KaTeX_Size3-Regular.woff2 │ │ ├── KaTeX_Size4-Regular.ttf │ │ ├── KaTeX_Size4-Regular.woff │ │ ├── KaTeX_Size4-Regular.woff2 │ │ ├── KaTeX_Typewriter-Regular.ttf │ │ ├── KaTeX_Typewriter-Regular.woff │ │ ├── KaTeX_Typewriter-Regular.woff2 │ │ └── noto-sans-sc │ │ │ ├── regular.woff2 │ │ │ ├── w-500.woff2 │ │ │ ├── w-600.woff2 │ │ │ ├── w-700.woff2 │ │ │ └── w-800.woff2 │ └── main.scss ├── typings.d.ts └── views │ ├── DeviceInfoView.vue │ ├── HomeView.vue │ ├── JsonViewerView.vue │ ├── LoginView.vue │ ├── MainView.vue │ ├── QrCodeGeneratorView.vue │ ├── ScreenMirrorView.vue │ ├── TextFileView.vue │ ├── apps │ ├── AppsSidebar.vue │ └── AppsView.vue │ ├── audios │ ├── AudiosSidebar.vue │ └── AudiosView.vue │ ├── calls │ ├── CallsSidebar.vue │ └── CallsView.vue │ ├── contacts │ ├── ContactsSidebar.vue │ └── ContactsView.vue │ ├── feeds │ ├── FeedEntryView.vue │ ├── FeedModal.vue │ ├── FeedsSidebar.vue │ └── FeedsSidebar2.vue │ ├── files │ ├── FilesRecentView.vue │ ├── FilesSidebar.vue │ └── FilesView.vue │ ├── images │ ├── ImagesSidebar.vue │ └── ImagesView.vue │ ├── messages │ ├── MessagesSidebar.vue │ └── MessagesView.vue │ ├── notes │ ├── NoteEditView.vue │ ├── NotesSidebar.vue │ └── NotesView.vue │ └── videos │ ├── VideosSidebar.vue │ └── VideosView.vue ├── tsconfig.json ├── tsconfig.vite-config.json ├── vite.config.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # VITE_APP_API_HOST=192.168.1.8:8443 -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VITE_APP_API_HOST="" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | .yarn -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "printWidth": 200, 5 | "semi": false, 6 | "singleQuote": true 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ismartcoding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | yarn build 4 | setopt localoptions rmstarsilent 5 | rm -rf ../plain-app/app/src/main/resources/web/* 6 | cp -r dist/* ../plain-app/app/src/main/resources/web/ 7 | 8 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | interface ImportMetaEnv { 3 | readonly VITE_APP_API_HOST: string 4 | } 5 | interface ImportMeta { 6 | readonly env: ImportMetaEnv 7 | } 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | PlainApp 9 | 10 | 11 | 12 |
Your browser is not supported or up-to-date. Please try updating it.
13 |
14 | 15 | 16 | 50 | 51 | -------------------------------------------------------------------------------- /public/broken-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/broken-image.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/favicon.ico -------------------------------------------------------------------------------- /public/ficons/aze.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/blank.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/chm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/code-workspace.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/coffeelintignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/dll.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/elf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/eslintignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/f4v.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/fla.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/flv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/folder.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/gadget.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/gitattributes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/gitignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/hlp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/key.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/md.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/npmignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/swf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/tfignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/ttf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/twig.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/vscodeignore.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/vtx.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/ficons/xfl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/icons/logo-114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-114.png -------------------------------------------------------------------------------- /public/icons/logo-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-120.png -------------------------------------------------------------------------------- /public/icons/logo-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-144.png -------------------------------------------------------------------------------- /public/icons/logo-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-152.png -------------------------------------------------------------------------------- /public/icons/logo-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-180.png -------------------------------------------------------------------------------- /public/icons/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-192.png -------------------------------------------------------------------------------- /public/icons/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-512.png -------------------------------------------------------------------------------- /public/icons/logo-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-57.png -------------------------------------------------------------------------------- /public/icons/logo-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-60.png -------------------------------------------------------------------------------- /public/icons/logo-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-72.png -------------------------------------------------------------------------------- /public/icons/logo-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/icons/logo-76.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PlainApp", 3 | "short_name": "PlainApp", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "icons": [ 7 | { 8 | "sizes": "114x114", 9 | "src": "/icons/logo-114.png" 10 | }, 11 | { 12 | "sizes": "120x120", 13 | "src": "/icons/logo-120.png" 14 | }, 15 | { 16 | "sizes": "144x144", 17 | "src": "/icons/logo-144.png" 18 | }, 19 | { 20 | "sizes": "152x152", 21 | "src": "/icons/logo-152.png" 22 | }, 23 | { 24 | "sizes": "180x180", 25 | "src": "/icons/logo-180.png" 26 | }, 27 | { 28 | "sizes": "57x57", 29 | "src": "/icons/logo-57.png" 30 | }, 31 | { 32 | "sizes": "60x60", 33 | "src": "/icons/logo-60.png" 34 | }, 35 | { 36 | "sizes": "72x72", 37 | "src": "/icons/logo-72.png" 38 | }, 39 | { 40 | "sizes": "76x76", 41 | "src": "/icons/logo-76.png" 42 | }, 43 | { 44 | "src": "/icons/logo-192.png", 45 | "type": "image/png", 46 | "sizes": "192x192" 47 | }, 48 | { 49 | "src": "/icons/logo-512.png", 50 | "type": "image/png", 51 | "sizes": "512x512" 52 | } 53 | ], 54 | "prefer_related_applications": true, 55 | "related_applications": [ 56 | { 57 | "platform": "play", 58 | "url": "https://play.google.com/store/apps/details?id=com.ismartcoding.plain", 59 | "id": "com.ismartcoding.plain" 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/public/sw.js -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/AllCheckedAlert.vue: -------------------------------------------------------------------------------- 1 | 13 | 29 | -------------------------------------------------------------------------------- /src/components/Breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 9 | 24 | 47 | -------------------------------------------------------------------------------- /src/components/BucketFilter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 60 | -------------------------------------------------------------------------------- /src/components/ConfirmModal.vue: -------------------------------------------------------------------------------- 1 | 16 | 28 | -------------------------------------------------------------------------------- /src/components/DeleteFileConfirm.vue: -------------------------------------------------------------------------------- 1 | 14 | 54 | -------------------------------------------------------------------------------- /src/components/DeleteItemsConfirm.vue: -------------------------------------------------------------------------------- 1 | 12 | 44 | -------------------------------------------------------------------------------- /src/components/EditToolbar.vue: -------------------------------------------------------------------------------- 1 | 15 | 33 | -------------------------------------------------------------------------------- /src/components/FeatureButton.vue: -------------------------------------------------------------------------------- 1 | 10 | 25 | -------------------------------------------------------------------------------- /src/components/FieldId.vue: -------------------------------------------------------------------------------- 1 | 9 | 15 | -------------------------------------------------------------------------------- /src/components/IconButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 13 | -------------------------------------------------------------------------------- /src/components/ImageVideoListSkeleton.vue: -------------------------------------------------------------------------------- 1 | 26 | 31 | -------------------------------------------------------------------------------- /src/components/ItemTags.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 40 | 41 | 50 | -------------------------------------------------------------------------------- /src/components/LeftSidebar.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 49 | -------------------------------------------------------------------------------- /src/components/PromptModal.vue: -------------------------------------------------------------------------------- 1 | 17 | 52 | 57 | -------------------------------------------------------------------------------- /src/components/ThemeChanger.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 35 | 36 | 41 | -------------------------------------------------------------------------------- /src/components/buttons/FilledButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/buttons/OutlinedButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/buttons/index.ts: -------------------------------------------------------------------------------- 1 | import FilledButton from './FilledButton.vue' 2 | import OutlinedButton from './OutlinedButton.vue' 3 | 4 | export { FilledButton, OutlinedButton } -------------------------------------------------------------------------------- /src/components/chat/ChatImages.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 63 | -------------------------------------------------------------------------------- /src/components/contextmenu/ContextMenuDefine.ts: -------------------------------------------------------------------------------- 1 | export const MenuConstOptions = { 2 | defaultMinWidth: 100, 3 | defaultMaxWidth: 600, 4 | defaultStartZindex: 1, 5 | } 6 | 7 | export interface MenuOptions { 8 | items: MenuItem[] 9 | x: number 10 | y: number 11 | xOffset?: number 12 | yOffset?: number 13 | zIndex?: number 14 | customClass?: string 15 | iconFontClass?: string 16 | maxWidth?: number 17 | minWidth?: number 18 | } 19 | export interface MenuItem { 20 | label?: string 21 | icon?: string 22 | disabled?: boolean 23 | divided?: boolean 24 | customClass?: string 25 | maxWidth?: number 26 | minWidth?: number 27 | onClick?: () => void 28 | children?: MenuItem[] 29 | } 30 | 31 | export interface ContextMenuPositionData { 32 | x: number 33 | y: number 34 | } 35 | export interface ContextMenuGlobalData { 36 | parentPosition: ContextMenuPositionData 37 | screenSize: { 38 | w: number 39 | h: number 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/contextmenu/index.ts: -------------------------------------------------------------------------------- 1 | import { h, render } from 'vue' 2 | import type { MenuOptions } from './ContextMenuDefine' 3 | import ContextMenuConstructor from './ContextMenu.vue' 4 | 5 | export const contextmenu = (options: MenuOptions) => { 6 | const container = document.createElement('div') 7 | const vnode = h(ContextMenuConstructor, { 8 | options: options, 9 | show: true, 10 | onClose: () => { 11 | render(null, container) 12 | }, 13 | }) 14 | render(vnode, container) 15 | document.body.appendChild(container.firstElementChild as Node) 16 | return vnode.component 17 | } 18 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-boolean.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-date.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-function.vue: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-number.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-regexp.vue: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-string.vue: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /src/components/jsonviewer/types/json-undefined.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /src/components/jsonviewer/utils.js: -------------------------------------------------------------------------------- 1 | export const debounce = function (func, wait) { 2 | let startTime = Date.now() 3 | let timer 4 | 5 | return (...args) => { 6 | if (Date.now() - startTime < wait && timer) { 7 | clearTimeout(timer) 8 | } 9 | timer = setTimeout(() => { 10 | func(...args) 11 | }, wait) 12 | startTime = Date.now() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/lightbox/types.ts: -------------------------------------------------------------------------------- 1 | import type { DataType } from '@/lib/data' 2 | 3 | export interface IImgState { 4 | width: number 5 | height: number 6 | naturalWidth: number 7 | naturalHeight: number 8 | maxScale: number 9 | } 10 | 11 | export interface IImgWrapperState { 12 | scale: number 13 | lastScale: number 14 | rotateDeg: number 15 | top: number 16 | left: number 17 | initX: number 18 | initY: number 19 | lastX: number 20 | lastY: number 21 | touches: TouchList | [] 22 | } 23 | 24 | export interface IStatus { 25 | loadError: boolean 26 | loading: boolean 27 | dragging: boolean 28 | gesturing: boolean 29 | swipeToLeft: boolean 30 | swipeToRight: boolean 31 | } 32 | 33 | export interface ISource { 34 | src: string 35 | viewOriginImage?: boolean 36 | path: string // file path 37 | name: string 38 | size: number 39 | duration: number 40 | type?: DataType 41 | fileId?: string 42 | extension?: string 43 | thumbnail?: string // video thumbnail 44 | summary?: string // text file summary 45 | data?: any // video, audio, image item 46 | } 47 | 48 | export type MouseEventHandler = (e: MouseEvent) => void 49 | export type TouchEventHandler = (e: TouchEvent) => void 50 | 51 | export type IndexChangeAction = 'on-prev' | 'on-next' | 'on-prev-click' | 'on-next-click' 52 | export type IndexChangeActions = IndexChangeAction | IndexChangeAction[] 53 | -------------------------------------------------------------------------------- /src/components/lightbox/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const voidFn = () => { 2 | return 3 | } 4 | 5 | // TODO: prepare for mobile touch event 6 | export let supportsPassive = false 7 | 8 | try { 9 | const options = {} 10 | Object.defineProperty(options, 'passive', { 11 | get() { 12 | supportsPassive = true 13 | }, 14 | }) 15 | window.addEventListener('test-passive', voidFn, options) 16 | } catch (e) { 17 | voidFn() 18 | } 19 | 20 | export const on = (target: Element | Document | Window, event: string, handler: EventListenerOrEventListenerObject, passive = false) => { 21 | target.addEventListener(event, handler, supportsPassive ? { capture: false, passive } : false) 22 | } 23 | 24 | export const off = (target: Element | Document | Window, event: string, handler: EventListenerOrEventListenerObject) => { 25 | target.removeEventListener(event, handler) 26 | } 27 | 28 | export const preventDefault = (e: Event) => { 29 | e.preventDefault() 30 | } 31 | 32 | const toString = Object.prototype.toString 33 | const isType = (type: string) => (arg: unknown) => toString.call(arg).slice(8, -1) === type 34 | 35 | export function isArray(arg: unknown): arg is unknown[] { 36 | return isType('Array')(arg) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/modal/ModalContainer.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/components/modal/ModalContainerItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 37 | -------------------------------------------------------------------------------- /src/components/modal/index.ts: -------------------------------------------------------------------------------- 1 | import { modalQueue } from './utils/state' 2 | import onBeforeModalClose from './onBeforeModalClose' 3 | import Modal from './utils/Modal' 4 | import useModalRouter from './router' 5 | import { closeModal, popModal, pushModal, openModal, promptModal, getCurrentModal, closeById } from './methods' 6 | export { Modal, closeModal, popModal, pushModal, openModal, promptModal, modalQueue, onBeforeModalClose, useModalRouter, getCurrentModal, closeById } 7 | -------------------------------------------------------------------------------- /src/components/modal/onBeforeModalClose.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from 'vue' 2 | import guards from './utils/guards' 3 | import type { GuardFunction } from './utils/types' 4 | 5 | export default function onBeforeModalClose(callback: GuardFunction) { 6 | const a = getCurrentInstance() 7 | const attrModalId = String(a?.props?.modalId || a?.props?.['modal-id'] || a?.attrs?.modalId) 8 | 9 | const modalId = attrModalId.replace(/[^0-9]/g, '') 10 | 11 | guards.add(Number(modalId), 'close', callback) 12 | } 13 | -------------------------------------------------------------------------------- /src/components/modal/utils/config.ts: -------------------------------------------------------------------------------- 1 | export const configuration = { 2 | backgroundClose: true, // Closing on click back area of modal. 3 | escClose: true, // Closing on press ESC key 4 | } 5 | -------------------------------------------------------------------------------- /src/components/modal/utils/dto-modal-options.ts: -------------------------------------------------------------------------------- 1 | import type { ModalOptions } from './Modal' 2 | import { configuration } from './config' 3 | 4 | export default function DtoModalOptions(options: Partial): ModalOptions { 5 | const output: ModalOptions = { 6 | backgroundClose: configuration.backgroundClose, 7 | isRoute: false, 8 | } 9 | 10 | if (options.backgroundClose !== undefined) output.backgroundClose = options.backgroundClose 11 | 12 | if (options.isRoute) output.isRoute = options.isRoute 13 | 14 | return output 15 | } 16 | -------------------------------------------------------------------------------- /src/components/modal/utils/event-close.ts: -------------------------------------------------------------------------------- 1 | export interface IEventClose { 2 | background: boolean 3 | esc: boolean 4 | } 5 | 6 | export function DtoEventClose(obj: Partial = {}): IEventClose { 7 | const defaultValues: IEventClose = { 8 | background: false, 9 | esc: false, 10 | } 11 | 12 | return Object.assign(defaultValues, obj) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/modal/utils/initialize.ts: -------------------------------------------------------------------------------- 1 | import { state } from './state' 2 | import { configuration } from './config' 3 | import { closeById, getCurrentModal } from '../methods' 4 | 5 | export default function initialize() { 6 | state.initialized = true 7 | 8 | document.addEventListener('keyup', (e) => { 9 | // Closing the last modal window when user pressed Escape 10 | if (configuration.escClose && (e.key === 'Escape' || e.code === 'Escape')) { 11 | const modal = getCurrentModal() 12 | if (!modal) return 13 | closeById(modal.id, { esc: true }) 14 | } 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/modal/utils/instances.ts: -------------------------------------------------------------------------------- 1 | import { state } from './state' 2 | import type { ModalComponentInterface } from './types' 3 | 4 | export function saveInstance(id: number, instance: ModalComponentInterface) { 5 | state.instanceStorage[id] = instance 6 | } 7 | export function getInstance(id: number) { 8 | return state.instanceStorage[id] 9 | } 10 | -------------------------------------------------------------------------------- /src/components/modal/utils/state.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import type Modal from './Modal' 3 | import type { ModalComponentInterface } from './types' 4 | 5 | const modalQueue = ref([]) //All modals that showing now 6 | 7 | interface InstancesStorageInterface { 8 | [index: number]: ModalComponentInterface 9 | } 10 | 11 | interface StateInterface { 12 | initialized: boolean 13 | instanceStorage: InstancesStorageInterface 14 | } 15 | 16 | const state: StateInterface = { 17 | initialized: false, 18 | instanceStorage: {}, 19 | } 20 | 21 | export { modalQueue, state } 22 | -------------------------------------------------------------------------------- /src/components/modal/utils/types.ts: -------------------------------------------------------------------------------- 1 | import type { IEventClose } from './event-close' 2 | 3 | export type GuardFunction = (e: IEventClose) => void | boolean | Promise 4 | export type GuardFunctionPromisify = () => Promise 5 | 6 | export interface ModalComponentInterface { 7 | [name: string]: any 8 | } 9 | -------------------------------------------------------------------------------- /src/components/popper/Arrow.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 53 | -------------------------------------------------------------------------------- /src/components/popper/use-content.ts: -------------------------------------------------------------------------------- 1 | import { ref, onMounted, onBeforeUnmount, watch } from 'vue' 2 | 3 | export default function useContent(slots: { content: undefined }, popperNode: { value: Node }, content: { value: any }) { 4 | let observer: MutationObserver | null = null 5 | const hasContent = ref(false) 6 | 7 | onMounted(() => { 8 | if (slots.content !== undefined || content.value) { 9 | hasContent.value = true 10 | } 11 | 12 | observer = new MutationObserver(checkContent) 13 | observer.observe(popperNode.value, { 14 | childList: true, 15 | subtree: true, 16 | }) 17 | }) 18 | 19 | onBeforeUnmount(() => observer?.disconnect()) 20 | 21 | watch(content, (content) => { 22 | if (content) { 23 | hasContent.value = true 24 | } else { 25 | hasContent.value = false 26 | } 27 | }) 28 | 29 | const checkContent = () => { 30 | if (slots.content) { 31 | hasContent.value = true 32 | } else { 33 | hasContent.value = false 34 | } 35 | } 36 | 37 | return { 38 | hasContent, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/components/toaster/PToaster.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 56 | -------------------------------------------------------------------------------- /src/components/toaster/index.ts: -------------------------------------------------------------------------------- 1 | import Toaster from './PToaster.vue' 2 | import { render, h } from 'vue' 3 | 4 | export default (message: string, type = '') => { 5 | render(h(Toaster, { message, type }), document.createElement('div')) 6 | } 7 | -------------------------------------------------------------------------------- /src/components/toaster/timer.ts: -------------------------------------------------------------------------------- 1 | export default class Timer { 2 | startedAt: number 3 | callback: TimerHandler 4 | delay: number 5 | timer: number 6 | constructor(callback: TimerHandler, delay: number) { 7 | this.startedAt = Date.now() 8 | this.callback = callback 9 | this.delay = delay 10 | 11 | this.timer = setTimeout(callback, delay) 12 | } 13 | 14 | pause() { 15 | this.stop() 16 | this.delay -= Date.now() - this.startedAt 17 | } 18 | 19 | resume() { 20 | this.stop() 21 | this.startedAt = Date.now() 22 | this.timer = setTimeout(this.callback, this.delay) 23 | } 24 | 25 | stop() { 26 | clearTimeout(this.timer) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/virtualscroll/index.ts: -------------------------------------------------------------------------------- 1 | import VirtualList from './virtual-list' 2 | 3 | export default VirtualList 4 | -------------------------------------------------------------------------------- /src/components/virtualscroll/props.ts: -------------------------------------------------------------------------------- 1 | import type { PropType } from 'vue' 2 | 3 | export const VirtualProps = { 4 | dataKey: { 5 | type: [String, Function], 6 | required: true, 7 | }, 8 | dataSources: { 9 | type: Array, 10 | required: true, 11 | default: () => [], 12 | }, 13 | keeps: { 14 | type: Number, 15 | default: 100, 16 | }, 17 | estimateSize: { 18 | type: Number, 19 | default: 50, 20 | }, 21 | 22 | direction: { 23 | type: String as PropType<'vertical' | 'horizontal'>, 24 | default: 'vertical', // the other value is horizontal 25 | }, 26 | start: { 27 | type: Number, 28 | default: 0, 29 | }, 30 | offset: { 31 | type: Number, 32 | default: 0, 33 | }, 34 | topThreshold: { 35 | type: Number, 36 | default: 0, 37 | }, 38 | bottomThreshold: { 39 | type: Number, 40 | default: 0, 41 | }, 42 | pageMode: { 43 | type: Boolean, 44 | default: false, 45 | }, 46 | } 47 | 48 | export const ItemProps = { 49 | index: { 50 | type: Number, 51 | }, 52 | event: { 53 | type: String, 54 | }, 55 | horizontal: { 56 | type: Boolean, 57 | }, 58 | source: { 59 | type: Object, 60 | }, 61 | component: { 62 | type: Function, 63 | }, 64 | uniqueKey: { 65 | type: [String, Number], 66 | }, 67 | } 68 | 69 | export const SlotProps = { 70 | event: { 71 | type: String, 72 | }, 73 | uniqueKey: { 74 | type: String, 75 | }, 76 | horizontal: { 77 | type: Boolean, 78 | }, 79 | } 80 | -------------------------------------------------------------------------------- /src/hooks/feed-entries.ts: -------------------------------------------------------------------------------- 1 | import { feedEntriesGQL, initLazyQuery } from '@/lib/api/query' 2 | import type { IFeedEntry } from '@/lib/interfaces' 3 | import { ref, type Ref } from 'vue' 4 | import toast from '@/components/toaster' 5 | import { useI18n } from 'vue-i18n' 6 | 7 | export const useList = (items: Ref, q: Ref, total: Ref) => { 8 | const { t } = useI18n() 9 | 10 | const page = ref(1) 11 | const limit = 100 12 | const noMore = ref(false) 13 | const { loading, fetch } = initLazyQuery({ 14 | handle: (data: { items: IFeedEntry[]; total: number }, error: string) => { 15 | if (error) { 16 | toast(t(error), 'error') 17 | } else { 18 | if (data) { 19 | if (data.items.length < limit) { 20 | noMore.value = true 21 | } 22 | const newItems = data.items 23 | if (page.value === 1) { 24 | items.value = newItems 25 | } else { 26 | items.value = items.value.concat(newItems) 27 | } 28 | total.value = data.total 29 | } 30 | } 31 | }, 32 | document: feedEntriesGQL, 33 | variables: () => ({ 34 | offset: (page.value - 1) * limit, 35 | limit, 36 | query: q.value, 37 | }), 38 | }) 39 | 40 | return { 41 | page, 42 | noMore, 43 | loading, 44 | fetch, 45 | loadMore: () => { 46 | if (noMore.value || loading.value) { 47 | return 48 | } 49 | page.value++ 50 | }, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/hooks/feeds.ts: -------------------------------------------------------------------------------- 1 | import type { IFeed } from '@/lib/interfaces' 2 | import { buildQuery } from '@/lib/search' 3 | import { encodeBase64 } from '@/lib/strutil' 4 | import { replacePath } from '@/plugins/router' 5 | 6 | export const useFeeds = (mainStore: any) => { 7 | return { 8 | viewAll: () => { 9 | replacePath(mainStore, '/feeds') 10 | }, 11 | viewFeed: (item: IFeed) => { 12 | const q = buildQuery([ 13 | { 14 | name: 'feed_id', 15 | op: '', 16 | value: item.id, 17 | }, 18 | ]) 19 | replacePath(mainStore, `/feeds?q=${encodeBase64(q)}`) 20 | }, 21 | viewToday: () => { 22 | const q = buildQuery([ 23 | { 24 | name: 'today', 25 | op: '', 26 | value: 'true', 27 | }, 28 | ]) 29 | replacePath(mainStore, `/feeds?q=${encodeBase64(q)}`) 30 | }, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/hooks/media-trash.ts: -------------------------------------------------------------------------------- 1 | import { initMutation, trashMediaItemsGQL, restoreMediaItemsGQL } from '@/lib/api/mutation' 2 | import type { DataType } from '@/lib/data' 3 | import emitter from '@/plugins/eventbus' 4 | import type { FetchResult } from '@apollo/client' 5 | import { reactive } from 'vue' 6 | 7 | export const useMediaTrash = () => { 8 | const { mutate, onDone: onTrashed } = initMutation({ 9 | document: trashMediaItemsGQL, 10 | }) 11 | 12 | const loading = reactive(new Map()) 13 | 14 | onTrashed((r: FetchResult, Record>) => { 15 | const { type, query } = r.data.trashMediaItems 16 | loading.delete(query) 17 | emitter.emit('refetch_tags', type) 18 | emitter.emit('media_items_actioned', { type, action: 'trash', query }) 19 | }) 20 | 21 | return { 22 | trashLoading(query: string) { 23 | return loading.get(query) ?? false 24 | }, 25 | trash(type: DataType, query: string) { 26 | loading.set(query, true) 27 | mutate({ query, type }) 28 | }, 29 | } 30 | } 31 | 32 | export const useMediaRestore = () => { 33 | const { mutate, onDone: onRestored } = initMutation({ 34 | document: restoreMediaItemsGQL, 35 | }) 36 | 37 | const loading = reactive(new Map()) 38 | 39 | onRestored((r: FetchResult, Record>) => { 40 | const { type, query } = r.data.restoreMediaItems 41 | loading.delete(query) 42 | emitter.emit('refetch_tags', type) 43 | emitter.emit('media_items_actioned', { type, action: 'restore', query }) 44 | }) 45 | 46 | return { 47 | restoreLoading(query: string) { 48 | return loading.get(query) ?? false 49 | }, 50 | restore(type: DataType, query: string) { 51 | loading.set(query, true) 52 | mutate({ query, type }) 53 | }, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/hooks/notes.ts: -------------------------------------------------------------------------------- 1 | import { initMutation, restoreNotesGQL, trashNotesGQL } from '@/lib/api/mutation' 2 | import { DataType } from '@/lib/data' 3 | import emitter from '@/plugins/eventbus' 4 | import type { FetchResult } from '@apollo/client' 5 | import { reactive } from 'vue' 6 | 7 | export const useNotesTrash = (clearSelection: () => void, fetch: () => void) => { 8 | const { mutate, onDone: onTrashed } = initMutation({ 9 | document: trashNotesGQL, 10 | }) 11 | 12 | const loading = reactive(new Map()) 13 | 14 | onTrashed((r: FetchResult, Record>) => { 15 | loading.delete(r.data.trashNotes) 16 | clearSelection() 17 | fetch() 18 | emitter.emit('refetch_tags', DataType.NOTE) 19 | emitter.emit('notes_actioned', { action: 'trash' }) 20 | }) 21 | 22 | return { 23 | trashLoading(query: string) { 24 | return loading.get(query) ?? false 25 | }, 26 | trash(query: string) { 27 | loading.set(query, true) 28 | mutate({ query }) 29 | }, 30 | } 31 | } 32 | 33 | export const useNotesRestore = (clearSelection: () => void, fetch: () => void) => { 34 | const { mutate, onDone: onRestored } = initMutation({ 35 | document: restoreNotesGQL, 36 | }) 37 | 38 | const loading = reactive(new Map()) 39 | 40 | onRestored((r: FetchResult, Record>) => { 41 | loading.delete(r.data.restoreNotes) 42 | clearSelection() 43 | fetch() 44 | emitter.emit('refetch_tags', DataType.NOTE) 45 | emitter.emit('notes_actioned', { action: 'restore' }) 46 | }) 47 | 48 | return { 49 | restoreLoading(query: string) { 50 | return loading.get(query) ?? false 51 | }, 52 | restore(query: string) { 53 | loading.set(query, true) 54 | mutate({ query }) 55 | }, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/hooks/sidebar.ts: -------------------------------------------------------------------------------- 1 | export const useLeftSidebarResize = (minWidth: number, getWidth: () => number, setWidth: (width: number) => void) => { 2 | return { 3 | resizeWidth: (e: MouseEvent) => { 4 | const startX = e.clientX 5 | const startWidth = getWidth() 6 | const appElement = document.getElementById('app') 7 | if (appElement) { 8 | appElement.style.userSelect = 'none' 9 | } 10 | const move = (e: MouseEvent) => { 11 | let width = startWidth + (e.clientX - startX) 12 | if (width < minWidth) { 13 | width = minWidth 14 | } 15 | setWidth(width) 16 | } 17 | const up = () => { 18 | appElement?.style.removeProperty('user-select') 19 | window.removeEventListener('mousemove', move) 20 | window.removeEventListener('mouseup', up) 21 | } 22 | window.addEventListener('mousemove', move) 23 | window.addEventListener('mouseup', up) 24 | }, 25 | } 26 | } 27 | 28 | export const useRightSidebarResize = (minWidth: number, getWidth: () => number, setWidth: (width: number) => void) => { 29 | return { 30 | resizeWidth: (e: MouseEvent) => { 31 | const startX = e.clientX 32 | const startWidth = getWidth() 33 | const appElement = document.getElementById('app') 34 | if (appElement) { 35 | appElement.style.userSelect = 'none' 36 | } 37 | const move = (e: MouseEvent) => { 38 | let width = startWidth + startX - e.clientX 39 | if (width < minWidth) { 40 | width = minWidth 41 | } 42 | setWidth(width) 43 | } 44 | const up = () => { 45 | appElement?.style.removeProperty('user-select') 46 | window.removeEventListener('mousemove', move) 47 | window.removeEventListener('mouseup', up) 48 | } 49 | window.addEventListener('mousemove', move) 50 | window.addEventListener('mouseup', up) 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/hooks/tags.ts: -------------------------------------------------------------------------------- 1 | import type { ITag } from '@/lib/interfaces' 2 | import { ref, type Ref } from 'vue' 3 | import { useI18n } from 'vue-i18n' 4 | import toast from '@/components/toaster' 5 | import { openModal } from '@/components/modal' 6 | import AddToTagsModal from '@/components/AddToTagsModal.vue' 7 | import { initLazyQuery, tagsGQL } from '@/lib/api/query' 8 | 9 | export const useAddToTags = (type: string, tags: Ref) => { 10 | const { t } = useI18n() 11 | 12 | return { 13 | addToTags: (ids: string[], realAllChecked: boolean, query: string) => { 14 | let q = query 15 | if (!realAllChecked) { 16 | if (ids.length === 0) { 17 | toast(t('select_first'), 'error') 18 | return 19 | } 20 | q = `ids:${ids.join(',')}` 21 | } 22 | 23 | openModal(AddToTagsModal, { 24 | type, 25 | tags: tags.value, 26 | query: q, 27 | }) 28 | }, 29 | } 30 | } 31 | 32 | export const useTags = (type: string, onLoad: () => void = () => {}) => { 33 | const tags = ref([]) 34 | const { t } = useI18n() 35 | 36 | const { loading, fetch } = initLazyQuery({ 37 | handle: async (data: any, error: string) => { 38 | if (error) { 39 | toast(t(error), 'error') 40 | } else { 41 | if (data) { 42 | tags.value = data.tags 43 | onLoad() 44 | } 45 | } 46 | }, 47 | document: tagsGQL, 48 | variables: { 49 | type: type, 50 | }, 51 | }) 52 | return { 53 | tags, 54 | loading, 55 | fetch, 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/hooks/text-selection.ts: -------------------------------------------------------------------------------- 1 | export function fixUserSelect(event: MouseEvent) { 2 | if (event.detail > 1) { 3 | event.preventDefault() 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/agent/agent.ts: -------------------------------------------------------------------------------- 1 | import type { AgentInfo } from './types' 2 | import { hasUserAgentData } from './utils' 3 | import { getClientHintsAgent } from './userAgentData' 4 | import { getLegacyAgent } from './userAgent' 5 | 6 | export async function getAccurateAgent(): Promise { 7 | if (hasUserAgentData()) { 8 | const info = await navigator.userAgentData.getHighEntropyValues(['architecture', 'model', 'platform', 'platformVersion', 'uaFullVersion', 'fullVersionList']) 9 | return getClientHintsAgent(info) 10 | } 11 | 12 | return agent() 13 | } 14 | 15 | function agent(userAgent?: string): AgentInfo { 16 | if (typeof userAgent === 'undefined' && hasUserAgentData()) { 17 | return getClientHintsAgent() 18 | } else { 19 | return getLegacyAgent(userAgent) 20 | } 21 | } 22 | export { getLegacyAgent } 23 | 24 | export default agent 25 | 26 | export * from './types' 27 | -------------------------------------------------------------------------------- /src/lib/agent/types.ts: -------------------------------------------------------------------------------- 1 | export interface AgentVersionInfo { 2 | name: string 3 | version: string 4 | majorVersion: number 5 | } 6 | export type AgentOSInfo = AgentVersionInfo 7 | 8 | export interface AgentBrowserInfo extends AgentVersionInfo { 9 | webkit: boolean 10 | webkitVersion: string 11 | chromium: boolean 12 | chromiumVersion: string 13 | webview: boolean 14 | } 15 | 16 | export interface AgentInfo { 17 | browser: AgentBrowserInfo 18 | os: AgentOSInfo 19 | isMobile: boolean 20 | isHints: boolean 21 | } 22 | 23 | export interface PresetInfo { 24 | test: string 25 | id: string 26 | brand?: boolean 27 | versionTest?: string 28 | versionAlias?: string 29 | } 30 | export interface PresetResult { 31 | preset: PresetInfo | null 32 | version: string 33 | } 34 | 35 | export interface NavigatorUABrandVersion { 36 | brand: string 37 | version: string 38 | } 39 | 40 | export interface UADataValues { 41 | platform: string 42 | platformVersion: string 43 | architecture: string 44 | model: string 45 | uaFullVersion: string 46 | fullVersionList: NavigatorUABrandVersion[] 47 | } 48 | export interface NavigatorUAData { 49 | brands?: NavigatorUABrandVersion[] 50 | mobile: boolean 51 | platform: string 52 | getHighEntropyValues( 53 | hints: readonly T[] 54 | ): Promise<{ 55 | [key in T]: UADataValues[key] 56 | }> 57 | } 58 | -------------------------------------------------------------------------------- /src/lib/api/api.ts: -------------------------------------------------------------------------------- 1 | export function getApiHost() { 2 | return import.meta.env.VITE_APP_API_HOST || window.location.host 3 | } 4 | 5 | export function getApiHeaders() { 6 | return { 7 | 'Content-Type': 'multipart/form-data', 8 | 'c-id': localStorage.getItem('client_id') ?? '', 9 | } 10 | } 11 | 12 | export function getWebSocketBaseUrl() { 13 | const p = window.location.protocol === 'http:' ? 'ws' : 'wss' 14 | return `${p}://${getApiHost()}` 15 | } 16 | 17 | export function getApiBaseUrl() { 18 | return `${window.location.protocol}\/\/${getApiHost()}` 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/api/crypto.ts: -------------------------------------------------------------------------------- 1 | import { stringToArrayBuffer } from '../strutil' 2 | import * as sjcl from 'sjcl' 3 | import _ from './sha512' 4 | import { arrayBufferFromBits, arrayBuffertoBits } from './sjcl-arraybuffer' 5 | 6 | export function sha512(input: string): string { 7 | const hashBits = sjcl.hash.sha512.hash(input) 8 | return sjcl.codec.hex.fromBits(hashBits) 9 | } 10 | 11 | export function hashToKey(hash: string): sjcl.BitArray { 12 | return arrayBuffertoBits(stringToArrayBuffer(hash.substring(0, 32)).buffer) 13 | } 14 | 15 | export function aesEncrypt(key: sjcl.BitArray, plaintext: string): sjcl.BitArray { 16 | const nonce = sjcl.random.randomWords(3) 17 | 18 | const cipher = new sjcl.cipher.aes(key) 19 | const encrypted = sjcl.mode.gcm.encrypt(cipher, sjcl.codec.utf8String.toBits(plaintext), nonce) 20 | return sjcl.bitArray.concat(nonce, encrypted) 21 | } 22 | 23 | export function aesDecrypt(key: sjcl.BitArray, data: sjcl.BitArray): string { 24 | const nonce = sjcl.bitArray.bitSlice(data, 0, 96) 25 | const ciphertext = sjcl.bitArray.bitSlice(data, 96, sjcl.bitArray.bitLength(data)) 26 | const cipher = new sjcl.cipher.aes(key) 27 | const decrypted = sjcl.mode.gcm.decrypt(cipher, ciphertext, nonce) 28 | 29 | return sjcl.codec.utf8String.fromBits(decrypted) 30 | } 31 | 32 | export function arrayBufferToBitArray(buffer: ArrayBuffer): sjcl.BitArray { 33 | const uint8Array = new Uint8Array(buffer) 34 | return arrayBuffertoBits(uint8Array.buffer) 35 | } 36 | 37 | export function bitArrayToUint8Array(bitArray: sjcl.BitArray): Uint8Array { 38 | const arrayBuffer = arrayBufferFromBits(bitArray) 39 | return new Uint8Array(arrayBuffer) 40 | } 41 | 42 | export function bitArrayToBase64(bitArray: sjcl.BitArray): string { 43 | return sjcl.codec.base64.fromBits(bitArray) 44 | } 45 | -------------------------------------------------------------------------------- /src/lib/array.ts: -------------------------------------------------------------------------------- 1 | export function deleteById(items: [{ id: string }], id: string) { 2 | const index = items.findIndex((it: { id: string }) => it.id === id) 3 | if (index !== -1) { 4 | items.splice(index, 1) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/contact/contact.ts: -------------------------------------------------------------------------------- 1 | function getTypes(length: number) { 2 | return Array.from({ length }, (_, i) => i + 1).concat(-1) 3 | } 4 | 5 | export const types = { 6 | phoneNumberTypes: getTypes(20), 7 | emailTypes: getTypes(4), 8 | addressTypes: getTypes(3), 9 | eventTypes: getTypes(3), 10 | imTypes: getTypes(8), 11 | websiteTypes: getTypes(7), 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/data.ts: -------------------------------------------------------------------------------- 1 | export enum DataType { 2 | AUDIO = 'AUDIO', 3 | VIDEO = 'VIDEO', 4 | IMAGE = 'IMAGE', 5 | PACKAGE = 'PACKAGE', 6 | AI_CHAT = 'AI_CHAT', 7 | CALL = 'CALL', 8 | CONTACT = 'CONTACT', 9 | SMS = 'SMS', 10 | NOTE = 'NOTE', 11 | BOOK = 'BOOK', 12 | FEED_ENTRY = 'FEED_ENTRY', 13 | } 14 | 15 | export enum FEATURE { 16 | MEDIA_TRASH, 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/effect.ts: -------------------------------------------------------------------------------- 1 | export function transferEffect(source: any, target: any, duration = 500) { 2 | const clone = source.cloneNode(true) 3 | 4 | const sourceRect = source.getBoundingClientRect() 5 | const targetRect = target.getBoundingClientRect() 6 | 7 | clone.style.position = 'absolute' 8 | clone.style.top = sourceRect.top + 'px' 9 | clone.style.left = sourceRect.left + 'px' 10 | clone.style.opacity = 1 11 | 12 | document.body.appendChild(clone) 13 | 14 | let startTime = 0 15 | 16 | function animate(currentTime: number) { 17 | if (!startTime) { 18 | startTime = currentTime 19 | } 20 | 21 | const elapsed = currentTime - startTime 22 | const progress = Math.min(elapsed / duration, 1) 23 | 24 | clone.style.top = sourceRect.top + (targetRect.top - sourceRect.top) * progress + 'px' 25 | clone.style.left = sourceRect.left + (targetRect.left - sourceRect.left) * progress + 'px' 26 | 27 | if (progress < 1) { 28 | requestAnimationFrame(animate) 29 | } else { 30 | document.body.removeChild(clone) 31 | } 32 | } 33 | 34 | requestAnimationFrame(animate) 35 | } 36 | -------------------------------------------------------------------------------- /src/lib/feature.ts: -------------------------------------------------------------------------------- 1 | import { FEATURE } from '@/lib/data' 2 | import { isRPlus } from '@/lib/sdk-version' 3 | 4 | export const hasFeature = (feature: FEATURE, osVersion: number) => { 5 | if (feature === FEATURE.MEDIA_TRASH) { 6 | return isRPlus(osVersion) 7 | } 8 | 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/list.ts: -------------------------------------------------------------------------------- 1 | export function noDataKey(loading: boolean, permissions: string[] = [], permission = ''): string { 2 | if (loading) { 3 | return 'loading' 4 | } 5 | 6 | if (permission && !permissions.includes(permission)) { 7 | return 'no_permission' 8 | } 9 | 10 | return 'no_data' 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/sdk-version.ts: -------------------------------------------------------------------------------- 1 | const VERSION_CODES = { 2 | Q: 29, // Android 10 3 | R: 30, // Android 11 4 | S: 31, // Android 12 5 | S_V2: 32, // Android 12L 6 | TIRAMISU: 33, // Android 13 7 | UPSIDE_DOWN_CAKE: 34, // Android 14 8 | } 9 | 10 | // Check if the SDK version is at least the specified version 11 | export function isQPlus(version: number) { 12 | return version >= VERSION_CODES.Q 13 | } 14 | 15 | export function isRPlus(version: number) { 16 | return version >= VERSION_CODES.R 17 | } 18 | 19 | export function isSPlus(version: number) { 20 | return version >= VERSION_CODES.S 21 | } 22 | 23 | export function isSV2Plus(version: number) { 24 | return version >= VERSION_CODES.S_V2 25 | } 26 | 27 | export function isTPlus(version: number) { 28 | return version >= VERSION_CODES.TIRAMISU 29 | } 30 | 31 | export function isUPlus(version: number) { 32 | return version >= VERSION_CODES.UPSIDE_DOWN_CAKE 33 | } 34 | -------------------------------------------------------------------------------- /src/lib/tag.ts: -------------------------------------------------------------------------------- 1 | export const names: Record = { 2 | NOTE: 'notes', 3 | AUDIO: 'audios', 4 | IMAGE: 'images', 5 | VIDEO: 'videos', 6 | FEED_ENTRY: 'feeds', 7 | SMS: 'messages', 8 | CALL: 'calls', 9 | CONTACT: 'contacts', 10 | AI_CHAT: 'aichats', 11 | } 12 | -------------------------------------------------------------------------------- /src/lib/timeago/format.ts: -------------------------------------------------------------------------------- 1 | import { formatDiff, diffSec } from './utils/date' 2 | import { getLocale } from './register' 3 | import type { Opts, TDate } from './interface' 4 | 5 | /** 6 | * format a TDate into string 7 | * @param date 8 | * @param locale 9 | * @param opts 10 | */ 11 | export const format = (date: TDate, locale?: string, opts?: Opts): string => { 12 | // diff seconds 13 | const sec = diffSec(date, opts && opts.relativeDate) 14 | // format it with locale 15 | return formatDiff(sec, getLocale(locale)) 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/timeago/index.ts: -------------------------------------------------------------------------------- 1 | import * as Languages from './lang' 2 | import { register } from './register' 3 | import type { LocaleFunc } from './interface' 4 | 5 | Object.keys(Languages).forEach((locale: string) => { 6 | register(locale, Languages[locale] as LocaleFunc) 7 | }) 8 | 9 | export { format } from './format' 10 | export { render, cancel } from './realtime' 11 | export { register } 12 | export * from './interface' 13 | -------------------------------------------------------------------------------- /src/lib/timeago/interface.ts: -------------------------------------------------------------------------------- 1 | export type LocaleFunc = (diff: number, idx: number, totalSec?: number) => [string, string] 2 | 3 | export type LocaleMap = Record 4 | 5 | export type TDate = Date | string | number 6 | 7 | export type TimerPool = Record 8 | 9 | /** 10 | * render / format options 11 | */ 12 | export type Opts = { 13 | /** the relative date */ 14 | readonly relativeDate?: TDate 15 | /** the realtime */ 16 | readonly minInterval?: number 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ar.ts: -------------------------------------------------------------------------------- 1 | const timeTypes = [ 2 | ['ثانية', 'ثانيتين', '%s ثوان', '%s ثانية'], // Seconds 3 | ['دقيقة', 'دقيقتين', '%s دقائق', '%s دقيقة'], // Minutes 4 | ['ساعة', 'ساعتين', '%s ساعات', '%s ساعة'], // Hours 5 | ['يوم', 'يومين', '%s أيام', '%s يوماً'], // Days 6 | ['أسبوع', 'أسبوعين', '%s أسابيع', '%s أسبوعاً'], // Weeks 7 | ['شهر', 'شهرين', '%s أشهر', '%s شهراً'], // Months 8 | ['عام', 'عامين', '%s أعوام', '%s عاماً'], // Years 9 | ] 10 | 11 | function formatTime(type: number, n: number): string { 12 | if (n < 3) return timeTypes[type][n - 1] 13 | if (n >= 3 && n <= 10) return timeTypes[type][2] 14 | return timeTypes[type][3] 15 | } 16 | 17 | export default function (number: number, index: number): [string, string] { 18 | if (index === 0) { 19 | return ['منذ لحظات', 'بعد لحظات'] 20 | } 21 | 22 | const timeStr = formatTime(Math.floor(index / 2), number) 23 | return ['منذ' + ' ' + timeStr, 'بعد' + ' ' + timeStr] 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/bg.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['току що', 'съвсем скоро'], 4 | ['преди %s секунди', 'след %s секунди'], 5 | ['преди 1 минута', 'след 1 минута'], 6 | ['преди %s минути', 'след %s минути'], 7 | ['преди 1 час', 'след 1 час'], 8 | ['преди %s часа', 'след %s часа'], 9 | ['преди 1 ден', 'след 1 ден'], 10 | ['преди %s дни', 'след %s дни'], 11 | ['преди 1 седмица', 'след 1 седмица'], 12 | ['преди %s седмици', 'след %s седмици'], 13 | ['преди 1 месец', 'след 1 месец'], 14 | ['преди %s месеца', 'след %s месеца'], 15 | ['преди 1 година', 'след 1 година'], 16 | ['преди %s години', 'след %s години'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/bn_IN.ts: -------------------------------------------------------------------------------- 1 | // Bangla (India) 2 | export default function (number: number, index: number): [string, string] { 3 | return [ 4 | ['এইমাত্র', 'একটা সময়'], 5 | ['%s সেকেন্ড আগে', '%s এর সেকেন্ডের মধ্যে'], 6 | ['1 মিনিট আগে', '1 মিনিটে'], 7 | ['%s এর মিনিট আগে', '%s এর মিনিটের মধ্যে'], 8 | ['1 ঘন্টা আগে', '1 ঘন্টা'], 9 | ['%s ঘণ্টা আগে', '%s এর ঘন্টার মধ্যে'], 10 | ['1 দিন আগে', '1 দিনের মধ্যে'], 11 | ['%s এর দিন আগে', '%s এর দিন'], 12 | ['1 সপ্তাহ আগে', '1 সপ্তাহের মধ্যে'], 13 | ['%s এর সপ্তাহ আগে', '%s সপ্তাহের মধ্যে'], 14 | ['1 মাস আগে', '1 মাসে'], 15 | ['%s মাস আগে', '%s মাসে'], 16 | ['1 বছর আগে', '1 বছরের মধ্যে'], 17 | ['%s বছর আগে', '%s বছরে'], 18 | ][index] as [string, string] 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ca.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['fa un moment', "d'aquí un moment"], 4 | ['fa %s segons', "d'aquí %s segons"], 5 | ['fa 1 minut', "d'aquí 1 minut"], 6 | ['fa %s minuts', "d'aquí %s minuts"], 7 | ['fa 1 hora', "d'aquí 1 hora"], 8 | ['fa %s hores', "d'aquí %s hores"], 9 | ['fa 1 dia', "d'aquí 1 dia"], 10 | ['fa %s dies', "d'aquí %s dies"], 11 | ['fa 1 setmana', "d'aquí 1 setmana"], 12 | ['fa %s setmanes', "d'aquí %s setmanes"], 13 | ['fa 1 mes', "d'aquí 1 mes"], 14 | ['fa %s mesos', "d'aquí %s mesos"], 15 | ['fa 1 any', "d'aquí 1 any"], 16 | ['fa %s anys', "d'aquí %s anys"], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/cs.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | let inflectionIndex = 0 3 | 4 | const isInflectionNeeded = index == 1 || index == 3 || index == 5 || index == 7 || index == 9 || index == 11 || index == 13 5 | if (isInflectionNeeded && number >= 5) { 6 | inflectionIndex = 1 7 | } 8 | 9 | return [ 10 | [['právě teď', 'právě teď']], 11 | [ 12 | ['před %s vteřinami', 'za %s vteřiny'], 13 | ['před %s vteřinami', 'za %s vteřin'], 14 | ], 15 | [['před minutou', 'za minutu']], 16 | [ 17 | ['před %s minutami', 'za %s minuty'], 18 | ['před %s minutami', 'za %s minut'], 19 | ], 20 | [['před hodinou', 'za hodinu']], 21 | [ 22 | ['před %s hodinami', 'za %s hodiny'], 23 | ['před %s hodinami', 'za %s hodin'], 24 | ], 25 | [['včera', 'zítra']], 26 | [ 27 | ['před %s dny', 'za %s dny'], 28 | ['před %s dny', 'za %s dnů'], 29 | ], 30 | [['minulý týden', 'příští týden']], 31 | [ 32 | ['před %s týdny', 'za %s týdny'], 33 | ['před %s týdny', 'za %s týdnů'], 34 | ], 35 | [['minulý měsíc', 'přístí měsíc']], 36 | [ 37 | ['před %s měsíci', 'za %s měsíce'], 38 | ['před %s měsíci', 'za %s měsíců'], 39 | ], 40 | [['před rokem', 'přístí rok']], 41 | [ 42 | ['před %s lety', 'za %s roky'], 43 | ['před %s lety', 'za %s let'], 44 | ], 45 | ][index][inflectionIndex] as [string, string] 46 | } 47 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/da.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['for et øjeblik siden', 'om et øjeblik'], 4 | ['for %s sekunder siden', 'om %s sekunder'], 5 | ['for 1 minut siden', 'om 1 minut'], 6 | ['for %s minutter siden', 'om %s minutter'], 7 | ['for 1 time siden', 'om 1 time'], 8 | ['for %s timer siden', 'om %s timer'], 9 | ['for 1 dag siden', 'om 1 dag'], 10 | ['for %s dage siden', 'om %s dage'], 11 | ['for 1 uge siden', 'om 1 uge'], 12 | ['for %s uger siden', 'om %s uger'], 13 | ['for 1 måned siden', 'om 1 måned'], 14 | ['for %s måneder siden', 'om %s måneder'], 15 | ['for 1 år siden', 'om 1 år'], 16 | ['for %s år siden', 'om %s år'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/de.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['gerade eben', 'vor einer Weile'], 4 | ['vor %s Sekunden', 'in %s Sekunden'], 5 | ['vor 1 Minute', 'in 1 Minute'], 6 | ['vor %s Minuten', 'in %s Minuten'], 7 | ['vor 1 Stunde', 'in 1 Stunde'], 8 | ['vor %s Stunden', 'in %s Stunden'], 9 | ['vor 1 Tag', 'in 1 Tag'], 10 | ['vor %s Tagen', 'in %s Tagen'], 11 | ['vor 1 Woche', 'in 1 Woche'], 12 | ['vor %s Wochen', 'in %s Wochen'], 13 | ['vor 1 Monat', 'in 1 Monat'], 14 | ['vor %s Monaten', 'in %s Monaten'], 15 | ['vor 1 Jahr', 'in 1 Jahr'], 16 | ['vor %s Jahren', 'in %s Jahren'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/el.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['μόλις τώρα', 'σε λίγο'], 4 | ['%s δευτερόλεπτα πριν', 'σε %s δευτερόλεπτα'], 5 | ['1 λεπτό πριν', 'σε 1 λεπτό'], 6 | ['%s λεπτά πριν', 'σε %s λεπτά'], 7 | ['1 ώρα πριν', 'σε 1 ώρα'], 8 | ['%s ώρες πριν', 'σε %s ώρες'], 9 | ['1 μέρα πριν', 'σε 1 μέρα'], 10 | ['%s μέρες πριν', 'σε %s μέρες'], 11 | ['1 εβδομάδα πριν', 'σε 1 εβδομάδα'], 12 | ['%s εβδομάδες πριν', 'σε %s εβδομάδες'], 13 | ['1 μήνα πριν', 'σε 1 μήνα'], 14 | ['%s μήνες πριν', 'σε %s μήνες'], 15 | ['1 χρόνο πριν', 'σε 1 χρόνο'], 16 | ['%s χρόνια πριν', 'σε %s χρόνια'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/en_US.ts: -------------------------------------------------------------------------------- 1 | const EN_US = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year'] 2 | 3 | export default function (diff: number, idx: number): [string, string] { 4 | if (idx === 0) return ['just now', 'right now'] 5 | let unit = EN_US[Math.floor(idx / 2)] 6 | if (diff > 1) unit += 's' 7 | return [`${diff} ${unit} ago`, `in ${diff} ${unit}`] 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/en_short.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['just now', 'right now'], 4 | ['%ss ago', 'in %ss'], 5 | ['1m ago', 'in 1m'], 6 | ['%sm ago', 'in %sm'], 7 | ['1h ago', 'in 1h'], 8 | ['%sh ago', 'in %sh'], 9 | ['1d ago', 'in 1d'], 10 | ['%sd ago', 'in %sd'], 11 | ['1w ago', 'in 1w'], 12 | ['%sw ago', 'in %sw'], 13 | ['1mo ago', 'in 1mo'], 14 | ['%smo ago', 'in %smo'], 15 | ['1yr ago', 'in 1yr'], 16 | ['%syr ago', 'in %syr'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/es.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['justo ahora', 'en un rato'], 4 | ['hace %s segundos', 'en %s segundos'], 5 | ['hace 1 minuto', 'en 1 minuto'], 6 | ['hace %s minutos', 'en %s minutos'], 7 | ['hace 1 hora', 'en 1 hora'], 8 | ['hace %s horas', 'en %s horas'], 9 | ['hace 1 día', 'en 1 día'], 10 | ['hace %s días', 'en %s días'], 11 | ['hace 1 semana', 'en 1 semana'], 12 | ['hace %s semanas', 'en %s semanas'], 13 | ['hace 1 mes', 'en 1 mes'], 14 | ['hace %s meses', 'en %s meses'], 15 | ['hace 1 año', 'en 1 año'], 16 | ['hace %s años', 'en %s años'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/eu.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['orain', 'denbora bat barru'], 4 | ['duela %s segundu', '%s segundu barru'], 5 | ['duela minutu 1', 'minutu 1 barru'], 6 | ['duela %s minutu', '%s minutu barru'], 7 | ['duela ordu 1', 'ordu 1 barru'], 8 | ['duela %s ordu', '%s ordu barru'], 9 | ['duela egun 1', 'egun 1 barru'], 10 | ['duela %s egun', '%s egun barru'], 11 | ['duela aste 1', 'aste 1 barru'], 12 | ['duela %s aste', '%s aste barru'], 13 | ['duela hillabete 1', 'hillabete 1 barru'], 14 | ['duela %s hillabete', '%s hillabete barru'], 15 | ['duela urte 1', 'urte 1 barru'], 16 | ['duela %s urte', '%s urte barru'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/fa.ts: -------------------------------------------------------------------------------- 1 | // As persian language has different number symbols we need to replace regular numbers 2 | // to standard persian numbres. 3 | function toPersianNumber(number): string { 4 | // List of standard persian numbers from 0 to 9 5 | const persianDigits = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] 6 | 7 | return number.toString().replace(/\d/g, (x) => persianDigits[x]) 8 | } 9 | 10 | export default function (number: number, index: number): [string, string] { 11 | const formattedString = [ 12 | ['لحظاتی پیش', 'همین حالا'], 13 | ['%s ثانیه پیش', '%s ثانیه دیگر'], 14 | ['۱ دقیقه پیش', '۱ دقیقه دیگر'], 15 | ['%s دقیقه پیش', '%s دقیقه دیگر'], 16 | ['۱ ساعت پیش', '۱ ساعت دیگر'], 17 | ['%s ساعت پیش', '%s ساعت دیگر'], 18 | ['۱ روز پیش', '۱ روز دیگر'], 19 | ['%s روز پیش', '%s روز دیگر'], 20 | ['۱ هفته پیش', '۱ هفته دیگر'], 21 | ['%s هفته پیش', '%s هفته دیگر'], 22 | ['۱ ماه پیش', '۱ ماه دیگر'], 23 | ['%s ماه پیش', '%s ماه دیگر'], 24 | ['۱ سال پیش', '۱ سال دیگر'], 25 | ['%s سال پیش', '%s سال دیگر'], 26 | ][index] 27 | 28 | // We convert regular numbers (%s) to standard persian numbers using toPersianNumber function 29 | return [formattedString[0].replace('%s', toPersianNumber(number)), formattedString[1].replace('%s', toPersianNumber(number))] 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/fi.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['juuri äsken', 'juuri nyt'], 4 | ['%s sekuntia sitten', '%s sekunnin päästä'], 5 | ['minuutti sitten', 'minuutin päästä'], 6 | ['%s minuuttia sitten', '%s minuutin päästä'], 7 | ['tunti sitten', 'tunnin päästä'], 8 | ['%s tuntia sitten', '%s tunnin päästä'], 9 | ['päivä sitten', 'päivän päästä'], 10 | ['%s päivää sitten', '%s päivän päästä'], 11 | ['viikko sitten', 'viikon päästä'], 12 | ['%s viikkoa sitten', '%s viikon päästä'], 13 | ['kuukausi sitten', 'kuukauden päästä'], 14 | ['%s kuukautta sitten', '%s kuukauden päästä'], 15 | ['vuosi sitten', 'vuoden päästä'], 16 | ['%s vuotta sitten', '%s vuoden päästä'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/fr.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ["à l'instant", 'dans un instant'], 4 | ['il y a %s secondes', 'dans %s secondes'], 5 | ['il y a 1 minute', 'dans 1 minute'], 6 | ['il y a %s minutes', 'dans %s minutes'], 7 | ['il y a 1 heure', 'dans 1 heure'], 8 | ['il y a %s heures', 'dans %s heures'], 9 | ['il y a 1 jour', 'dans 1 jour'], 10 | ['il y a %s jours', 'dans %s jours'], 11 | ['il y a 1 semaine', 'dans 1 semaine'], 12 | ['il y a %s semaines', 'dans %s semaines'], 13 | ['il y a 1 mois', 'dans 1 mois'], 14 | ['il y a %s mois', 'dans %s mois'], 15 | ['il y a 1 an', 'dans 1 an'], 16 | ['il y a %s ans', 'dans %s ans'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/gl.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['xusto agora', 'daquí a un pouco'], 4 | ['hai %s segundos', 'en %s segundos'], 5 | ['hai 1 minuto', 'nun minuto'], 6 | ['hai %s minutos', 'en %s minutos'], 7 | ['hai 1 hora', 'nunha hora'], 8 | ['hai %s horas', 'en %s horas'], 9 | ['hai 1 día', 'nun día'], 10 | ['hai %s días', 'en %s días'], 11 | ['hai 1 semana', 'nunha semana'], 12 | ['hai %s semanas', 'en %s semanas'], 13 | ['hai 1 mes', 'nun mes'], 14 | ['hai %s meses', 'en %s meses'], 15 | ['hai 1 ano', 'nun ano'], 16 | ['hai %s anos', 'en %s anos'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/he.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['זה עתה', 'עכשיו'], 4 | ['לפני %s שניות', 'בעוד %s שניות'], 5 | ['לפני דקה', 'בעוד דקה'], 6 | ['לפני %s דקות', 'בעוד %s דקות'], 7 | ['לפני שעה', 'בעוד שעה'], 8 | number === 2 ? ['לפני שעתיים', 'בעוד שעתיים'] : ['לפני %s שעות', 'בעוד %s שעות'], 9 | ['אתמול', 'מחר'], 10 | number === 2 ? ['לפני יומיים', 'בעוד יומיים'] : ['לפני %s ימים', 'בעוד %s ימים'], 11 | ['לפני שבוע', 'בעוד שבוע'], 12 | number === 2 ? ['לפני שבועיים', 'בעוד שבועיים'] : ['לפני %s שבועות', 'בעוד %s שבועות'], 13 | ['לפני חודש', 'בעוד חודש'], 14 | number === 2 ? ['לפני חודשיים', 'בעוד חודשיים'] : ['לפני %s חודשים', 'בעוד %s חודשים'], 15 | ['לפני שנה', 'בעוד שנה'], 16 | number === 2 ? ['לפני שנתיים', 'בעוד שנתיים'] : ['לפני %s שנים', 'בעוד %s שנים'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/hi_IN.ts: -------------------------------------------------------------------------------- 1 | // Hindi (India) 2 | export default function (number: number, index: number): [string, string] { 3 | return [ 4 | ['अभी', 'कुछ समय'], 5 | ['%s सेकंड पहले', '%s सेकंड में'], 6 | ['1 मिनट पहले', '1 मिनट में'], 7 | ['%s मिनट पहले', '%s मिनट में'], 8 | ['1 घंटे पहले', '1 घंटे में'], 9 | ['%s घंटे पहले', '%s घंटे में'], 10 | ['1 दिन पहले', '1 दिन में'], 11 | ['%s दिन पहले', '%s दिनों में'], 12 | ['1 सप्ताह पहले', '1 सप्ताह में'], 13 | ['%s हफ्ते पहले', '%s हफ्तों में'], 14 | ['1 महीने पहले', '1 महीने में'], 15 | ['%s महीने पहले', '%s महीनों में'], 16 | ['1 साल पहले', '1 साल में'], 17 | ['%s साल पहले', '%s साल में'], 18 | ][index] as [string, string] 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/hu.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['éppen most', 'éppen most'], 4 | ['%s másodperce', '%s másodpercen belül'], 5 | ['1 perce', '1 percen belül'], 6 | ['%s perce', '%s percen belül'], 7 | ['1 órája', '1 órán belül'], 8 | ['%s órája', '%s órán belül'], 9 | ['1 napja', '1 napon belül'], 10 | ['%s napja', '%s napon belül'], 11 | ['1 hete', '1 héten belül'], 12 | ['%s hete', '%s héten belül'], 13 | ['1 hónapja', '1 hónapon belül'], 14 | ['%s hónapja', '%s hónapon belül'], 15 | ['1 éve', '1 éven belül'], 16 | ['%s éve', '%s éven belül'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/id_ID.ts: -------------------------------------------------------------------------------- 1 | // Indonesian (Indonesia) 2 | export default function (number: number, index: number): [string, string] { 3 | return [ 4 | ['baru saja', 'sebentar'], 5 | ['%s detik yang lalu', 'dalam %s detik'], 6 | ['1 menit yang lalu', 'dalam 1 menit'], 7 | ['%s menit yang lalu', 'dalam %s menit'], 8 | ['1 jam yang lalu', 'dalam 1 jam'], 9 | ['%s jam yang lalu', 'dalam %s jam'], 10 | ['1 hari yang lalu', 'dalam 1 hari'], 11 | ['%s hari yang lalu', 'dalam %s hari'], 12 | ['1 minggu yang lalu', 'dalam 1 minggu'], 13 | ['%s minggu yang lalu', 'dalam %s minggu'], 14 | ['1 bulan yang lalu', 'dalam 1 bulan'], 15 | ['%s bulan yang lalu', 'dalam %s bulan'], 16 | ['1 tahun yang lalu', 'dalam 1 tahun'], 17 | ['%s tahun yang lalu', 'dalam %s tahun'], 18 | ][index] as [string, string] 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ar } from './ar' 2 | export { default as be } from './be' 3 | export { default as bg } from './bg' 4 | export { default as bn_IN } from './bn_IN' 5 | export { default as ca } from './ca' 6 | export { default as cs } from './cs' 7 | export { default as da } from './da' 8 | export { default as de } from './de' 9 | export { default as el } from './el' 10 | export { default as en_short } from './en_short' 11 | export { default as en_US } from './en_US' 12 | export { default as es } from './es' 13 | export { default as eu } from './eu' 14 | export { default as fa } from './fa' 15 | export { default as fi } from './fi' 16 | export { default as fr } from './fr' 17 | export { default as gl } from './gl' 18 | export { default as he } from './he' 19 | export { default as hi_IN } from './hi_IN' 20 | export { default as hu } from './hu' 21 | export { default as id_ID } from './id_ID' 22 | export { default as it } from './it' 23 | export { default as ja } from './ja' 24 | export { default as ka } from './ka' 25 | export { default as ko } from './ko' 26 | export { default as ml } from './ml' 27 | export { default as my } from './my' 28 | export { default as nb_NO } from './nb_NO' 29 | export { default as nl } from './nl' 30 | export { default as nn_NO } from './nn_NO' 31 | export { default as oc } from './oc' 32 | export { default as pl } from './pl' 33 | export { default as pt_BR } from './pt_BR' 34 | export { default as ro } from './ro' 35 | export { default as ru } from './ru' 36 | export { default as sq } from './sq' 37 | export { default as sr } from './sr' 38 | export { default as sv } from './sv' 39 | export { default as ta } from './ta' 40 | export { default as th } from './th' 41 | export { default as tk } from './tk' 42 | export { default as tr } from './tr' 43 | export { default as uk } from './uk' 44 | export { default as vi } from './vi' 45 | export { default as zh_CN } from './zh_CN' 46 | export { default as zh_TW } from './zh_TW' 47 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/it.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['poco fa', 'fra poco'], 4 | ['%s secondi fa', 'fra %s secondi'], 5 | ['un minuto fa', 'fra un minuto'], 6 | ['%s minuti fa', 'fra %s minuti'], 7 | ["un'ora fa", "fra un'ora"], 8 | ['%s ore fa', 'fra %s ore'], 9 | ['un giorno fa', 'fra un giorno'], 10 | ['%s giorni fa', 'fra %s giorni'], 11 | ['una settimana fa', 'fra una settimana'], 12 | ['%s settimane fa', 'fra %s settimane'], 13 | ['un mese fa', 'fra un mese'], 14 | ['%s mesi fa', 'fra %s mesi'], 15 | ['un anno fa', 'fra un anno'], 16 | ['%s anni fa', 'fra %s anni'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ja.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['すこし前', 'すぐに'], 4 | ['%s秒前', '%s秒以内'], 5 | ['1分前', '1分以内'], 6 | ['%s分前', '%s分以内'], 7 | ['1時間前', '1時間以内'], 8 | ['%s時間前', '%s時間以内'], 9 | ['1日前', '1日以内'], 10 | ['%s日前', '%s日以内'], 11 | ['1週間前', '1週間以内'], 12 | ['%s週間前', '%s週間以内'], 13 | ['1ヶ月前', '1ヶ月以内'], 14 | ['%sヶ月前', '%sヶ月以内'], 15 | ['1年前', '1年以内'], 16 | ['%s年前', '%s年以内'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ka.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['ამ წამს', 'ახლა'], 4 | ['%s წამის წინ', '%s წამში'], 5 | ['1 წუთის წინ', '1 წუთში'], 6 | ['%s წუთის წინ', '%s წუთში'], 7 | ['1 საათის წინ', '1 საათში'], 8 | ['%s საათის წინ', '%s საათში'], 9 | ['1 დღის წინ', '1 დღეში'], 10 | ['%s დღის წინ', '%s დღეში'], 11 | ['1 კვირის წინ', '1 კვირაში'], 12 | ['%s კვირის წინ', '%s კვირაში'], 13 | ['1 თვის წინ', '1 თვეში'], 14 | ['%s თვის წინ', '%s თვეში'], 15 | ['1 წლის წინ', '1 წელიწადში'], 16 | ['%s წლის წინ', '%s წელიწადში'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ko.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['방금', '곧'], 4 | ['%s초 전', '%s초 후'], 5 | ['1분 전', '1분 후'], 6 | ['%s분 전', '%s분 후'], 7 | ['1시간 전', '1시간 후'], 8 | ['%s시간 전', '%s시간 후'], 9 | ['1일 전', '1일 후'], 10 | ['%s일 전', '%s일 후'], 11 | ['1주일 전', '1주일 후'], 12 | ['%s주일 전', '%s주일 후'], 13 | ['1개월 전', '1개월 후'], 14 | ['%s개월 전', '%s개월 후'], 15 | ['1년 전', '1년 후'], 16 | ['%s년 전', '%s년 후'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ml.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['ഇപ്പോള്‍', 'കുറച്ചു മുന്‍പ്'], 4 | ['%s സെക്കന്റ്‌കള്‍ക്ക് മുന്‍പ്', '%s സെക്കന്റില്‍'], 5 | ['1 മിനിറ്റിനു മുന്‍പ്', '1 മിനിറ്റില്‍'], 6 | ['%s മിനിറ്റുകള്‍ക്ക് മുന്‍പ', '%s മിനിറ്റില്‍'], 7 | ['1 മണിക്കൂറിനു മുന്‍പ്', '1 മണിക്കൂറില്‍'], 8 | ['%s മണിക്കൂറുകള്‍ക്കു മുന്‍പ്', '%s മണിക്കൂറില്‍'], 9 | ['1 ഒരു ദിവസം മുന്‍പ്', '1 ദിവസത്തില്‍'], 10 | ['%s ദിവസങ്ങള്‍ക് മുന്‍പ്', '%s ദിവസങ്ങള്‍ക്കുള്ളില്‍'], 11 | ['1 ആഴ്ച മുന്‍പ്', '1 ആഴ്ചയില്‍'], 12 | ['%s ആഴ്ചകള്‍ക്ക് മുന്‍പ്', '%s ആഴ്ചകള്‍ക്കുള്ളില്‍'], 13 | ['1 മാസത്തിനു മുന്‍പ്', '1 മാസത്തിനുള്ളില്‍'], 14 | ['%s മാസങ്ങള്‍ക്ക് മുന്‍പ്', '%s മാസങ്ങള്‍ക്കുള്ളില്‍'], 15 | ['1 വര്‍ഷത്തിനു മുന്‍പ്', '1 വര്‍ഷത്തിനുള്ളില്‍'], 16 | ['%s വര്‍ഷങ്ങള്‍ക്കു മുന്‍പ്', '%s വര്‍ഷങ്ങള്‍ക്കുല്ല്ളില്‍'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/my.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['ယခုအတွင်း', 'ယခု'], 4 | ['%s စက္ကန့် အကြာက', '%s စက္ကန့်အတွင်း'], 5 | ['1 မိနစ် အကြာက', '1 မိနစ်အတွင်း'], 6 | ['%s မိနစ် အကြာက', '%s မိနစ်အတွင်း'], 7 | ['1 နာရီ အကြာက', '1 နာရီအတွင်း'], 8 | ['%s နာရီ အကြာက', '%s နာရီအတွင်း'], 9 | ['1 ရက် အကြာက', '1 ရက်အတွင်း'], 10 | ['%s ရက် အကြာက', '%s ရက်အတွင်း'], 11 | ['1 ပတ် အကြာက', '1 ပတ်အတွင်း'], 12 | ['%s ပတ် အကြာက', '%s ပတ်အတွင်း'], 13 | ['1 လ အကြာက', '1 လအတွင်း'], 14 | ['%s လ အကြာက', '%s လအတွင်း'], 15 | ['1 နှစ် အကြာက', '1 နှစ်အတွင်း'], 16 | ['%s နှစ် အကြာက', '%s နှစ်အတွင်း'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/nb_NO.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['akkurat nå', 'om litt'], 4 | ['%s sekunder siden', 'om %s sekunder'], 5 | ['1 minutt siden', 'om 1 minutt'], 6 | ['%s minutter siden', 'om %s minutter'], 7 | ['1 time siden', 'om 1 time'], 8 | ['%s timer siden', 'om %s timer'], 9 | ['1 dag siden', 'om 1 dag'], 10 | ['%s dager siden', 'om %s dager'], 11 | ['1 uke siden', 'om 1 uke'], 12 | ['%s uker siden', 'om %s uker'], 13 | ['1 måned siden', 'om 1 måned'], 14 | ['%s måneder siden', 'om %s måneder'], 15 | ['1 år siden', 'om 1 år'], 16 | ['%s år siden', 'om %s år'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/nl.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['recent', 'binnenkort'], 4 | ['%s seconden geleden', 'binnen %s seconden'], 5 | ['1 minuut geleden', 'binnen 1 minuut'], 6 | ['%s minuten geleden', 'binnen %s minuten'], 7 | ['1 uur geleden', 'binnen 1 uur'], 8 | ['%s uur geleden', 'binnen %s uur'], 9 | ['1 dag geleden', 'binnen 1 dag'], 10 | ['%s dagen geleden', 'binnen %s dagen'], 11 | ['1 week geleden', 'binnen 1 week'], 12 | ['%s weken geleden', 'binnen %s weken'], 13 | ['1 maand geleden', 'binnen 1 maand'], 14 | ['%s maanden geleden', 'binnen %s maanden'], 15 | ['1 jaar geleden', 'binnen 1 jaar'], 16 | ['%s jaar geleden', 'binnen %s jaar'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/nn_NO.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['nett no', 'om litt'], 4 | ['%s sekund sidan', 'om %s sekund'], 5 | ['1 minutt sidan', 'om 1 minutt'], 6 | ['%s minutt sidan', 'om %s minutt'], 7 | ['1 time sidan', 'om 1 time'], 8 | ['%s timar sidan', 'om %s timar'], 9 | ['1 dag sidan', 'om 1 dag'], 10 | ['%s dagar sidan', 'om %s dagar'], 11 | ['1 veke sidan', 'om 1 veke'], 12 | ['%s veker sidan', 'om %s veker'], 13 | ['1 månad sidan', 'om 1 månad'], 14 | ['%s månadar sidan', 'om %s månadar'], 15 | ['1 år sidan', 'om 1 år'], 16 | ['%s år sidan', 'om %s år'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/oc.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['fa un moment', "d'aquí un moment"], 4 | ['fa %s segondas', "d'aquí %s segondas"], 5 | ['fa 1 minuta', "d'aquí 1 minuta"], 6 | ['fa %s minutas', "d'aquí %s minutas"], 7 | ['fa 1 ora', "d'aquí 1 ora"], 8 | ['fa %s oras', "d'aquí %s oras"], 9 | ['fa 1 jorn', "d'aquí 1 jorn"], 10 | ['fa %s jorns', "d'aquí %s jorns"], 11 | ['fa 1 setmana', "d'aquí 1 setmana"], 12 | ['fa %s setmanas', "d'aquí %s setmanas"], 13 | ['fa 1 mes', "d'aquí 1 mes"], 14 | ['fa %s meses', "d'aquí %s meses"], 15 | ['fa 1 an', "d'aquí 1 an"], 16 | ['fa %s ans', "d'aquí %s ans"], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/pl.ts: -------------------------------------------------------------------------------- 1 | // 0-13 alternately: single unit of time, 2 | // genitive plural form for all other numbers excluding cases below: 3 | // 14-20: nominative plural form for the numbers 2,3,4 4 | // and all other numbers higher than 21 which end in 2,3,4 5 | const l = [ 6 | ['w tej chwili', 'za chwilę'], 7 | ['%s sekund temu', 'za %s sekund'], 8 | ['1 minutę temu', 'za 1 minutę'], 9 | ['%s minut temu', 'za %s minut'], 10 | ['1 godzinę temu', 'za 1 godzinę'], 11 | ['%s godzin temu', 'za %s godzin'], 12 | ['1 dzień temu', 'za 1 dzień'], // ['wczoraj', 'jutro'], 13 | ['%s dni temu', 'za %s dni'], 14 | ['1 tydzień temu', 'za 1 tydzień'], 15 | ['%s tygodni temu', 'za %s tygodni'], 16 | ['1 miesiąc temu', 'za 1 miesiąc'], 17 | ['%s miesięcy temu', 'za %s miesięcy'], 18 | ['1 rok temu', 'za 1 rok'], 19 | ['%s lat temu', 'za %s lat'], 20 | ['%s sekundy temu', 'za %s sekundy'], 21 | ['%s minuty temu', 'za %s minuty'], 22 | ['%s godziny temu', 'za %s godziny'], 23 | ['%s dni temu', 'za %s dni'], 24 | ['%s tygodnie temu', 'za %s tygodnie'], 25 | ['%s miesiące temu', 'za %s miesiące'], 26 | ['%s lata temu', 'za %s lata'], 27 | ] 28 | 29 | export default function (number: number, index: number): [string, string] { 30 | // to determine which plural form must be used check the last 2 digits 31 | // and calculate new index value to get the nominative form (14-20) 32 | // for all other cases use index value as it is (0-13) 33 | return l[index & 1 ? (number % 10 > 4 || number % 10 < 2 || 1 === ~~(number / 10) % 10 ? index : ++index / 2 + 13) : index] as [string, string] 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/pt_BR.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['agora mesmo', 'agora'], 4 | ['há %s segundos', 'em %s segundos'], 5 | ['há um minuto', 'em um minuto'], 6 | ['há %s minutos', 'em %s minutos'], 7 | ['há uma hora', 'em uma hora'], 8 | ['há %s horas', 'em %s horas'], 9 | ['há um dia', 'em um dia'], 10 | ['há %s dias', 'em %s dias'], 11 | ['há uma semana', 'em uma semana'], 12 | ['há %s semanas', 'em %s semanas'], 13 | ['há um mês', 'em um mês'], 14 | ['há %s meses', 'em %s meses'], 15 | ['há um ano', 'em um ano'], 16 | ['há %s anos', 'em %s anos'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ro.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | const langTable = [ 3 | ['chiar acum', 'chiar acum'], 4 | ['acum %s secunde', 'peste %s secunde'], 5 | ['acum un minut', 'peste un minut'], 6 | ['acum %s minute', 'peste %s minute'], 7 | ['acum o oră', 'peste o oră'], 8 | ['acum %s ore', 'peste %s ore'], 9 | ['acum o zi', 'peste o zi'], 10 | ['acum %s zile', 'peste %s zile'], 11 | ['acum o săptămână', 'peste o săptămână'], 12 | ['acum %s săptămâni', 'peste %s săptămâni'], 13 | ['acum o lună', 'peste o lună'], 14 | ['acum %s luni', 'peste %s luni'], 15 | ['acum un an', 'peste un an'], 16 | ['acum %s ani', 'peste %s ani'], 17 | ] 18 | 19 | if (number < 20) { 20 | return langTable[index] as [string, string] 21 | } 22 | 23 | // A `de` preposition must be added between the number and the adverb 24 | // if the number is greater than 20. 25 | return [langTable[index][0].replace('%s', '%s de'), langTable[index][1].replace('%s', '%s de')] 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/sq.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['pak më parë', 'pas pak'], 4 | ['para %s sekondash', 'pas %s sekondash'], 5 | ['para një minute', 'pas një minute'], 6 | ['para %s minutash', 'pas %s minutash'], 7 | ['para një ore', 'pas një ore'], 8 | ['para %s orësh', 'pas %s orësh'], 9 | ['dje', 'nesër'], 10 | ['para %s ditësh', 'pas %s ditësh'], 11 | ['para një jave', 'pas një jave'], 12 | ['para %s javësh', 'pas %s javësh'], 13 | ['para një muaji', 'pas një muaji'], 14 | ['para %s muajsh', 'pas %s muajsh'], 15 | ['para një viti', 'pas një viti'], 16 | ['para %s vjetësh', 'pas %s vjetësh'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/sr.ts: -------------------------------------------------------------------------------- 1 | function formatNum(single: string, one: string, few: string, other: string, n: number): string { 2 | const rem10 = n % 10 3 | const rem100 = n % 100 4 | 5 | if (n == 1) { 6 | return single 7 | } else if (rem10 == 1 && rem100 != 11) { 8 | return one 9 | } else if (rem10 >= 2 && rem10 <= 4 && !(rem100 >= 12 && rem100 <= 14)) { 10 | return few 11 | } else { 12 | return other 13 | } 14 | } 15 | 16 | const seconds = formatNum.bind(null, '1 секунд', '%s секунд', '%s секунде', '%s секунди'), 17 | minutes = formatNum.bind(null, '1 минут', '%s минут', '%s минуте', '%s минута'), 18 | hours = formatNum.bind(null, 'сат времена', '%s сат', '%s сата', '%s сати'), 19 | days = formatNum.bind(null, '1 дан', '%s дан', '%s дана', '%s дана'), 20 | weeks = formatNum.bind(null, 'недељу дана', '%s недељу', '%s недеље', '%s недеља'), 21 | months = formatNum.bind(null, 'месец дана', '%s месец', '%s месеца', '%s месеци'), 22 | years = formatNum.bind(null, 'годину дана', '%s годину', '%s године', '%s година') 23 | 24 | export default function (number: number, index: number): [string, string] { 25 | switch (index) { 26 | case 0: 27 | return ['малопре', 'управо сад'] 28 | case 1: 29 | return ['пре ' + seconds(number), 'за ' + seconds(number)] 30 | case 2: 31 | case 3: 32 | return ['пре ' + minutes(number), 'за ' + minutes(number)] 33 | case 4: 34 | case 5: 35 | return ['пре ' + hours(number), 'за ' + hours(number)] 36 | case 6: 37 | case 7: 38 | return ['пре ' + days(number), 'за ' + days(number)] 39 | case 8: 40 | case 9: 41 | return ['пре ' + weeks(number), 'за ' + weeks(number)] 42 | case 10: 43 | case 11: 44 | return ['пре ' + months(number), 'за ' + months(number)] 45 | case 12: 46 | case 13: 47 | return ['пре ' + years(number), 'за ' + years(number)] 48 | default: 49 | return ['', ''] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/sv.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['just nu', 'om en stund'], 4 | ['%s sekunder sedan', 'om %s sekunder'], 5 | ['1 minut sedan', 'om 1 minut'], 6 | ['%s minuter sedan', 'om %s minuter'], 7 | ['1 timme sedan', 'om 1 timme'], 8 | ['%s timmar sedan', 'om %s timmar'], 9 | ['1 dag sedan', 'om 1 dag'], 10 | ['%s dagar sedan', 'om %s dagar'], 11 | ['1 vecka sedan', 'om 1 vecka'], 12 | ['%s veckor sedan', 'om %s veckor'], 13 | ['1 månad sedan', 'om 1 månad'], 14 | ['%s månader sedan', 'om %s månader'], 15 | ['1 år sedan', 'om 1 år'], 16 | ['%s år sedan', 'om %s år'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/ta.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['இப்போது', 'சற்று நேரம் முன்பு'], 4 | ['%s நொடிக்கு முன்', '%s நொடிகளில்'], 5 | ['1 நிமிடத்திற்க்கு முன்', '1 நிமிடத்தில்'], 6 | ['%s நிமிடத்திற்க்கு முன்', '%s நிமிடங்களில்'], 7 | ['1 மணி நேரத்திற்கு முன்', '1 மணி நேரத்திற்குள்'], 8 | ['%s மணி நேரத்திற்கு முன்', '%s மணி நேரத்திற்குள்'], 9 | ['1 நாளுக்கு முன்', '1 நாளில்'], 10 | ['%s நாட்களுக்கு முன்', '%s நாட்களில்'], 11 | ['1 வாரத்திற்கு முன்', '1 வாரத்தில்'], 12 | ['%s வாரங்களுக்கு முன்', '%s வாரங்களில்'], 13 | ['1 மாதத்திற்கு முன்', '1 மாதத்தில்'], 14 | ['%s மாதங்களுக்கு முன்', '%s மாதங்களில்'], 15 | ['1 வருடத்திற்கு முன்', '1 வருடத்தில்'], 16 | ['%s வருடங்களுக்கு முன்', '%s வருடங்களில்'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/th.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['เมื่อสักครู่นี้', 'อีกสักครู่'], 4 | ['%s วินาทีที่แล้ว', 'ใน %s วินาที'], 5 | ['1 นาทีที่แล้ว', 'ใน 1 นาที'], 6 | ['%s นาทีที่แล้ว', 'ใน %s นาที'], 7 | ['1 ชั่วโมงที่แล้ว', 'ใน 1 ชั่วโมง'], 8 | ['%s ชั่วโมงที่แล้ว', 'ใน %s ชั่วโมง'], 9 | ['1 วันที่แล้ว', 'ใน 1 วัน'], 10 | ['%s วันที่แล้ว', 'ใน %s วัน'], 11 | ['1 อาทิตย์ที่แล้ว', 'ใน 1 อาทิตย์'], 12 | ['%s อาทิตย์ที่แล้ว', 'ใน %s อาทิตย์'], 13 | ['1 เดือนที่แล้ว', 'ใน 1 เดือน'], 14 | ['%s เดือนที่แล้ว', 'ใน %s เดือน'], 15 | ['1 ปีที่แล้ว', 'ใน 1 ปี'], 16 | ['%s ปีที่แล้ว', 'ใน %s ปี'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/tk.ts: -------------------------------------------------------------------------------- 1 | const TK = ['sekunt', 'minut', 'sagat', 'gün', 'hepde', 'aý', 'ýyl'] 2 | function getSuffix(unit: string): string { 3 | return unit.match(/[aouy]/) ? 'dan' : 'den' 4 | } 5 | export default function (diff: number, idx: number): [string, string] { 6 | if (idx === 0) return ['biraz öň', 'şuwagt'] 7 | const unit = TK[Math.floor(idx / 2)] 8 | return [`${diff} ${unit} öň`, `${diff} ${unit}${getSuffix(unit)}`] 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/tr.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['az önce', 'şimdi'], 4 | ['%s saniye önce', '%s saniye içinde'], 5 | ['1 dakika önce', '1 dakika içinde'], 6 | ['%s dakika önce', '%s dakika içinde'], 7 | ['1 saat önce', '1 saat içinde'], 8 | ['%s saat önce', '%s saat içinde'], 9 | ['1 gün önce', '1 gün içinde'], 10 | ['%s gün önce', '%s gün içinde'], 11 | ['1 hafta önce', '1 hafta içinde'], 12 | ['%s hafta önce', '%s hafta içinde'], 13 | ['1 ay önce', '1 ay içinde'], 14 | ['%s ay önce', '%s ay içinde'], 15 | ['1 yıl önce', '1 yıl içinde'], 16 | ['%s yıl önce', '%s yıl içinde'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/uk.ts: -------------------------------------------------------------------------------- 1 | function formatNum(f1: string, f: string, s: string, t: string, n: number): string { 2 | const n10 = n % 10 3 | let str = t 4 | 5 | if (n === 1) { 6 | str = f1 7 | } else if (n10 === 1 && n > 20) { 8 | str = f 9 | } else if (n10 > 1 && n10 < 5 && (n > 20 || n < 10)) { 10 | str = s 11 | } 12 | return str 13 | } 14 | 15 | const seconds = formatNum.bind(null, 'секунду', '%s секунду', '%s секунди', '%s секунд'), 16 | minutes = formatNum.bind(null, 'хвилину', '%s хвилину', '%s хвилини', '%s хвилин'), 17 | hours = formatNum.bind(null, 'годину', '%s годину', '%s години', '%s годин'), 18 | days = formatNum.bind(null, 'день', '%s день', '%s дні', '%s днів'), 19 | weeks = formatNum.bind(null, 'тиждень', '%s тиждень', '%s тиждні', '%s тижднів'), 20 | months = formatNum.bind(null, 'місяць', '%s місяць', '%s місяці', '%s місяців'), 21 | years = formatNum.bind(null, 'рік', '%s рік', '%s роки', '%s років') 22 | 23 | export default function (number: number, index: number): [string, string] { 24 | switch (index) { 25 | case 0: 26 | return ['щойно', 'через декілька секунд'] 27 | case 1: 28 | return [seconds(number) + ' тому', 'через ' + seconds(number)] 29 | case 2: 30 | case 3: 31 | return [minutes(number) + ' тому', 'через ' + minutes(number)] 32 | case 4: 33 | case 5: 34 | return [hours(number) + ' тому', 'через ' + hours(number)] 35 | case 6: 36 | case 7: 37 | return [days(number) + ' тому', 'через ' + days(number)] 38 | case 8: 39 | case 9: 40 | return [weeks(number) + ' тому', 'через ' + weeks(number)] 41 | case 10: 42 | case 11: 43 | return [months(number) + ' тому', 'через ' + months(number)] 44 | case 12: 45 | case 13: 46 | return [years(number) + ' тому', 'через ' + years(number)] 47 | default: 48 | return ['', ''] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/vi.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['vừa xong', 'một lúc'], 4 | ['%s giây trước', 'trong %s giây'], 5 | ['1 phút trước', 'trong 1 phút'], 6 | ['%s phút trước', 'trong %s phút'], 7 | ['1 giờ trước', 'trong 1 giờ'], 8 | ['%s giờ trước', 'trong %s giờ'], 9 | ['1 ngày trước', 'trong 1 ngày'], 10 | ['%s ngày trước', 'trong %s ngày'], 11 | ['1 tuần trước', 'trong 1 tuần'], 12 | ['%s tuần trước', 'trong %s tuần'], 13 | ['1 tháng trước', 'trong 1 tháng'], 14 | ['%s tháng trước', 'trong %s tháng'], 15 | ['1 năm trước', 'trong 1 năm'], 16 | ['%s năm trước', 'trong %s năm'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/zh_CN.ts: -------------------------------------------------------------------------------- 1 | const ZH_CN = ['秒', '分钟', '小时', '天', '周', '个月', '年'] 2 | 3 | export default function (diff: number, idx: number): [string, string] { 4 | if (idx === 0) return ['刚刚', '片刻后'] 5 | const unit = ZH_CN[~~(idx / 2)] 6 | return [`${diff} ${unit}前`, `${diff} ${unit}后`] 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/timeago/lang/zh_TW.ts: -------------------------------------------------------------------------------- 1 | export default function (number: number, index: number): [string, string] { 2 | return [ 3 | ['剛剛', '片刻後'], 4 | ['%s 秒前', '%s 秒後'], 5 | ['1 分鐘前', '1 分鐘後'], 6 | ['%s 分鐘前', '%s 分鐘後'], 7 | ['1 小時前', '1 小時後'], 8 | ['%s 小時前', '%s 小時後'], 9 | ['1 天前', '1 天後'], 10 | ['%s 天前', '%s 天後'], 11 | ['1 週前', '1 週後'], 12 | ['%s 週前', '%s 週後'], 13 | ['1 個月前', '1 個月後'], 14 | ['%s 個月前', '%s 個月後'], 15 | ['1 年前', '1 年後'], 16 | ['%s 年前', '%s 年後'], 17 | ][index] as [string, string] 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/timeago/register.ts: -------------------------------------------------------------------------------- 1 | import type { LocaleFunc, LocaleMap } from './interface' 2 | 3 | /** 4 | * All supported locales 5 | */ 6 | const Locales: LocaleMap = {} 7 | 8 | /** 9 | * register a locale 10 | * @param locale 11 | * @param func 12 | */ 13 | export const register = (locale: string, func: LocaleFunc) => { 14 | Locales[locale] = func 15 | } 16 | 17 | /** 18 | * get a locale, default is en_US 19 | * @param locale 20 | * @returns {*} 21 | */ 22 | export const getLocale = (locale: string): LocaleFunc => { 23 | return Locales[locale] || Locales['en_US'] 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/timeago/utils/dom.ts: -------------------------------------------------------------------------------- 1 | const ATTR_TIMEAGO_TID = 'timeago-id' 2 | 3 | /** 4 | * get the datetime attribute, `datetime` are supported. 5 | * @param node 6 | * @returns {*} 7 | */ 8 | export function getDateAttribute(node: HTMLElement): string { 9 | return node.getAttribute('datetime') 10 | } 11 | 12 | /** 13 | * set the node attribute, native DOM 14 | * @param node 15 | * @param timerId 16 | * @returns {*} 17 | */ 18 | export function setTimerId(node: HTMLElement, timerId: number): void { 19 | // @ts-ignore 20 | node.setAttribute(ATTR_TIMEAGO_TID, timerId) 21 | } 22 | 23 | /** 24 | * get the timer id 25 | * @param node 26 | */ 27 | export function getTimerId(node: HTMLElement): number { 28 | return parseInt(node.getAttribute(ATTR_TIMEAGO_TID)) 29 | } 30 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp, provide, h } from 'vue' 2 | import { ApolloClients } from '@vue/apollo-composable' 3 | import App from './App.vue' 4 | import './registerServiceWorker' 5 | import router from './plugins/router' 6 | import '@material/web/all' 7 | import '@/styles/main.scss' 8 | import { createPinia } from 'pinia' 9 | import apollo from './plugins/apollo' 10 | import VueClickAway from './plugins/clickaway' 11 | import VueTooltip from './plugins/tooltip' 12 | import i18n from './plugins/i18n' 13 | import { shortUUID } from './lib/strutil' 14 | 15 | const clientId = localStorage.getItem('client_id') 16 | if (!clientId) { 17 | localStorage.setItem('client_id', shortUUID()) 18 | } 19 | 20 | createApp({ 21 | setup() { 22 | provide(ApolloClients, apollo) 23 | }, 24 | render: () => h(App), 25 | }) 26 | .use(VueClickAway) 27 | .use(VueTooltip) 28 | .use(createPinia()) 29 | .use(router) 30 | .use(i18n) 31 | .mount('#app') 32 | -------------------------------------------------------------------------------- /src/plugins/apollo.ts: -------------------------------------------------------------------------------- 1 | import { createHttpLink } from '@/lib/api/create-http-link' 2 | import { ApolloClient, InMemoryCache } from '@apollo/client/core' 3 | 4 | const appApolloClient = new ApolloClient({ 5 | link: createHttpLink(), 6 | cache: new InMemoryCache({ 7 | typePolicies: { 8 | File: { 9 | keyFields: ['path'], 10 | }, 11 | ChatItem: { 12 | fields: { 13 | _content: { 14 | read(_, { readField }) { 15 | return JSON.parse(readField('content') as string) 16 | }, 17 | }, 18 | }, 19 | }, 20 | }, 21 | }), 22 | defaultOptions: { 23 | watchQuery: { 24 | errorPolicy: 'all', 25 | fetchPolicy: 'network-only', 26 | }, 27 | }, 28 | }) 29 | 30 | export default { 31 | a: appApolloClient, 32 | default: appApolloClient, 33 | } 34 | -------------------------------------------------------------------------------- /src/plugins/clickaway.ts: -------------------------------------------------------------------------------- 1 | import type { Directive, Plugin } from 'vue' 2 | 3 | const DOWN_ID = '__vue_click_away_down__' 4 | const UP_ID = '__vue_click_away_up__' 5 | 6 | const onMounted = (el: any, binding: any, vnode: any) => { 7 | onUnmounted(el) 8 | let downTarget: EventTarget | null 9 | 10 | const vm = vnode.context 11 | const callback = binding.value 12 | el[DOWN_ID] = (event: PointerEvent) => { 13 | downTarget = event.target 14 | } 15 | 16 | el[UP_ID] = (event: PointerEvent) => { 17 | if (downTarget !== event.target) { 18 | return 19 | } 20 | 21 | if ((!el || !el.contains(event.target)) && callback && typeof callback === 'function') { 22 | return callback.call(vm, event) 23 | } 24 | } 25 | 26 | document.addEventListener('mousedown', el[DOWN_ID], false) 27 | document.addEventListener('mouseup', el[UP_ID], false) 28 | } 29 | 30 | const onUnmounted = (el: { [x: string]: any }) => { 31 | document.removeEventListener('mousedown', el[DOWN_ID], false) 32 | document.removeEventListener('mouseup', el[UP_ID], false) 33 | delete el[DOWN_ID] 34 | delete el[UP_ID] 35 | } 36 | 37 | const onUpdated = (el: any, binding: { value: any; oldValue: any }, vnode: any) => { 38 | if (binding.value === binding.oldValue) { 39 | return 40 | } 41 | onMounted(el, binding, vnode) 42 | } 43 | 44 | const plugin: Plugin = { 45 | install: (app) => { 46 | app.directive('click-away', directive) 47 | }, 48 | } 49 | 50 | const directive: Directive = { 51 | mounted: onMounted, 52 | updated: onUpdated, 53 | unmounted: onUnmounted, 54 | } 55 | 56 | export default plugin 57 | -------------------------------------------------------------------------------- /src/plugins/eventbus.ts: -------------------------------------------------------------------------------- 1 | import type { IItemTagsUpdatedEvent, IItemsTagsUpdatedEvent, IFileDeletedEvent, IFileRenamedEvent, IMediaItemsActionedEvent, INotesActionedEvent } from '@/lib/interfaces' 2 | import type { IUploadItem } from '@/stores/temp' 3 | import mitt, { type Emitter } from 'mitt' 4 | 5 | type Events = { 6 | upload_task_done: IUploadItem 7 | refetch_app: undefined 8 | play_audio: undefined 9 | do_play_audio: undefined 10 | pause_audio: undefined 11 | item_tags_updated: IItemTagsUpdatedEvent 12 | items_tags_updated: IItemsTagsUpdatedEvent 13 | refetch_tags: string 14 | media_items_actioned: IMediaItemsActionedEvent 15 | feed_entries_deleted: undefined 16 | calls_deleted: undefined 17 | notes_actioned: INotesActionedEvent 18 | file_deleted: IFileDeletedEvent 19 | file_renamed: IFileRenamedEvent 20 | toast: string 21 | tap_phone: string 22 | feeds_fetched: any 23 | screen_mirroring: Blob 24 | message_created: any 25 | message_updated: any 26 | message_deleted: any 27 | notification_created: any 28 | notification_updated: any 29 | notification_deleted: any 30 | color_mode_changed: undefined 31 | app_socket_connection_changed: boolean 32 | } 33 | 34 | const emitter: Emitter = mitt() 35 | 36 | export default emitter 37 | -------------------------------------------------------------------------------- /src/plugins/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | import en_US from '@/locales/en-US' 3 | import zh_CN from '@/locales/zh-CN' 4 | import zh_TW from '@/locales/zh-TW' 5 | import es from '@/locales/es' 6 | import ja from '@/locales/ja' 7 | import nl from '@/locales/nl' 8 | import it from '@/locales/it' 9 | import hi from '@/locales/hi' 10 | import fr from '@/locales/fr' 11 | import ru from '@/locales/ru' 12 | import bn from '@/locales/bn' 13 | import de from '@/locales/de' 14 | import pt from '@/locales/pt' 15 | import ta from '@/locales/ta' 16 | import ko from '@/locales/ko' 17 | import tr from '@/locales/tr' 18 | import vi from '@/locales/vi' 19 | import { setLocale } from 'yup' 20 | 21 | setLocale({ 22 | mixed: { 23 | required: 'valid.required', 24 | }, 25 | string: { 26 | min: 'valid.string_min', 27 | }, 28 | }) 29 | 30 | export default createI18n({ 31 | legacy: false, 32 | locale: localStorage.getItem('locale') ?? navigator.language, 33 | fallbackLocale: 'en-US', 34 | messages: { 35 | 'en-US': en_US, 36 | 'zh-CN': zh_CN, 37 | 'zh-TW': zh_TW, 38 | es: es, 39 | ja: ja, 40 | nl: nl, 41 | it: it, 42 | hi: hi, 43 | fr: fr, 44 | ru: ru, 45 | bn: bn, 46 | de: de, 47 | pt: pt, 48 | ta: ta, 49 | ko: ko, 50 | tr: tr, 51 | vi: vi, 52 | }, 53 | }) 54 | -------------------------------------------------------------------------------- /src/plugins/monacoworker.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor' 2 | import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' 3 | import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker' 4 | import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker' 5 | import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker' 6 | import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' 7 | 8 | // @ts-ignore 9 | self.MonacoEnvironment = { 10 | getWorker(_: any, label: string) { 11 | if (label === 'json') { 12 | return new jsonWorker() 13 | } 14 | if (['css', 'scss', 'less'].includes(label)) { 15 | return new cssWorker() 16 | } 17 | if (['html', 'handlebars', 'razor'].includes(label)) { 18 | return new htmlWorker() 19 | } 20 | if (['typescript', 'javascript'].includes(label)) { 21 | return new tsWorker() 22 | } 23 | return new editorWorker() 24 | }, 25 | } 26 | 27 | monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true) 28 | -------------------------------------------------------------------------------- /src/plugins/tapphone.ts: -------------------------------------------------------------------------------- 1 | import emitter from '@/plugins/eventbus' 2 | 3 | export default (message: string) => { 4 | emitter.emit('tap_phone', message) 5 | } 6 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | if ('serviceWorker' in navigator) { 2 | // Register a service worker hosted at the root of the 3 | // site using the default scope. 4 | navigator.serviceWorker.register('/sw.js').then( 5 | (registration) => { 6 | console.log('Service worker registration succeeded:', registration) 7 | }, 8 | (error) => { 9 | console.error(`Service worker registration failed: ${error}`) 10 | } 11 | ) 12 | } else { 13 | console.error('Service workers are not supported.') 14 | } 15 | -------------------------------------------------------------------------------- /src/stores/files.ts: -------------------------------------------------------------------------------- 1 | import type { IFile } from '@/lib/file' 2 | import { defineStore } from 'pinia' 3 | import type sjcl from 'sjcl' 4 | 5 | export type FilesState = { 6 | selectedFiles: IFile[] 7 | isCut: boolean 8 | } 9 | 10 | export const useFilesStore = defineStore('files', { 11 | state: () => 12 | ({ 13 | selectedFiles: [], 14 | isCut: false, 15 | }) as FilesState, 16 | }) 17 | -------------------------------------------------------------------------------- /src/styles/_alert.scss: -------------------------------------------------------------------------------- 1 | %alert-common-properties { 2 | border-radius: 8px; 3 | padding: 0; 4 | display: grid; 5 | grid-template-areas: 6 | 'svg body' 7 | 'actions actions'; 8 | grid-template-columns: auto 1fr; 9 | max-height: 0; 10 | opacity: 0; 11 | transition: 12 | padding 0.3s ease, 13 | max-height 0.3s ease, 14 | opacity 0.3s ease; 15 | svg { 16 | margin-inline-end: 8px; 17 | margin-block-start: 2px; 18 | grid-area: svg; 19 | } 20 | .body { 21 | grid-area: body; 22 | } 23 | .actions { 24 | grid-area: actions; 25 | margin-block-start: 8px; 26 | text-align: right; 27 | } 28 | } 29 | 30 | .alert-danger { 31 | @extend %alert-common-properties; 32 | color: var(--md-sys-color-error); 33 | svg { 34 | color: var(--md-sys-color-error); 35 | } 36 | } 37 | 38 | .alert-info { 39 | @extend %alert-common-properties; 40 | background-color: var(--md-sys-color-surface-container); 41 | color: var(--md-sys-color-on-surface); 42 | } 43 | 44 | .dark { 45 | .alert-info { 46 | background-color: var(--md-sys-color-surface-container-highest); 47 | } 48 | } 49 | 50 | .alert-warning { 51 | @extend %alert-common-properties; 52 | background-color: var(--md-sys-color-surface); 53 | svg { 54 | color: var(--md-sys-color-warning); 55 | } 56 | } 57 | 58 | .alert-danger.show, 59 | .alert-info.show, 60 | .alert-warning.show { 61 | padding: 12px; 62 | max-height: 100px; 63 | opacity: 1; 64 | } 65 | 66 | .alert-all-checked { 67 | display: flex; 68 | justify-content: center; 69 | margin: 0 16px; 70 | gap: 8px; 71 | } 72 | 73 | .alert-all-checked + .table-responsive { 74 | margin-block-start: 8px; 75 | } 76 | -------------------------------------------------------------------------------- /src/styles/_audio.scss: -------------------------------------------------------------------------------- 1 | // https://opensource.apple.com/source/WebCore/WebCore-1889.59/css/mediaControls.css.auto.html 2 | audio::-webkit-media-controls-play-button, 3 | audio::-webkit-media-controls-panel { 4 | background-color: var(--md-sys-color-surface); 5 | } 6 | 7 | :root.dark { 8 | audio::-webkit-media-controls-play-button, 9 | audio::-webkit-media-controls-panel { 10 | background-color: #ccc; 11 | } 12 | } 13 | 14 | 15 | audio::-webkit-media-controls-current-time-display, 16 | audio::-webkit-media-controls-time-remaining-display { 17 | text-shadow: none; 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/_buttons.scss: -------------------------------------------------------------------------------- 1 | .button-group { 2 | display: flex; 3 | border-radius: var(--md-shape-corner-full, 9999px); 4 | border: 1px solid var(--md-sys-color-outline, #79747e); 5 | overflow: hidden; 6 | height: 32px; 7 | 8 | button { 9 | flex: 1; 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | background: none; 14 | border: none; 15 | padding: 0 16px; 16 | cursor: pointer; 17 | color: var(--md-sys-color-on-surface); 18 | transition: 19 | background-color 0.2s, 20 | color 0.2s; 21 | } 22 | 23 | button:not(:last-child) { 24 | border-right: 1px solid var(--md-sys-color-outline, #79747e); 25 | } 26 | 27 | button.selected { 28 | background-color: var(--md-sys-color-secondary-container); 29 | color: var(--md-sys-color-on-secondary-container); 30 | } 31 | 32 | button:hover:not(.selected) { 33 | background-color: var(--md-sys-color-surface-variant); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/styles/_card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | position: relative; 3 | display: flex; 4 | flex-direction: column; 5 | word-wrap: break-word; 6 | background-clip: border-box; 7 | border-radius: 8px; 8 | padding: 16px; 9 | box-sizing: border-box; 10 | background: var(--md-sys-color-surface-container); 11 | color: var(--md-sys-color-on-surface); 12 | &.selected { 13 | background: var(--md-sys-color-surface-container-highest); 14 | } 15 | } 16 | 17 | .card-body { 18 | flex: 1 1 auto; 19 | } 20 | 21 | .card-title { 22 | font-weight: bold; 23 | text-transform: uppercase; 24 | font-size: 1.25rem; 25 | margin: 0 0 16px 0; 26 | } 27 | 28 | .card.outlined { 29 | border: 1px solid var(--md-sys-color-outline); 30 | background-color: transparent; 31 | } -------------------------------------------------------------------------------- /src/styles/_chat.scss: -------------------------------------------------------------------------------- 1 | .chat-section { 2 | border-radius: 8px; 3 | border: 1px solid var(--md-sys-color-outline); 4 | overflow: hidden; 5 | } 6 | 7 | .chat-item { 8 | padding: 0 16px 16px; 9 | 10 | .date { 11 | color: var(--md-sys-color-secondary); 12 | font-size: 0.8rem; 13 | text-align: center; 14 | } 15 | 16 | &:first-child { 17 | padding-top: 16px; 18 | } 19 | 20 | .bi-more { 21 | display: none; 22 | vertical-align: middle; 23 | } 24 | 25 | &:hover .bi-more { 26 | display: inline-block; 27 | } 28 | 29 | .image-container { 30 | display: grid; 31 | grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); 32 | gap: 4px; 33 | } 34 | } 35 | 36 | .chat-title { 37 | display: inline-block; 38 | height: 24px; 39 | 40 | .name { 41 | font-weight: bold; 42 | margin-right: 4px; 43 | font-size: 0.875rem; 44 | text-transform: uppercase; 45 | } 46 | 47 | .time, 48 | .sending { 49 | color: var(--md-sys-color-secondary); 50 | font-size: 0.875rem; 51 | } 52 | 53 | &:hover { 54 | cursor: pointer; 55 | } 56 | 57 | .sending { 58 | margin-left: 4px; 59 | } 60 | } 61 | 62 | .image-container .media-item { 63 | box-sizing: border-box; 64 | aspect-ratio: 1 / 1; 65 | overflow: hidden; 66 | display: flex; 67 | align-items: center; 68 | position: relative; 69 | transition: padding 0.3s ease-in-out; 70 | .duration { 71 | background: rgba(0, 0, 0, 0.4); 72 | color: #fff; 73 | position: absolute; 74 | bottom: 0; 75 | right: 0; 76 | font-size: 12px; 77 | padding: 2px 8px; 78 | border-radius: 8px 0 8px 0; 79 | } 80 | } -------------------------------------------------------------------------------- /src/styles/_form.scss: -------------------------------------------------------------------------------- 1 | .form-label { 2 | display: block; 3 | margin-block-end: 16px; 4 | font-weight: bold; 5 | } 6 | 7 | .form-row { 8 | display: flex; 9 | flex-direction: row; 10 | gap: 16px; 11 | margin-block-end: 16px; 12 | align-items: center; 13 | 14 | &>* { 15 | flex: 1; 16 | } 17 | 18 | .flex-2 { 19 | flex: 2; 20 | } 21 | 22 | .flex-3 { 23 | flex: 3; 24 | } 25 | } 26 | 27 | .form-control { 28 | width: 100%; 29 | } 30 | 31 | .form-check-label { 32 | display: inline-flex; 33 | place-items: center; 34 | } 35 | 36 | .btn-icon { 37 | align-items: center; 38 | background: none; 39 | border: none; 40 | box-sizing: border-box; 41 | cursor: pointer; 42 | display: inline-flex; 43 | justify-content: center; 44 | outline: none; 45 | padding: 0px; 46 | position: relative; 47 | text-decoration: none; 48 | user-select: none; 49 | z-index: 0; 50 | border-radius: 50%; 51 | width: 40px; 52 | height: 40px; 53 | flex: 0 0 40px; // Fix width in flex layout 54 | color: currentColor; 55 | 56 | *:is(svg) { 57 | height: 24px; 58 | width: 24px; 59 | } 60 | 61 | &.no-click { 62 | cursor: default; 63 | } 64 | &:disabled { 65 | cursor: default; 66 | md-ripple { 67 | display: none; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/styles/_md.scss: -------------------------------------------------------------------------------- 1 | md-dialog { 2 | min-width: 360px; 3 | } 4 | 5 | md-outlined-select::part(menu) { 6 | max-height: 50vh; 7 | z-index: 999; 8 | } 9 | 10 | md-chip-set { 11 | align-items: center; 12 | } 13 | 14 | .outlined-button.btn-sm, 15 | .filled-button.btn-sm { 16 | --_label-text-size: 0.75rem; 17 | height: 32px; 18 | --_container-height: 32px; 19 | --_leading-space: 16px; 20 | --_trailing-space: 16px; 21 | } 22 | 23 | .outlined-button.btn-lg, 24 | .filled-button.btn-lg { 25 | --_label-text-size: 1rem; 26 | --_container-height: 56px; 27 | height: 56px; 28 | --_leading-space: 24px; 29 | --_trailing-space: 24px; 30 | } 31 | 32 | md-checkbox { 33 | border-radius: 4px; 34 | } 35 | 36 | .btn-icon { 37 | md-circular-progress { 38 | --md-circular-progress-size: 26px; 39 | --md-circular-progress-active-indicator-width: 16; 40 | } 41 | } 42 | 43 | .filled-button { 44 | md-circular-progress { 45 | --md-circular-progress-size: 18px; 46 | --md-circular-progress-active-indicator-width: 16; 47 | --_active-indicator-color: var(--md-sys-color-on-primary); 48 | } 49 | } -------------------------------------------------------------------------------- /src/styles/_mobile.scss: -------------------------------------------------------------------------------- 1 | @media (max-width: 767px) { 2 | } -------------------------------------------------------------------------------- /src/styles/_pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination { 2 | display: flex; 3 | justify-content: center; 4 | gap: 8px; 5 | padding-bottom: 24px; 6 | .btn-icon { 7 | font-size: 1.125rem; 8 | } 9 | .btn-icon.active { 10 | background-color: var(--md-sys-color-primary); 11 | color: var(--md-sys-color-on-primary); 12 | } 13 | .page-link { 14 | display: flex; 15 | align-items: center; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/styles/_print.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | #header, 3 | .quick, 4 | .quick-content, 5 | .top-app-bar, 6 | .sidebar, 7 | .sidebar2 { 8 | display: none !important; 9 | } 10 | 11 | .layout, .page-content, .page-content .main { 12 | height: auto !important; 13 | } 14 | 15 | .page-content .main { 16 | margin: 0 !important; 17 | border-top-left-radius: 0 !important; 18 | border-top-right-radius: 0 !important; 19 | padding: 0 !important; 20 | } 21 | .md-container { 22 | padding: 0 !important; 23 | } 24 | } -------------------------------------------------------------------------------- /src/styles/_selectable.scss: -------------------------------------------------------------------------------- 1 | .selectable-card { 2 | box-sizing: border-box; 3 | background: var(--md-sys-color-surface-container-low); 4 | color: var(--md-sys-color-on-surface); 5 | &.selecting { 6 | background: var(--pl-selecting-container-color) !important; 7 | } 8 | &.selected { 9 | background: var(--md-sys-color-surface-container-highest); 10 | } 11 | &:hover { 12 | background: var(--md-sys-color-surface-container-high); 13 | } 14 | } -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_AMS-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_AMS-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_AMS-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_AMS-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_AMS-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_AMS-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Bold.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Bold.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Bold.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Caligraphic-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Caligraphic-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Bold.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Bold.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Bold.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Fraktur-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Fraktur-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Bold.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Bold.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Bold.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-BoldItalic.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-BoldItalic.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Italic.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Italic.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Italic.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Main-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Main-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-BoldItalic.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-BoldItalic.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-BoldItalic.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-Italic.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-Italic.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Math-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Math-Italic.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Bold.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Bold.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Bold.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Italic.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Italic.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Italic.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_SansSerif-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_SansSerif-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Script-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Script-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Script-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Script-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Script-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Script-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size1-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size1-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size1-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size1-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size1-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size1-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size2-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size2-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size2-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size2-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size2-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size2-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size3-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size3-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size3-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size3-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size3-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size3-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size4-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size4-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size4-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size4-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Size4-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Size4-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Typewriter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Typewriter-Regular.ttf -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Typewriter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Typewriter-Regular.woff -------------------------------------------------------------------------------- /src/styles/fonts/KaTeX_Typewriter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/KaTeX_Typewriter-Regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/noto-sans-sc/regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/noto-sans-sc/regular.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/noto-sans-sc/w-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/noto-sans-sc/w-500.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/noto-sans-sc/w-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/noto-sans-sc/w-600.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/noto-sans-sc/w-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/noto-sans-sc/w-700.woff2 -------------------------------------------------------------------------------- /src/styles/fonts/noto-sans-sc/w-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ismartcoding/plain-web/aa5e0b0e21b5a97797e13f34ef02336737af1c44/src/styles/fonts/noto-sans-sc/w-800.woff2 -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | import 'vue-router' 2 | 3 | declare module 'vue-router' { 4 | interface RouteMeta { 5 | group?: string 6 | requiresAuth?: boolean 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/views/JsonViewerView.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 47 | 57 | -------------------------------------------------------------------------------- /src/views/QrCodeGeneratorView.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | 62 | -------------------------------------------------------------------------------- /src/views/audios/AudiosSidebar.vue: -------------------------------------------------------------------------------- 1 | 4 | 8 | -------------------------------------------------------------------------------- /src/views/images/ImagesSidebar.vue: -------------------------------------------------------------------------------- 1 | 4 | 8 | -------------------------------------------------------------------------------- /src/views/videos/VideosSidebar.vue: -------------------------------------------------------------------------------- 1 | 4 | 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["./src/*"] 8 | }, 9 | "lib": ["ESNext", "dom", "dom.iterable"] 10 | }, 11 | "plugins": [ 12 | { 13 | "name": "ts-lit-plugin", 14 | "strict": true 15 | } 16 | ], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.vite-config.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.vite-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.json", 3 | "include": ["vite.config.*"], 4 | "compilerOptions": { 5 | "declaration": true, 6 | "noEmit": false, 7 | "composite": true, 8 | "types": ["node"] 9 | } 10 | } 11 | --------------------------------------------------------------------------------