├── .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 |
2 |
12 |
13 |
29 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | {{ $t(`page_title.${getRouteName(item)}`) }}
5 |
6 | - {{ typeof current === 'function' ? current() : current }}
7 |
8 |
9 |
24 |
47 |
--------------------------------------------------------------------------------
/src/components/BucketFilter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ item.name }}{{ item.itemCount.toLocaleString() }}
5 |
6 |
7 |
8 |
60 |
--------------------------------------------------------------------------------
/src/components/ConfirmModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ title || $t('confirm') }}
5 |
6 |
9 |
10 |
11 | {{ $t('ok') }}
12 |
13 |
14 |
15 |
16 |
28 |
--------------------------------------------------------------------------------
/src/components/DeleteFileConfirm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | {{ $t('cancel') }}
8 |
9 | {{ $t('delete') }}
10 |
11 |
12 |
13 |
14 |
54 |
--------------------------------------------------------------------------------
/src/components/DeleteItemsConfirm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | {{ $t('cancel') }}
8 | {{ $t('delete') }}
9 |
10 |
11 |
12 |
44 |
--------------------------------------------------------------------------------
/src/components/EditToolbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 | {{ item.startsWith('t:') ? $t(item.slice(2)) : item }}
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
33 |
--------------------------------------------------------------------------------
/src/components/FeatureButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $t(props.name) }}
6 | {{ count.toLocaleString() }}
7 |
8 |
9 |
10 |
25 |
--------------------------------------------------------------------------------
/src/components/FieldId.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ id }}
4 |
5 | {{ raw }}
6 |
7 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/src/components/IconButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/src/components/ImageVideoListSkeleton.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
31 |
--------------------------------------------------------------------------------
/src/components/ItemTags.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | #{{ tag.name }}
4 |
5 |
8 |
9 |
10 |
40 |
41 |
50 |
--------------------------------------------------------------------------------
/src/components/LeftSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
49 |
--------------------------------------------------------------------------------
/src/components/PromptModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ title }}
5 |
6 |
7 |
8 |
9 |
10 | {{ $t('cancel') }}
11 |
12 | {{ $t('ok') }}
13 |
14 |
15 |
16 |
17 |
52 |
57 |
--------------------------------------------------------------------------------
/src/components/ThemeChanger.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
14 |
19 |
20 |
21 |
22 |
23 |
35 |
36 |
41 |
--------------------------------------------------------------------------------
/src/components/buttons/FilledButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/buttons/OutlinedButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
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 |
2 |
8 |
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 |
2 |
3 |
4 |
5 |
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 |
2 |
3 |
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 |
2 |
3 |
4 | {{ message }}
5 |
6 |
7 |
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 |
2 |
3 |
{{ $t('json_viewer') }}
4 |
5 | {{ $t('expand_all') }}
6 | {{ $t('collapse_all') }}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
47 |
57 |
--------------------------------------------------------------------------------
/src/views/QrCodeGeneratorView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ $t('qrcode_generator') }}
5 |
6 |
7 |
8 |
9 |
10 |
![]()
11 |
12 |
13 |
14 |
15 |
43 |
62 |
--------------------------------------------------------------------------------
/src/views/audios/AudiosSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/src/views/images/ImagesSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/src/views/videos/VideosSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
--------------------------------------------------------------------------------