Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 5cf4c6b7c0 |
7346 changed files with 135019 additions and 699223 deletions
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
run: npx cap copy electron
|
||||
|
||||
- name: Build
|
||||
run: mkdir -p build && cd build && npx electron-packager ../electron miarven --platform=linux --arch=x64 --icon=../icons/icon.png --overwrite --asar && cp ../icons/icon.png ./miarven-linux-x64/icon.png && cp ../assets/olcxja.miarven.desktop ./miarven-linux-x64/olcxja.miarven.desktop && cp ../assets/install.sh ./miarven-linux-x64/install.sh && chmod +x ./miarven-linux-x64/install.sh
|
||||
run: mkdir -p build && cd build && npx electron-packager ../electron miarven --platform=linux --arch=x64 --icon=../icons/icon.png --overwrite --asar && cp ../icons/icon.png ./miarven-linux-x64/icon.png && cp ../assets/Miarven.desktop ./miarven-linux-x64/Miarven.desktop
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
|
|
|||
2
.npmrc
2
.npmrc
|
|
@ -1,2 +0,0 @@
|
|||
min-release-age=21
|
||||
ignore-scripts=true
|
||||
|
|
@ -7,8 +7,8 @@ android {
|
|||
applicationId "olcxja.miarven"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 8
|
||||
versionName "0.2.2"
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,355 +0,0 @@
|
|||
{
|
||||
"user.not.found": "meowser not found :c",
|
||||
"dm.already.exists": "direct meowchat already exists :3",
|
||||
"cant.invite.urself": "you can't invite yourself, silly :3",
|
||||
"user.already.invited": "you have already invited this user meow",
|
||||
"user.invited": "user invited successfully :3",
|
||||
"cant.create.dm.without.invitation": "you can't create a dm without invitation ><",
|
||||
"dm.begin.notice": "at the beginning of a meowchat, you should always verify that the person you're talking to is the intended recipient",
|
||||
"failed.accept.dm": "failed to accept direct meowchat",
|
||||
"dm.accepted": "direct meowchat accepted",
|
||||
"no.invite.found": "you can't create a dm without invitation *hiss*",
|
||||
"unknown.error": "unknown error :o",
|
||||
"missing.keys": "cannot rotate nametags: some kitties don't have public nametags available yet. they must set up their identity.",
|
||||
"key.rotated": "meowchat keys have been successfully rotated! meow!",
|
||||
"forbidden": "you do not have pawmission to perform this action *hiss*",
|
||||
"dm.key.too.large": "meowchat key too large :o",
|
||||
"dm.key.not.found": "meowchat key not found :c",
|
||||
"dm.key.updated": "meowchat key updated successfully :3",
|
||||
"body.too.large": "Meow is too big! Max meow is {0} MB.",
|
||||
"message.too.long": "Meowssage exceeds maximum allowed size ({0}).",
|
||||
"reaction.too.long": "Re-meow-ction exceeds maximum allowed size ({0}).",
|
||||
"invite.revoked": "invite revoked successfully",
|
||||
"invite.declined": "invite declined successfully",
|
||||
"account.creation.request.expired": "cat creation request expired. try again",
|
||||
"invalid.username": "invalid cat name: {0}",
|
||||
"invalid.password": "invalid meow word: {0}",
|
||||
"incorrect.captcha": "incorrect captcha. try meowgain",
|
||||
"username.taken": "this cat name is already taken",
|
||||
"accounts.slots.full": "you can't create a new cat because all collars are used. try again later",
|
||||
"registration.disabled": "registration disabled",
|
||||
"account.created": "cat created successfully",
|
||||
"password.changed": "meow word changed successfully",
|
||||
"keys.updated": "nametag updated successfully",
|
||||
"unknown.request": "unknown request: {0}",
|
||||
"login.successful": "pawgin successful :3",
|
||||
"username.length": "cat name must be {0} characters long",
|
||||
"username.conditions.allowed": "cat name can only include {all}",
|
||||
"password.not.hashed.properly": "meow word is not hashed properly",
|
||||
"invalid.username.or.password": "invalid cat name or meow word",
|
||||
"error.storage.limit.exceeded": "Storage Limit Exceeded, purr",
|
||||
"error.network.error": "Network error, check connection or file size, meow.",
|
||||
"account.not.exist": "cat with this name doesn't exist",
|
||||
"invalid.nonce": "invalid nonce. try again",
|
||||
"username.changed": "cat name changed successfully",
|
||||
"auth.failed.redirect.to.login": "failed to find owner. redirecting to pawgin...",
|
||||
"dm.refresh.failed": "failed to refresh direct meowchats",
|
||||
"chat.add.failed": "failed to add meowchat",
|
||||
"something.wrong.mayb.pass": "something went wrong... :c (probably wrong meow word)",
|
||||
"something.wrong": "something went wrong... :c",
|
||||
"passwords.not.match": "meow word do not match",
|
||||
"password.cant.empty": "meow word cannot be empty",
|
||||
"username.cant.empty": "cat name cannot be empty",
|
||||
"bad.request": "bad request!!!",
|
||||
"letters": "letters",
|
||||
"numbers": "numbers",
|
||||
"underscores": "underscores",
|
||||
"loading.connecting": "connecting...",
|
||||
"loading.loading": "loading...",
|
||||
"loading.done": "ready! :3",
|
||||
"title.home": "bed",
|
||||
"title.dms": "direct meowchats",
|
||||
"title.groups": "clowder",
|
||||
"title.welcome.splash": "welpaw to Meowven",
|
||||
"desc.welcome.splash": "first Larpix client",
|
||||
"title.splash": "splash",
|
||||
"title.create.dm": "invite to direct meowchat",
|
||||
"title.create.space": "create house",
|
||||
"title.create.group": "create clowder",
|
||||
"title.add.chat": "add meowchat",
|
||||
"desc.add.chat": "add a private, encrypted meowchat by entering a cat name",
|
||||
"desc.create.group": "create a private, encrypted clowder",
|
||||
"placeholder.create.group.input": "my clowder",
|
||||
"placeholder.create.space.input": "my house",
|
||||
"title.name": "name",
|
||||
"desc.create.space": "create a house for your community",
|
||||
"desc.join.space": "join a community that fits you",
|
||||
"title.join.space": "join house",
|
||||
"title.space.id": "house id",
|
||||
"title.inbox": "inbox",
|
||||
"title.invites": "invites",
|
||||
"title.notifications": "notifications",
|
||||
"title.received": "received",
|
||||
"title.sent": "sent",
|
||||
"title.all": "all",
|
||||
"title.unread": "unread",
|
||||
"desc.no.invites": "no invites found :c",
|
||||
"desc.no.notifications": "no notifications found :c",
|
||||
"desc.fetching.invites": "fetching invites...",
|
||||
"desc.fetching.notifications": "fetching notifications...",
|
||||
"desc.invite.dm.received": "{0} ({1}) invited you to meowchat",
|
||||
"desc.invite.dm.sent": "you invited {0} ({1}) to meowchat",
|
||||
"desc.invite.group.received": "{0} ({1}) invited you to a clowder",
|
||||
"desc.invite.group.sent": "you invited {0} ({1}) to a clowder",
|
||||
"action.invite.sending": "Sending mewvite...",
|
||||
"action.processing": "Purrcessing...",
|
||||
"action.fetching.invites.sent": "Fetching sent mewvites...",
|
||||
"action.fetching.invites.recv": "fetching received invites...",
|
||||
"action.dm.fetch": "fetching direct meowchats...",
|
||||
"action.auth": "authenticating...",
|
||||
"action.dm.adding": "adding...",
|
||||
"action.invite.revoking": "revoking invite...",
|
||||
"action.invite.accepting": "accepting invite...",
|
||||
"action.invite.declining": "declining invite...",
|
||||
"title.sign.up": "sign up",
|
||||
"title.sign.in": "sign in",
|
||||
"title.sign.in.to": "sign in to your larpix instance",
|
||||
"title.server.host": "server host",
|
||||
"title.username": "meow name",
|
||||
"title.password": "meow word",
|
||||
"title.new.here.?": "new here?",
|
||||
"title.create.an.account": "create a cat",
|
||||
"join.larpix": "join Larpix",
|
||||
"title.create.new.account": "create new larpix cat account",
|
||||
"notice.use.strong.pass": "remember to use a strong meow word! tt will be used to lock your collar",
|
||||
"title.confirm.password": "confirm meow word",
|
||||
"title.already.registered.?": "already a kitty?",
|
||||
"title.back.to.login": "back to pawgin",
|
||||
"title.verify": "verify",
|
||||
"title.captcha.desc": "prove you are a kitty",
|
||||
"title.captcha.code": "captcha code",
|
||||
"title.invitation.code": "invitation code",
|
||||
"title.back.to.register": "back to registration",
|
||||
"title.details": "details",
|
||||
"title.members": "cats",
|
||||
"placeholder.username": "cat name",
|
||||
"placeholder.captcha.code": "captcha code",
|
||||
"desc.messages.loading": "loading meows...",
|
||||
"desc.no.dms": "no direct meowchats :c",
|
||||
"action.dm.opening": "opening meowchat...",
|
||||
"action.group.opening": "opening clowder...",
|
||||
"dm.open.failed": "failed to open meowchat :c",
|
||||
"keys.local.server.mismatch": "this device has different nametags than server :c use the device u first logged in on",
|
||||
"keys.server.decrypt.failed": "wrong password for ur secret nametag bundle :c",
|
||||
"dm.messages.fetch.failed": "failed to fetch meows :c",
|
||||
"messages.decrypt.failed": "failed to decrypt meow :c",
|
||||
"title.reply.message": "meow back",
|
||||
"title.react.message": "purr",
|
||||
"title.text.react": "text purr",
|
||||
"title.delete.message": "hiss away",
|
||||
"title.replied.to": "meowed back to",
|
||||
"title.replying.to": "meowing back to",
|
||||
"action.message.deleting": "hissing message away...",
|
||||
"action.message.sending": "sending meow...",
|
||||
"action.message.reacting": "adding purr...",
|
||||
"error:message.delete.failed": "failed to hiss away meow",
|
||||
"error:message.send.failed": "failed to send meow",
|
||||
"error:message.react.failed": "failed to add purr",
|
||||
"info.sending.message": "sending...",
|
||||
"placeholder.message.input": "meow...",
|
||||
"larp.redacted": "Meow meow was meowed.",
|
||||
"message.already.deleted": "This meow has already been meowed.",
|
||||
"rate.limit": "You are meowing too quickly. Please meow again later.",
|
||||
"placeholder.invitation.code": "meow meow meow",
|
||||
"info.messages.loading.older": "Loading older meowsages...",
|
||||
"error:message.not.found": "Meowsage not found",
|
||||
"title.profile": "cat profile",
|
||||
"title.settings": "cat settings",
|
||||
"title.logout": "log out",
|
||||
"title.loading": "sniffing...",
|
||||
"action.edit.profile": "edit cat profile",
|
||||
"action.open.dm": "open direct meowchat",
|
||||
"action.invite.accept": "accept meow invite",
|
||||
"action.invite.cancel": "hiss away invite",
|
||||
"action.invite.send": "invite to meowchat",
|
||||
"action.profile.loading": "sniffing profile...",
|
||||
"error.profile.load.failed": "failed to sniff profile.",
|
||||
"unknown.author": "somecat",
|
||||
"title.account": "Cat",
|
||||
"title.delete.account": "Delete cat",
|
||||
"desc.delete.account": "Once you delete your cat, it will go to cat heaven. Please be certain :c",
|
||||
"title.avatar": "Cat picture",
|
||||
"title.banner": "Cat banner",
|
||||
"title.bio": "Cat bio",
|
||||
"title.appearance": "Appearance",
|
||||
"title.language": "Meownguage",
|
||||
"action.save.profile": "Save cat profile",
|
||||
"action.save.bio": "Save meow-io",
|
||||
"action.uploading": "Uploading...",
|
||||
"action.bio.saving": "Saving meow-io...",
|
||||
"action.account.saving": "Saving cat...",
|
||||
"action.account.deleting": "Deleting cat...",
|
||||
"success:profile.updated": "Cat profile updated successfully :3",
|
||||
"success:avatar.updated": "Cat picture updated successfully :3",
|
||||
"success:banner.updated": "Cat banner updated successfully :3",
|
||||
"action.save": "Save :3",
|
||||
"action.delete.account": "Delete cat",
|
||||
"account.deleted.scheduled": "Cat scheduled for deletion. It will go to cat heaven after a month of inactivity. Pawing in again will cancel the deletion process :3",
|
||||
"action.attachment.take.photo": "Meow photo",
|
||||
"action.attachment.media": "Meowdia",
|
||||
"action.attachment.file": "Meowle",
|
||||
"action.attachment.poll": "Meowll",
|
||||
"action.attachment.uploading": "Uploading meowttachments...",
|
||||
"attachment.legacy": "Legacy meowttachment",
|
||||
"warning.unencrypted": "This meowsage is not encrypted",
|
||||
"action.change.avatar": "Change Cat Picture",
|
||||
"action.change.banner": "Change Cat Banner",
|
||||
"placeholder.type.text.emoji": "Type purr / meow...",
|
||||
"action.cancel": "Cancel",
|
||||
"title.crop.image": "Crop Image",
|
||||
"title.zoom": "Zoom",
|
||||
"title.pin.message": "Pin",
|
||||
"title.unpin.message": "Unpin",
|
||||
"message.pinned.by": "{0} pinned a {1}.",
|
||||
"message.unpinned.by": "{0} unpinned a {1}.",
|
||||
"title.pinned.messages": "Pinned",
|
||||
"error.pinned.messages.empty": "No pinned meows",
|
||||
"title.message.lowercase": "meowsage",
|
||||
"action.rotate.keys": "Meowtate Keys",
|
||||
"already.in.group": "Meowready in group",
|
||||
"desc.key.rotation.info": "Meow need to purrotate meowcription keys to purrtinue sending meowsages in this group. Other meowmbers will autopurrtically receive the new key.",
|
||||
"desc.no.groups": "Meow are not in any groups yet.",
|
||||
"dm.read": "Purread",
|
||||
"empty.body": "Empty meowdy",
|
||||
"failed.create.group": "Failed to purreate group",
|
||||
"federation.not.implemented": "Meowderation not implemeowted",
|
||||
"group.not.found": "Group not meownd",
|
||||
"group.read": "Purread",
|
||||
"invalid.entry.name": "Invalid purrtry name",
|
||||
"invalid.key.index": "Invalid key index (purrlease purrefresh the page)",
|
||||
"invalid.msg.id": "Invalid meowsage ID",
|
||||
"invalid.payload": "Invalid purrload",
|
||||
"invalid.reaction.id": "Invalid purreaction ID",
|
||||
"invalid.user": "Invalid meowser",
|
||||
"joined": "Purroined",
|
||||
"key.rotation.required": "Key purrotation meowquired",
|
||||
"key.too.large": "Key too meowrge",
|
||||
"keys.public.mismatch": "Public keys mismemeow",
|
||||
"label.members": "MEOWMBERS",
|
||||
"message.deleted": "Meowsage purrleted",
|
||||
"message.not.found": "Meowsage not meownd",
|
||||
"message.pin.toggled": "Meowsage purrin purrggled",
|
||||
"message.reacted": "Meowsage purreacted",
|
||||
"message.reaction.removed": "Purreaction remeowed",
|
||||
"no.invite": "No meowvite",
|
||||
"not.a.member": "Not a meowmber",
|
||||
"removed.from.group": "You got hissed out of the clowder",
|
||||
"not.in.group": "Not in purroup",
|
||||
"reaction.not.found": "Purreaction not meownd",
|
||||
"rotation.not.required": "Purrotation not meowquired",
|
||||
"storage.entry.updated": "Storage purrtry updated",
|
||||
"storage.limit.exceeded": "Storage limit exmeowded",
|
||||
"title.key.rotation": "Key Purrotation",
|
||||
"error:messages.decrypt.failed": "Depurryption meowled",
|
||||
"error.message.already.deleted": "Meowsage alpurrdy meowleted",
|
||||
"group.user.joined.by": "{0} purroined the group",
|
||||
"group.created.by": "Group purreated by {0}",
|
||||
"group.user.left.by": "{0} left the purroup",
|
||||
"action.group.fetch": "Meowtching groups",
|
||||
"group.created": "Group purreated",
|
||||
"group.user.joined": "Purroined group",
|
||||
"prompt.invite.username": "Enter username to invite meow:",
|
||||
"desc.failed.load.members": "Failed to load members meow",
|
||||
"prompt.are.you.sure": "Are you sure meow?",
|
||||
"title.unknown.group": "Unknown Group meow",
|
||||
"title.unknown": "Unknown meow",
|
||||
"en-us": "English meow",
|
||||
"pl-pl": "Polish meow",
|
||||
"en-cat": "Meowlish",
|
||||
"avatar.updated": "Avatar updated successfully",
|
||||
"profile.updated": "Profile updated successfully",
|
||||
"banner.updated": "Banner updated successfully",
|
||||
"action.invite": "Invite, meow",
|
||||
"action.leaving.group": "Leaving group, meow...",
|
||||
"action.key.rotating": "Rotating keys, meow...",
|
||||
"action.key.rotation": "Key rotation, meow",
|
||||
"desc.leave.group.confirm": "Are you sure you want to leave this group, meow?",
|
||||
"desc.key.rotation.you": "You will generate a new encryption key for this group, meow.",
|
||||
"label.keys.to.encrypt": "keys to encrypt, meow",
|
||||
"action.group.creating": "Creating group, meow...",
|
||||
"title.group.settings": "Group settings, meow",
|
||||
"title.leave.group": "Leave group, meow",
|
||||
"role.member": "Meowber",
|
||||
"role.group_owner": "Big purr",
|
||||
"role.Owner": "Big purr",
|
||||
"action.kick": "Hiss and scratch from group",
|
||||
"action.transfer_owner": "Give shiny toy",
|
||||
"group.user.kicked.by": "{0} hissed at {1} and they ran away",
|
||||
"group.owner.transferred.by": "{0} gave the shiny toy to {1}",
|
||||
"group.user.kicked": "Kitty kicked",
|
||||
"group.owner.transferred": "Shiny toy given",
|
||||
"success:kicked": "Hissed away successfully",
|
||||
"success:owner.transferred": "Toy transferred",
|
||||
"error:cannot.kick.self": "You can't scratch yourself",
|
||||
"action.saving": "Meowving...",
|
||||
"desc.no.chat.selected": "No purr selected. Select a purr from the sidebar to start meowing.",
|
||||
"failed.to.prepare.keys": "Failed to prepare meows",
|
||||
"group.name.empty": "Purr name cannot be empty",
|
||||
"group.refresh.failed": "Failed to refresh purr",
|
||||
"group.settings.saved": "Purr settings saved successfully",
|
||||
"kicked": "Bited successfully",
|
||||
"message.delete.failed": "Failed to delete meow",
|
||||
"message.react.failed": "Failed to react to meow",
|
||||
"message.send.failed": "Failed to send meow",
|
||||
"owner.transferred": "Purrship transferred successfully",
|
||||
"title.voice.chat": "voice chat :3",
|
||||
"status.voice.connecting": "connecting... :3",
|
||||
"status.voice.disconnected": "disconnected :3",
|
||||
"action.call.mute": "mute microphone",
|
||||
"action.call.camera": "camera",
|
||||
"action.call.screen": "share screen",
|
||||
"action.call.disconnect": "disconnect :3",
|
||||
"action.call.speaker": "speaker :3",
|
||||
"action.call.join": "join :3",
|
||||
"action.call.decline": "decline :c",
|
||||
"title.incoming.call": "incoming voice chat :3",
|
||||
"desc.call.in.chat": "{0} in chat :3",
|
||||
"label.user.volume": "user volume",
|
||||
"label.stream.volume": "screen volume",
|
||||
"error.call.unsupported": "no permission or not supported: {0} :c",
|
||||
"error.call.camera": "camera error: {0} :c",
|
||||
"error.call.screen": "screen share error: {0} :c",
|
||||
"error.call.screen.unsupported": "ur browser doesn't support screen sharing :c",
|
||||
"label.stream.cam": "cam",
|
||||
"label.stream.screen": "screen",
|
||||
"action.call.settings": "voice settings :3",
|
||||
"label.voice.microphone": "microphone",
|
||||
"label.voice.speaker": "speaker",
|
||||
"label.voice.camera": "camera",
|
||||
"label.voice.camera.quality": "camera quality",
|
||||
"label.voice.camera.fps": "camera fps",
|
||||
"label.voice.screen.quality": "screen quality",
|
||||
"label.voice.screen.fps": "screen fps",
|
||||
"label.voice.sensitivity": "input sensitivity",
|
||||
"label.voice.default": "default",
|
||||
"error.call.microphone": "microphone error :c",
|
||||
"error.voice.reconnect.failed": "voice connection lost :c",
|
||||
"error.voice.ticket.rejected": "voice auth failed :c check VOICE_KEY/server version",
|
||||
"error.voice.not.configured": "voice server not configured :c missing VOICE_KEY",
|
||||
"title.group.name": "clowder name",
|
||||
"title.group.history.visible": "history visible to newcomers",
|
||||
"desc.group.history.visible": "new clowder meowmbers will be able to see older meowsages (disables key purrotation)",
|
||||
"history.visible": "enabled, meow",
|
||||
"history.hidden": "disabled, meow",
|
||||
"group.history.changed.by": "{0} changed history visibility to: {1}",
|
||||
"group.name.changed.by": "{0} changed the clowder name to {1}",
|
||||
"group.pfp.changed.by": "{0} changed the clowder picture",
|
||||
"group.pfp.removed.by": "{0} removed the clowder picture",
|
||||
"action.call.init": "starting call... :3",
|
||||
"already.in.call": "ur already in a call meow",
|
||||
"call.connect.failed": "failed to connect to the call :c",
|
||||
"call.microphone": "microphone error :c",
|
||||
"voice.reconnect.failed": "voice connection lost :c",
|
||||
"voice.ticket.rejected": "voice auth failed :c check VOICE_KEY/server version",
|
||||
"kdf.salt.missing": "missing encryption salt :c try pawging in again",
|
||||
"group-avatar.updated": "clowder picture updated successfully :3",
|
||||
"error.unknown.error": "unknown error :o",
|
||||
"federation.disabled": "federation is disabled :c",
|
||||
"invalid.name": "invalid name meow",
|
||||
"invalid.keys": "invalid nametags meow",
|
||||
"history.hidden.keys.provided": "no nametags allowed when history is hissed away, meow",
|
||||
"cannot.kick.self": "you can't scratch yourself",
|
||||
"invalid.attachment": "invalid meowttachment :c",
|
||||
"no.voice.servers": "no voice servers available :c",
|
||||
"voice.not.configured": "voice server not configured :c missing VOICE_KEY"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"en-us": { "english": "English (US)", "native": "English (US)" },
|
||||
"en-cat": { "english": "English (Cat)", "native": "Meowlish" },
|
||||
"pl-pl": { "english": "Polish", "native": "Polski" }
|
||||
}
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
{
|
||||
"user.not.found": "Nie znaleziono użytkownika",
|
||||
"dm.already.exists": "Wiadomość prywatna już istnieje",
|
||||
"cant.invite.urself": "Nie możesz zaprosić samego siebie",
|
||||
"user.already.invited": "Ten użytkownik został już przez Ciebie zaproszony",
|
||||
"user.invited": "Użytkownik został pomyślnie zaproszony",
|
||||
"cant.create.dm.without.invitation": "Nie możesz utworzyć wiadomości prywatnej bez zaproszenia",
|
||||
"dm.begin.notice": "Na początku każdej rozmowy prywatnej zawsze upewnij się, że rozmawiasz z właściwą osobą",
|
||||
"failed.accept.dm": "Nie udało się zaakceptować rozmowy prywatnej",
|
||||
"dm.accepted": "Rozmowa prywatna została zaakceptowana",
|
||||
"no.invite.found": "Nie możesz utworzyć wiadomości prywatnej bez zaproszenia",
|
||||
"unknown.error": "Nieznany błąd",
|
||||
"forbidden": "Nie masz uprawnień do wykonania tej akcji",
|
||||
"dm.key.too.large": "Klucz wiadomości prywatnej jest zbyt duży",
|
||||
"dm.key.not.found": "Nie znaleziono klucza wiadomości prywatnej",
|
||||
"dm.key.updated": "Klucz wiadomości prywatnej został pomyślnie zaktualizowany",
|
||||
"body.too.large": "Treść żądania jest zbyt duża. Maksymalny limit to {0} MB",
|
||||
"message.too.long": "Wiadomość przekracza dozwolony rozmiar ({0}).",
|
||||
"reaction.too.long": "Reakcja przekracza dozwolony rozmiar ({0}).",
|
||||
"invite.revoked": "Zaproszenie zostało cofnięte",
|
||||
"invite.declined": "Zaproszenie zostało odrzucone",
|
||||
"account.creation.request.expired": "Żądanie utworzenia konta wygasło. Spróbuj ponownie",
|
||||
"invalid.username": "Nieprawidłowa nazwa użytkownika: {0}",
|
||||
"invalid.password": "Nieprawidłowe hasło: {0}",
|
||||
"incorrect.captcha": "Nieprawidłowa captcha. Spróbuj ponownie",
|
||||
"username.taken": "Ta nazwa użytkownika jest już zajęta",
|
||||
"accounts.slots.full": "Nie można utworzyć nowego konta, ponieważ wszystkie identyfikatory są zajęte. Spróbuj ponownie później",
|
||||
"registration.disabled": "Rejestracja jest wyłączona",
|
||||
"account.created": "Konto zostało pomyślnie utworzone",
|
||||
"password.changed": "Hasło zostało pomyślnie zmienione",
|
||||
"keys.updated": "Klucze zostały pomyślnie zaktualizowane",
|
||||
"unknown.request": "Nieznane żądanie: {0}",
|
||||
"login.successful": "Logowanie zakończone pomyślnie",
|
||||
"username.length": "Nazwa użytkownika musi mieć {0} znaków",
|
||||
"username.conditions.allowed": "Nazwa użytkownika może zawierać tylko: {all}",
|
||||
"password.not.hashed.properly": "Hasło nie zostało poprawnie zahaszowane",
|
||||
"invalid.username.or.password": "Nieprawidłowa nazwa użytkownika lub hasło",
|
||||
"error.storage.limit.exceeded": "Przekroczono limit pamięci",
|
||||
"error.network.error": "Błąd sieci. Sprawdź połączenie lub rozmiar pliku.",
|
||||
"account.not.exist": "Konto o tej nazwie nie istnieje",
|
||||
"invalid.nonce": "Nieprawidłowy nonce. Spróbuj ponownie",
|
||||
"username.changed": "Nazwa użytkownika została pomyślnie zmieniona",
|
||||
"auth.failed.redirect.to.login": "Uwierzytelnianie nie powiodło się. Przekierowywanie do logowania...",
|
||||
"dm.refresh.failed": "Nie udało się odświeżyć rozmów prywatnych",
|
||||
"chat.add.failed": "Nie udało się dodać czatu",
|
||||
"something.wrong.mayb.pass": "Coś poszło nie tak... (prawdopodobnie błędne hasło)",
|
||||
"something.wrong": "Coś poszło nie tak...",
|
||||
"passwords.not.match": "Hasła nie są zgodne",
|
||||
"password.cant.empty": "Hasło nie może być puste",
|
||||
"username.cant.empty": "Nazwa użytkownika nie może być pusta",
|
||||
"bad.request": "Nieprawidłowe żądanie",
|
||||
"letters": "litery",
|
||||
"numbers": "cyfry",
|
||||
"underscores": "podkreślenia",
|
||||
"loading.connecting": "Łączenie...",
|
||||
"loading.loading": "Ładowanie...",
|
||||
"loading.done": "Gotowe!",
|
||||
"title.home": "Strona główna",
|
||||
"title.dms": "Wiadomości prywatne",
|
||||
"title.groups": "Grupy",
|
||||
"title.welcome.splash": "Witamy w Miarven",
|
||||
"desc.welcome.splash": "Pierwszy klient Larpix",
|
||||
"title.splash": "Ekran powitalny",
|
||||
"title.create.dm": "Zaproś do rozmowy prywatnej",
|
||||
"title.create.space": "Utwórz społeczność",
|
||||
"title.create.group": "Utwórz grupę",
|
||||
"title.add.chat": "Dodaj czat",
|
||||
"desc.add.chat": "Dodaj prywatny, szyfrowany czat, wpisując nazwę użytkownika",
|
||||
"desc.create.group": "Utwórz prywatną, szyfrowaną grupę",
|
||||
"placeholder.create.group.input": "Moja grupa",
|
||||
"placeholder.create.space.input": "Moja społeczność",
|
||||
"title.name": "Nazwa",
|
||||
"desc.create.space": "Utwórz społeczność dla swojej grupy",
|
||||
"desc.join.space": "Dołącz do społeczności, która Ci odpowiada",
|
||||
"title.join.space": "Dołącz do społeczności",
|
||||
"title.space.id": "Identyfikator społeczności",
|
||||
"title.inbox": "Skrzynka odbiorcza",
|
||||
"title.invites": "Zaproszenia",
|
||||
"title.notifications": "Powiadomienia",
|
||||
"title.received": "Odebrane",
|
||||
"title.sent": "Wysłane",
|
||||
"title.all": "Wszystkie",
|
||||
"title.unread": "Nieprzeczytane",
|
||||
"desc.no.invites": "Brak zaproszeń",
|
||||
"desc.no.notifications": "Brak powiadomień",
|
||||
"desc.fetching.invites": "Pobieranie zaproszeń...",
|
||||
"desc.fetching.notifications": "Pobieranie powiadomień...",
|
||||
"desc.invite.dm.received": "{0} ({1}) zaprosił(a) Cię do rozmowy",
|
||||
"desc.invite.dm.sent": "Zaprosiłeś(-aś) {0} ({1}) do rozmowy",
|
||||
"desc.invite.group.received": "{0} ({1}) zaprosił(a) Cię do grupy",
|
||||
"desc.invite.group.sent": "Zaprosiłeś(-aś) {0} ({1}) do grupy",
|
||||
"action.invite.sending": "Wysyłanie zaproszenia...",
|
||||
"action.processing": "Przetwarzanie...",
|
||||
"action.fetching.invites.sent": "Pobieranie wysłanych zaproszeń...",
|
||||
"action.fetching.invites.recv": "Pobieranie otrzymanych zaproszeń...",
|
||||
"action.dm.fetch": "Pobieranie rozmów prywatnych...",
|
||||
"action.auth": "Uwierzytelnianie...",
|
||||
"action.dm.adding": "Dodawanie...",
|
||||
"action.invite.revoking": "Cofanie zaproszenia...",
|
||||
"action.invite.accepting": "Akceptowanie zaproszenia...",
|
||||
"action.invite.declining": "Odrzucanie zaproszenia...",
|
||||
"title.sign.up": "Zarejestruj się",
|
||||
"title.sign.in": "Zaloguj się",
|
||||
"title.sign.in.to": "Zaloguj się do swojej instancji Larpix",
|
||||
"title.server.host": "Adres serwera",
|
||||
"title.username": "Nazwa użytkownika",
|
||||
"title.password": "Hasło",
|
||||
"title.new.here.?": "Nowy tutaj?",
|
||||
"title.create.an.account": "Utwórz konto",
|
||||
"join.larpix": "Dołącz do Larpix",
|
||||
"title.create.new.account": "Utwórz nowe konto Larpix",
|
||||
"notice.use.strong.pass": "Pamiętaj o użyciu silnego hasła! Posłuży ono do szyfrowania Twoich kluczy.",
|
||||
"title.confirm.password": "Potwierdź hasło",
|
||||
"title.already.registered.?": "Masz już konto?",
|
||||
"title.back.to.login": "Powrót do logowania",
|
||||
"title.verify": "Weryfikacja",
|
||||
"title.captcha.desc": "Udowodnij, że jesteś człowiekiem",
|
||||
"title.captcha.code": "Kod CAPTCHA",
|
||||
"title.invitation.code": "Kod zaproszenia",
|
||||
"title.back.to.register": "Powrót do rejestracji",
|
||||
"placeholder.username": "nazwa użytkownika",
|
||||
"placeholder.captcha.code": "kod captcha",
|
||||
"placeholder.message.input": "Wiadomość...",
|
||||
"desc.messages.loading": "Pobieranie wiadomości...",
|
||||
"desc.no.dms": "Brak wiadomości prywatnych",
|
||||
"action.dm.opening": "Otwieranie rozmowy...",
|
||||
"action.group.opening": "Otwieranie grupy...",
|
||||
"dm.open.failed": "Nie udało się otworzyć rozmowy",
|
||||
"keys.local.server.mismatch": "To urządzenie ma inne klucze szyfrowania niż serwer. Użyj urządzenia, na którym zalogowano się po raz pierwszy, lub przywróć klucze z kopii zapasowej.",
|
||||
"keys.server.decrypt.failed": "Nie udało się odszyfrować kluczy konta przy użyciu tego hasła.",
|
||||
"dm.messages.fetch.failed": "Nie udało się pobrać wiadomości",
|
||||
"messages.decrypt.failed": "Nie udało się odszyfrować wiadomości",
|
||||
"title.reply.message": "Odpowiedz",
|
||||
"title.react.message": "Zareaguj",
|
||||
"title.text.react": "Reakcja tekstowa",
|
||||
"title.delete.message": "Usuń",
|
||||
"title.replied.to": "W odpowiedzi do",
|
||||
"title.replying.to": "Odpowiadasz na",
|
||||
"action.message.deleting": "Usuwanie wiadomości...",
|
||||
"action.message.sending": "Wysyłanie wiadomości...",
|
||||
"action.message.reacting": "Dodawanie reakcji...",
|
||||
"error:message.delete.failed": "Nie udało się usunąć wiadomości",
|
||||
"error:message.send.failed": "Nie udało się wysłać wiadomości",
|
||||
"error:message.react.failed": "Nie udało się dodać reakcji",
|
||||
"info.sending.message": "Wysyłanie...",
|
||||
"larp.redacted": "Ta wiadomość została usunięta.",
|
||||
"message.already.deleted": "Ta wiadomość została już usunięta.",
|
||||
"rate.limit": "Wysyłasz żądania zbyt szybko. Spróbuj ponownie później.",
|
||||
"placeholder.invitation.code": "kod zaproszenia",
|
||||
"info.messages.loading.older": "Ładowanie starszych wiadomości...",
|
||||
"error:message.not.found": "Nie znaleziono wiadomości",
|
||||
"title.profile": "Profil",
|
||||
"title.settings": "Ustawienia",
|
||||
"title.logout": "Wyloguj",
|
||||
"title.loading": "Ładowanie...",
|
||||
"title.details": "Szczegóły",
|
||||
"title.members": "Członkowie",
|
||||
"action.edit.profile": "Edytuj profil",
|
||||
"action.open.dm": "Otwórz rozmowę",
|
||||
"action.invite.accept": "Akceptuj zaproszenie",
|
||||
"action.invite.cancel": "Anuluj zaproszenie",
|
||||
"action.invite.send": "Zaproś do rozmowy",
|
||||
"action.profile.loading": "Ładowanie profilu...",
|
||||
"error.profile.load.failed": "Nie udało się załadować profilu.",
|
||||
"unknown.author": "Ktoś",
|
||||
"title.account": "Konto",
|
||||
"title.delete.account": "Usuń konto",
|
||||
"desc.delete.account": "Po usunięciu konta nie będzie możliwości jego odzyskania. Upewnij się, że na pewno chcesz to zrobić.",
|
||||
"title.avatar": "Awatar",
|
||||
"title.banner": "Baner",
|
||||
"title.bio": "Opis",
|
||||
"title.appearance": "Wygląd",
|
||||
"title.language": "Język",
|
||||
"action.save.profile": "Zapisz profil",
|
||||
"action.save.bio": "Zapisz opis",
|
||||
"action.uploading": "Przesyłanie...",
|
||||
"action.bio.saving": "Zapisywanie opisu...",
|
||||
"action.account.saving": "Zapisywanie konta...",
|
||||
"action.account.deleting": "Usuwanie konta...",
|
||||
"profile.updated": "Profil został pomyślnie zaktualizowany",
|
||||
"avatar.updated": "Awatar został pomyślnie zaktualizowany",
|
||||
"banner.updated": "Baner został pomyślnie zaktualizowany",
|
||||
"action.save": "Zapisz",
|
||||
"action.delete.account": "Usuń konto",
|
||||
"account.deleted.scheduled": "Konto zostało oznaczone do usunięcia. Zostanie trwale usunięte po miesiącu braku aktywności. Zalogowanie się w tym czasie anuluje proces usuwania.",
|
||||
"action.attachment.take.photo": "Zrób zdjęcie",
|
||||
"action.attachment.media": "Multimedia",
|
||||
"action.attachment.file": "Plik",
|
||||
"action.attachment.poll": "Ankieta",
|
||||
"action.attachment.uploading": "Przesyłanie załączników...",
|
||||
"attachment.legacy": "Starszy załącznik",
|
||||
"warning.unencrypted": "Ta wiadomość nie jest szyfrowana",
|
||||
"action.change.avatar": "Zmień awatar",
|
||||
"action.change.banner": "Zmień baner",
|
||||
"placeholder.type.text.emoji": "Wpisz tekst / emoji...",
|
||||
"action.cancel": "Anuluj",
|
||||
"title.crop.image": "Przytnij obraz",
|
||||
"title.zoom": "Przybliżenie",
|
||||
"title.pin.message": "Przypnij",
|
||||
"title.unpin.message": "Odepnij",
|
||||
"message.pinned.by": "{0} przypiął {1}.",
|
||||
"message.unpinned.by": "{0} odpiął {1}.",
|
||||
"title.pinned.messages": "Przypięte",
|
||||
"error.pinned.messages.empty": "Brak przypiętych wiadomości",
|
||||
"title.message.lowercase": "wiadomość",
|
||||
"action.rotate.keys": "Rotuj Klucze",
|
||||
"already.in.group": "Już w grupie",
|
||||
"desc.key.rotation.info": "Musisz zrotować klucze szyfrujące, aby kontynuować wysyłanie wiadomości w tej grupie. Inni członkowie automatycznie otrzymają nowy klucz.",
|
||||
"desc.no.groups": "Nie jesteś jeszcze w żadnej grupie.",
|
||||
"dm.read": "Przeczytano",
|
||||
"empty.body": "Pusta zawartość",
|
||||
"failed.create.group": "Nie udało się utworzyć grupy",
|
||||
"federation.not.implemented": "Federacja niezaimplementowana",
|
||||
"group.not.found": "Nie znaleziono grupy",
|
||||
"group.read": "Przeczytano",
|
||||
"invalid.entry.name": "Nieprawidłowa nazwa wpisu",
|
||||
"invalid.key.index": "Nieprawidłowy indeks klucza (odśwież stronę)",
|
||||
"invalid.msg.id": "Nieprawidłowe ID wiadomości",
|
||||
"invalid.payload": "Nieprawidłowy ładunek",
|
||||
"invalid.reaction.id": "Nieprawidłowe ID reakcji",
|
||||
"invalid.user": "Nieprawidłowy użytkownik",
|
||||
"joined": "Dołączono",
|
||||
"key.rotation.required": "Wymagana rotacja kluczy",
|
||||
"key.too.large": "Klucz zbyt duży",
|
||||
"keys.public.mismatch": "Niezgodność kluczy publicznych",
|
||||
"label.members": "CZŁONKOWIE",
|
||||
"message.deleted": "Wiadomość usunięta",
|
||||
"message.not.found": "Nie znaleziono wiadomości",
|
||||
"message.pin.toggled": "Zmieniono przypięcie wiadomości",
|
||||
"message.reacted": "Dodano reakcję",
|
||||
"message.reaction.removed": "Reakcja usunięta",
|
||||
"no.invite": "Brak zaproszenia",
|
||||
"not.a.member": "Nie jesteś członkiem",
|
||||
"removed.from.group": "Zostałeś usunięty z grupy",
|
||||
"not.in.group": "Nie jesteś w grupie",
|
||||
"reaction.not.found": "Nie znaleziono reakcji",
|
||||
"rotation.not.required": "Rotacja niewymagana",
|
||||
"storage.entry.updated": "Zaktualizowano wpis pamięci",
|
||||
"storage.limit.exceeded": "Przekroczono limit pamięci",
|
||||
"title.key.rotation": "Rotacja kluczy",
|
||||
"missing.keys": "Brakujące klucze",
|
||||
"error:messages.decrypt.failed": "Deszyfrowanie nie powiodło się",
|
||||
"error.message.already.deleted": "Wiadomość została już usunięta",
|
||||
"group.user.joined.by": "{0} dołączył do grupy",
|
||||
"group.created.by": "Grupa utworzona przez {0}",
|
||||
"group.user.left.by": "{0} opuścił grupę",
|
||||
"action.group.fetch": "Pobieranie grup",
|
||||
"group.created": "Utworzono grupę",
|
||||
"group.user.joined": "Dołączono do grupy",
|
||||
"prompt.invite.username": "Podaj nazwę użytkownika, aby zaprosić:",
|
||||
"desc.failed.load.members": "Nie udało się załadować członków",
|
||||
"title.group.settings": "Ustawienia grupy",
|
||||
"title.group.name": "Nazwa grupy",
|
||||
"title.group.history.visible": "Historia widoczna dla nowych",
|
||||
"desc.group.history.visible": "Nowi członkowie grupy będą mogli zobaczyć starsze wiadomości (wyłącza rotację kluczy)",
|
||||
"history.visible": "włączona",
|
||||
"history.hidden": "wyłączona",
|
||||
"group.history.changed.by": "{0} zmienił widoczność historii na: {1}",
|
||||
"group.name.changed.by": "{0} zmienił(a) nazwę grupy na: {1}",
|
||||
"group.pfp.changed.by": "{0} zmienił(a) ikonę grupy",
|
||||
"group.pfp.removed.by": "{0} usunął(ęła) ikonę grupy",
|
||||
"prompt.are.you.sure": "Czy na pewno?",
|
||||
"title.unknown.group": "Nieznana grupa",
|
||||
"title.unknown": "Nieznany",
|
||||
"key.rotated": "Klucze grupy zostały pomyślnie zrotowane.",
|
||||
"success:banner.updated": "Baner został pomyślnie zaktualizowany",
|
||||
"success:profile.updated": "Profil został pomyślnie zaktualizowany",
|
||||
"success:avatar.updated": "Awatar został pomyślnie zaktualizowany",
|
||||
"en-us": "Angielski",
|
||||
"pl-pl": "Polski",
|
||||
"en-cat": "Koci (Meowlish)",
|
||||
"action.invite": "Zaproś",
|
||||
"action.leaving.group": "Opuszczanie grupy...",
|
||||
"action.key.rotating": "Obracanie kluczy...",
|
||||
"action.key.rotation": "Rotacja kluczy",
|
||||
"desc.leave.group.confirm": "Czy na pewno chcesz opuścić tę grupę?",
|
||||
"desc.key.rotation.you": "Wygenerujesz nowy klucz szyfrujący dla tej grupy.",
|
||||
"label.keys.to.encrypt": "kluczy do zaszyfrowania",
|
||||
"action.group.creating": "Tworzenie grupy...",
|
||||
"title.leave.group": "Opuść grupę",
|
||||
"role.member": "Członek",
|
||||
"role.group_owner": "Właściciel grupy",
|
||||
"role.Owner": "Właściciel",
|
||||
"action.kick": "Wyrzuć z grupy",
|
||||
"action.transfer_owner": "Przekaż właściciela",
|
||||
"group.user.kicked.by": "{0} wyrzucił/a {1} z grupy",
|
||||
"group.owner.transferred.by": "{0} przekazał/a właściciela dla {1}",
|
||||
"group.user.kicked": "Użytkownik wyrzucony",
|
||||
"group.owner.transferred": "Właściciel przekazany",
|
||||
"success:kicked": "Użytkownik wyrzucony pomyślnie",
|
||||
"success:owner.transferred": "Właściciel przekazany pomyślnie",
|
||||
"error:cannot.kick.self": "Nie możesz wyrzucić samego siebie",
|
||||
"action.saving": "Zapisywanie...",
|
||||
"desc.no.chat.selected": "Nie wybrano czatu. Wybierz czat z paska bocznego, aby rozpocząć pisanie.",
|
||||
"failed.to.prepare.keys": "Nie udało się przygotować kluczy",
|
||||
"group.name.empty": "Nazwa grupy nie może być pusta",
|
||||
"group.refresh.failed": "Nie udało się odświeżyć grupy",
|
||||
"group.settings.saved": "Ustawienia grupy zapisane pomyślnie",
|
||||
"kicked": "Wyrzucono pomyślnie",
|
||||
"message.delete.failed": "Nie udało się usunąć wiadomości",
|
||||
"message.react.failed": "Nie udało się dodać reakcji",
|
||||
"message.send.failed": "Nie udało się wysłać wiadomości",
|
||||
"owner.transferred": "Własność przekazana pomyślnie",
|
||||
"title.voice.chat": "Czat głosowy",
|
||||
"status.voice.connecting": "Łączenie...",
|
||||
"status.voice.disconnected": "Rozłączono",
|
||||
"action.call.mute": "Wycisz mikrofon",
|
||||
"action.call.camera": "Kamera",
|
||||
"action.call.screen": "Udostępnij ekran",
|
||||
"action.call.speaker": "Głośnik",
|
||||
"action.call.disconnect": "Rozłącz",
|
||||
"action.call.join": "Dołącz",
|
||||
"action.call.decline": "Odrzuć",
|
||||
"title.incoming.call": "Połączenie głosowe",
|
||||
"desc.call.in.chat": "{0} w rozmowie",
|
||||
"label.user.volume": "Głośność użytkownika",
|
||||
"label.stream.volume": "Głośność ekranu",
|
||||
"error.call.unsupported": "Brak uprawnień lub funkcja nieobsługiwana: {0}",
|
||||
"error.call.camera": "Błąd kamery: {0}",
|
||||
"error.call.screen": "Błąd udostępniania ekranu: {0}",
|
||||
"error.call.screen.unsupported": "Udostępnianie ekranu nie jest obsługiwane w tej przeglądarce.",
|
||||
"label.stream.cam": "Kamera",
|
||||
"label.stream.screen": "Ekran",
|
||||
"action.call.settings": "Ustawienia rozmowy",
|
||||
"label.voice.microphone": "Mikrofon",
|
||||
"label.voice.speaker": "Głośnik",
|
||||
"label.voice.camera": "Kamera",
|
||||
"label.voice.camera.quality": "Jakość kamery",
|
||||
"label.voice.camera.fps": "FPS kamery",
|
||||
"label.voice.screen.quality": "Jakość ekranu",
|
||||
"label.voice.screen.fps": "FPS ekranu",
|
||||
"label.voice.sensitivity": "Czułość wejścia",
|
||||
"label.voice.default": "Domyślne",
|
||||
"error.call.microphone": "Błąd mikrofonu",
|
||||
"error.voice.reconnect.failed": "Utracono połączenie głosowe",
|
||||
"error.voice.ticket.rejected": "Autoryzacja głosu nieudana - sprawdź VOICE_KEY/wersję serwera",
|
||||
"error.voice.not.configured": "Serwer głosowy nieskonfigurowany (brak VOICE_KEY)",
|
||||
"action.call.init": "Nawiązywanie połączenia...",
|
||||
"already.in.call": "Jesteś już w rozmowie",
|
||||
"call.connect.failed": "Nie udało się połączyć z rozmową",
|
||||
"call.microphone": "Błąd mikrofonu",
|
||||
"voice.reconnect.failed": "Utracono połączenie głosowe",
|
||||
"voice.ticket.rejected": "Autoryzacja głosu nieudana - sprawdź VOICE_KEY/wersję serwera",
|
||||
"kdf.salt.missing": "Brak soli szyfrowania. Zaloguj się ponownie.",
|
||||
"group-avatar.updated": "Ikona grupy została pomyślnie zaktualizowana",
|
||||
"error.unknown.error": "Nieznany błąd",
|
||||
"federation.disabled": "Federacja jest wyłączona",
|
||||
"invalid.name": "Nieprawidłowa nazwa",
|
||||
"invalid.keys": "Nieprawidłowe klucze",
|
||||
"history.hidden.keys.provided": "Nie można podać kluczy, gdy historia grupy jest ukryta",
|
||||
"cannot.kick.self": "Nie możesz wyrzucić samego siebie",
|
||||
"invalid.attachment": "Nieprawidłowy załącznik",
|
||||
"no.voice.servers": "Brak dostępnych serwerów głosowych",
|
||||
"voice.not.configured": "Serwer głosowy nieskonfigurowany (brak VOICE_KEY)"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -1 +0,0 @@
|
|||
Couldn't find the requested file /dist/gifuct-js.min.js in gifuct-js.
|
||||
|
|
@ -1,578 +0,0 @@
|
|||
"use strict";
|
||||
var gifuct = (() => {
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
try {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
} catch (e) {
|
||||
throw mod = 0, e;
|
||||
}
|
||||
};
|
||||
|
||||
// ../node_modules/js-binary-schema-parser/lib/index.js
|
||||
var require_lib = __commonJS({
|
||||
"../node_modules/js-binary-schema-parser/lib/index.js"(exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.loop = exports.conditional = exports.parse = void 0;
|
||||
var parse = function parse2(stream, schema) {
|
||||
var result = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
||||
var parent = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : result;
|
||||
if (Array.isArray(schema)) {
|
||||
schema.forEach(function(partSchema) {
|
||||
return parse2(stream, partSchema, result, parent);
|
||||
});
|
||||
} else if (typeof schema === "function") {
|
||||
schema(stream, result, parent, parse2);
|
||||
} else {
|
||||
var key = Object.keys(schema)[0];
|
||||
if (Array.isArray(schema[key])) {
|
||||
parent[key] = {};
|
||||
parse2(stream, schema[key], result, parent[key]);
|
||||
} else {
|
||||
parent[key] = schema[key](stream, result, parent, parse2);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
exports.parse = parse;
|
||||
var conditional = function conditional2(schema, conditionFunc) {
|
||||
return function(stream, result, parent, parse2) {
|
||||
if (conditionFunc(stream, result, parent)) {
|
||||
parse2(stream, schema, result, parent);
|
||||
}
|
||||
};
|
||||
};
|
||||
exports.conditional = conditional;
|
||||
var loop = function loop2(schema, continueFunc) {
|
||||
return function(stream, result, parent, parse2) {
|
||||
var arr = [];
|
||||
var lastStreamPos = stream.pos;
|
||||
while (continueFunc(stream, result, parent)) {
|
||||
var newParent = {};
|
||||
parse2(stream, schema, result, newParent);
|
||||
if (stream.pos === lastStreamPos) {
|
||||
break;
|
||||
}
|
||||
lastStreamPos = stream.pos;
|
||||
arr.push(newParent);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
};
|
||||
exports.loop = loop;
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/js-binary-schema-parser/lib/parsers/uint8.js
|
||||
var require_uint8 = __commonJS({
|
||||
"../node_modules/js-binary-schema-parser/lib/parsers/uint8.js"(exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.readBits = exports.readArray = exports.readUnsigned = exports.readString = exports.peekBytes = exports.readBytes = exports.peekByte = exports.readByte = exports.buildStream = void 0;
|
||||
var buildStream = function buildStream2(uint8Data) {
|
||||
return {
|
||||
data: uint8Data,
|
||||
pos: 0
|
||||
};
|
||||
};
|
||||
exports.buildStream = buildStream;
|
||||
var readByte = function readByte2() {
|
||||
return function(stream) {
|
||||
return stream.data[stream.pos++];
|
||||
};
|
||||
};
|
||||
exports.readByte = readByte;
|
||||
var peekByte = function peekByte2() {
|
||||
var offset = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
|
||||
return function(stream) {
|
||||
return stream.data[stream.pos + offset];
|
||||
};
|
||||
};
|
||||
exports.peekByte = peekByte;
|
||||
var readBytes = function readBytes2(length) {
|
||||
return function(stream) {
|
||||
return stream.data.subarray(stream.pos, stream.pos += length);
|
||||
};
|
||||
};
|
||||
exports.readBytes = readBytes;
|
||||
var peekBytes = function peekBytes2(length) {
|
||||
return function(stream) {
|
||||
return stream.data.subarray(stream.pos, stream.pos + length);
|
||||
};
|
||||
};
|
||||
exports.peekBytes = peekBytes;
|
||||
var readString = function readString2(length) {
|
||||
return function(stream) {
|
||||
return Array.from(readBytes(length)(stream)).map(function(value) {
|
||||
return String.fromCharCode(value);
|
||||
}).join("");
|
||||
};
|
||||
};
|
||||
exports.readString = readString;
|
||||
var readUnsigned = function readUnsigned2(littleEndian) {
|
||||
return function(stream) {
|
||||
var bytes = readBytes(2)(stream);
|
||||
return littleEndian ? (bytes[1] << 8) + bytes[0] : (bytes[0] << 8) + bytes[1];
|
||||
};
|
||||
};
|
||||
exports.readUnsigned = readUnsigned;
|
||||
var readArray = function readArray2(byteSize, totalOrFunc) {
|
||||
return function(stream, result, parent) {
|
||||
var total = typeof totalOrFunc === "function" ? totalOrFunc(stream, result, parent) : totalOrFunc;
|
||||
var parser = readBytes(byteSize);
|
||||
var arr = new Array(total);
|
||||
for (var i = 0; i < total; i++) {
|
||||
arr[i] = parser(stream);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
};
|
||||
exports.readArray = readArray;
|
||||
var subBitsTotal = function subBitsTotal2(bits, startIndex, length) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += bits[startIndex + i] && Math.pow(2, length - i - 1);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
var readBits = function readBits2(schema) {
|
||||
return function(stream) {
|
||||
var _byte = readByte()(stream);
|
||||
var bits = new Array(8);
|
||||
for (var i = 0; i < 8; i++) {
|
||||
bits[7 - i] = !!(_byte & 1 << i);
|
||||
}
|
||||
return Object.keys(schema).reduce(function(res, key) {
|
||||
var def = schema[key];
|
||||
if (def.length) {
|
||||
res[key] = subBitsTotal(bits, def.index, def.length);
|
||||
} else {
|
||||
res[key] = bits[def.index];
|
||||
}
|
||||
return res;
|
||||
}, {});
|
||||
};
|
||||
};
|
||||
exports.readBits = readBits;
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/js-binary-schema-parser/lib/schemas/gif.js
|
||||
var require_gif = __commonJS({
|
||||
"../node_modules/js-binary-schema-parser/lib/schemas/gif.js"(exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
var _ = require_lib();
|
||||
var _uint = require_uint8();
|
||||
var subBlocksSchema = {
|
||||
blocks: function blocks(stream) {
|
||||
var terminator = 0;
|
||||
var chunks = [];
|
||||
var streamSize = stream.data.length;
|
||||
var total = 0;
|
||||
for (var size = (0, _uint.readByte)()(stream); size !== terminator; size = (0, _uint.readByte)()(stream)) {
|
||||
if (!size) break;
|
||||
if (stream.pos + size >= streamSize) {
|
||||
var availableSize = streamSize - stream.pos;
|
||||
chunks.push((0, _uint.readBytes)(availableSize)(stream));
|
||||
total += availableSize;
|
||||
break;
|
||||
}
|
||||
chunks.push((0, _uint.readBytes)(size)(stream));
|
||||
total += size;
|
||||
}
|
||||
var result = new Uint8Array(total);
|
||||
var offset = 0;
|
||||
for (var i = 0; i < chunks.length; i++) {
|
||||
result.set(chunks[i], offset);
|
||||
offset += chunks[i].length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
var gceSchema = (0, _.conditional)({
|
||||
gce: [{
|
||||
codes: (0, _uint.readBytes)(2)
|
||||
}, {
|
||||
byteSize: (0, _uint.readByte)()
|
||||
}, {
|
||||
extras: (0, _uint.readBits)({
|
||||
future: {
|
||||
index: 0,
|
||||
length: 3
|
||||
},
|
||||
disposal: {
|
||||
index: 3,
|
||||
length: 3
|
||||
},
|
||||
userInput: {
|
||||
index: 6
|
||||
},
|
||||
transparentColorGiven: {
|
||||
index: 7
|
||||
}
|
||||
})
|
||||
}, {
|
||||
delay: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
transparentColorIndex: (0, _uint.readByte)()
|
||||
}, {
|
||||
terminator: (0, _uint.readByte)()
|
||||
}]
|
||||
}, function(stream) {
|
||||
var codes = (0, _uint.peekBytes)(2)(stream);
|
||||
return codes[0] === 33 && codes[1] === 249;
|
||||
});
|
||||
var imageSchema = (0, _.conditional)({
|
||||
image: [{
|
||||
code: (0, _uint.readByte)()
|
||||
}, {
|
||||
descriptor: [{
|
||||
left: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
top: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
width: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
height: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
lct: (0, _uint.readBits)({
|
||||
exists: {
|
||||
index: 0
|
||||
},
|
||||
interlaced: {
|
||||
index: 1
|
||||
},
|
||||
sort: {
|
||||
index: 2
|
||||
},
|
||||
future: {
|
||||
index: 3,
|
||||
length: 2
|
||||
},
|
||||
size: {
|
||||
index: 5,
|
||||
length: 3
|
||||
}
|
||||
})
|
||||
}]
|
||||
}, (0, _.conditional)({
|
||||
lct: (0, _uint.readArray)(3, function(stream, result, parent) {
|
||||
return Math.pow(2, parent.descriptor.lct.size + 1);
|
||||
})
|
||||
}, function(stream, result, parent) {
|
||||
return parent.descriptor.lct.exists;
|
||||
}), {
|
||||
data: [{
|
||||
minCodeSize: (0, _uint.readByte)()
|
||||
}, subBlocksSchema]
|
||||
}]
|
||||
}, function(stream) {
|
||||
return (0, _uint.peekByte)()(stream) === 44;
|
||||
});
|
||||
var textSchema = (0, _.conditional)({
|
||||
text: [{
|
||||
codes: (0, _uint.readBytes)(2)
|
||||
}, {
|
||||
blockSize: (0, _uint.readByte)()
|
||||
}, {
|
||||
preData: function preData(stream, result, parent) {
|
||||
return (0, _uint.readBytes)(parent.text.blockSize)(stream);
|
||||
}
|
||||
}, subBlocksSchema]
|
||||
}, function(stream) {
|
||||
var codes = (0, _uint.peekBytes)(2)(stream);
|
||||
return codes[0] === 33 && codes[1] === 1;
|
||||
});
|
||||
var applicationSchema = (0, _.conditional)({
|
||||
application: [{
|
||||
codes: (0, _uint.readBytes)(2)
|
||||
}, {
|
||||
blockSize: (0, _uint.readByte)()
|
||||
}, {
|
||||
id: function id(stream, result, parent) {
|
||||
return (0, _uint.readString)(parent.blockSize)(stream);
|
||||
}
|
||||
}, subBlocksSchema]
|
||||
}, function(stream) {
|
||||
var codes = (0, _uint.peekBytes)(2)(stream);
|
||||
return codes[0] === 33 && codes[1] === 255;
|
||||
});
|
||||
var commentSchema = (0, _.conditional)({
|
||||
comment: [{
|
||||
codes: (0, _uint.readBytes)(2)
|
||||
}, subBlocksSchema]
|
||||
}, function(stream) {
|
||||
var codes = (0, _uint.peekBytes)(2)(stream);
|
||||
return codes[0] === 33 && codes[1] === 254;
|
||||
});
|
||||
var schema = [
|
||||
{
|
||||
header: [{
|
||||
signature: (0, _uint.readString)(3)
|
||||
}, {
|
||||
version: (0, _uint.readString)(3)
|
||||
}]
|
||||
},
|
||||
{
|
||||
lsd: [{
|
||||
width: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
height: (0, _uint.readUnsigned)(true)
|
||||
}, {
|
||||
gct: (0, _uint.readBits)({
|
||||
exists: {
|
||||
index: 0
|
||||
},
|
||||
resolution: {
|
||||
index: 1,
|
||||
length: 3
|
||||
},
|
||||
sort: {
|
||||
index: 4
|
||||
},
|
||||
size: {
|
||||
index: 5,
|
||||
length: 3
|
||||
}
|
||||
})
|
||||
}, {
|
||||
backgroundColorIndex: (0, _uint.readByte)()
|
||||
}, {
|
||||
pixelAspectRatio: (0, _uint.readByte)()
|
||||
}]
|
||||
},
|
||||
(0, _.conditional)({
|
||||
gct: (0, _uint.readArray)(3, function(stream, result) {
|
||||
return Math.pow(2, result.lsd.gct.size + 1);
|
||||
})
|
||||
}, function(stream, result) {
|
||||
return result.lsd.gct.exists;
|
||||
}),
|
||||
// content frames
|
||||
{
|
||||
frames: (0, _.loop)([gceSchema, applicationSchema, commentSchema, imageSchema, textSchema], function(stream) {
|
||||
var nextCode = (0, _uint.peekByte)()(stream);
|
||||
return nextCode === 33 || nextCode === 44;
|
||||
})
|
||||
}
|
||||
];
|
||||
var _default = schema;
|
||||
exports["default"] = _default;
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/gifuct-js/lib/deinterlace.js
|
||||
var require_deinterlace = __commonJS({
|
||||
"../node_modules/gifuct-js/lib/deinterlace.js"(exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.deinterlace = void 0;
|
||||
var deinterlace = function deinterlace2(pixels, width) {
|
||||
var newPixels = new Array(pixels.length);
|
||||
var rows = pixels.length / width;
|
||||
var cpRow = function cpRow2(toRow2, fromRow2) {
|
||||
var fromPixels = pixels.slice(fromRow2 * width, (fromRow2 + 1) * width);
|
||||
newPixels.splice.apply(newPixels, [toRow2 * width, width].concat(fromPixels));
|
||||
};
|
||||
var offsets = [0, 4, 2, 1];
|
||||
var steps = [8, 8, 4, 2];
|
||||
var fromRow = 0;
|
||||
for (var pass = 0; pass < 4; pass++) {
|
||||
for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
|
||||
cpRow(toRow, fromRow);
|
||||
fromRow++;
|
||||
}
|
||||
}
|
||||
return newPixels;
|
||||
};
|
||||
exports.deinterlace = deinterlace;
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/gifuct-js/lib/lzw.js
|
||||
var require_lzw = __commonJS({
|
||||
"../node_modules/gifuct-js/lib/lzw.js"(exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.lzw = void 0;
|
||||
var lzw = function lzw2(minCodeSize, data, pixelCount) {
|
||||
var MAX_STACK_SIZE = 4096;
|
||||
var nullCode = -1;
|
||||
var npix = pixelCount;
|
||||
var available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, i, datum, data_size, first, top, bi, pi;
|
||||
var dstPixels = new Array(pixelCount);
|
||||
var prefix = new Array(MAX_STACK_SIZE);
|
||||
var suffix = new Array(MAX_STACK_SIZE);
|
||||
var pixelStack = new Array(MAX_STACK_SIZE + 1);
|
||||
data_size = minCodeSize;
|
||||
clear = 1 << data_size;
|
||||
end_of_information = clear + 1;
|
||||
available = clear + 2;
|
||||
old_code = nullCode;
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
for (code = 0; code < clear; code++) {
|
||||
prefix[code] = 0;
|
||||
suffix[code] = code;
|
||||
}
|
||||
var datum, bits, count, first, top, pi, bi;
|
||||
datum = bits = count = first = top = pi = bi = 0;
|
||||
for (i = 0; i < npix; ) {
|
||||
if (top === 0) {
|
||||
if (bits < code_size) {
|
||||
datum += data[bi] << bits;
|
||||
bits += 8;
|
||||
bi++;
|
||||
continue;
|
||||
}
|
||||
code = datum & code_mask;
|
||||
datum >>= code_size;
|
||||
bits -= code_size;
|
||||
if (code > available || code == end_of_information) {
|
||||
break;
|
||||
}
|
||||
if (code == clear) {
|
||||
code_size = data_size + 1;
|
||||
code_mask = (1 << code_size) - 1;
|
||||
available = clear + 2;
|
||||
old_code = nullCode;
|
||||
continue;
|
||||
}
|
||||
if (old_code == nullCode) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
old_code = code;
|
||||
first = code;
|
||||
continue;
|
||||
}
|
||||
in_code = code;
|
||||
if (code == available) {
|
||||
pixelStack[top++] = first;
|
||||
code = old_code;
|
||||
}
|
||||
while (code > clear) {
|
||||
pixelStack[top++] = suffix[code];
|
||||
code = prefix[code];
|
||||
}
|
||||
first = suffix[code] & 255;
|
||||
pixelStack[top++] = first;
|
||||
if (available < MAX_STACK_SIZE) {
|
||||
prefix[available] = old_code;
|
||||
suffix[available] = first;
|
||||
available++;
|
||||
if ((available & code_mask) === 0 && available < MAX_STACK_SIZE) {
|
||||
code_size++;
|
||||
code_mask += available;
|
||||
}
|
||||
}
|
||||
old_code = in_code;
|
||||
}
|
||||
top--;
|
||||
dstPixels[pi++] = pixelStack[top];
|
||||
i++;
|
||||
}
|
||||
for (i = pi; i < npix; i++) {
|
||||
dstPixels[i] = 0;
|
||||
}
|
||||
return dstPixels;
|
||||
};
|
||||
exports.lzw = lzw;
|
||||
}
|
||||
});
|
||||
|
||||
// ../node_modules/gifuct-js/lib/index.js
|
||||
var require_index = __commonJS({
|
||||
"../node_modules/gifuct-js/lib/index.js"(exports) {
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.decompressFrames = exports.decompressFrame = exports.parseGIF = void 0;
|
||||
var _gif = _interopRequireDefault(require_gif());
|
||||
var _jsBinarySchemaParser = require_lib();
|
||||
var _uint = require_uint8();
|
||||
var _deinterlace = require_deinterlace();
|
||||
var _lzw = require_lzw();
|
||||
function _interopRequireDefault(obj) {
|
||||
return obj && obj.__esModule ? obj : { "default": obj };
|
||||
}
|
||||
var parseGIF = function parseGIF2(arrayBuffer) {
|
||||
var byteData = new Uint8Array(arrayBuffer);
|
||||
return (0, _jsBinarySchemaParser.parse)((0, _uint.buildStream)(byteData), _gif["default"]);
|
||||
};
|
||||
exports.parseGIF = parseGIF;
|
||||
var generatePatch = function generatePatch2(image) {
|
||||
var totalPixels = image.pixels.length;
|
||||
var patchData = new Uint8ClampedArray(totalPixels * 4);
|
||||
for (var i = 0; i < totalPixels; i++) {
|
||||
var pos = i * 4;
|
||||
var colorIndex = image.pixels[i];
|
||||
var color = image.colorTable[colorIndex] || [0, 0, 0];
|
||||
patchData[pos] = color[0];
|
||||
patchData[pos + 1] = color[1];
|
||||
patchData[pos + 2] = color[2];
|
||||
patchData[pos + 3] = colorIndex !== image.transparentIndex ? 255 : 0;
|
||||
}
|
||||
return patchData;
|
||||
};
|
||||
var decompressFrame = function decompressFrame2(frame, gct, buildImagePatch) {
|
||||
if (!frame.image) {
|
||||
console.warn("gif frame does not have associated image.");
|
||||
return;
|
||||
}
|
||||
var image = frame.image;
|
||||
var totalPixels = image.descriptor.width * image.descriptor.height;
|
||||
var pixels = (0, _lzw.lzw)(image.data.minCodeSize, image.data.blocks, totalPixels);
|
||||
if (image.descriptor.lct.interlaced) {
|
||||
pixels = (0, _deinterlace.deinterlace)(pixels, image.descriptor.width);
|
||||
}
|
||||
var resultImage = {
|
||||
pixels,
|
||||
dims: {
|
||||
top: frame.image.descriptor.top,
|
||||
left: frame.image.descriptor.left,
|
||||
width: frame.image.descriptor.width,
|
||||
height: frame.image.descriptor.height
|
||||
}
|
||||
};
|
||||
if (image.descriptor.lct && image.descriptor.lct.exists) {
|
||||
resultImage.colorTable = image.lct;
|
||||
} else {
|
||||
resultImage.colorTable = gct;
|
||||
}
|
||||
if (frame.gce) {
|
||||
resultImage.delay = (frame.gce.delay || 10) * 10;
|
||||
resultImage.disposalType = frame.gce.extras.disposal;
|
||||
if (frame.gce.extras.transparentColorGiven) {
|
||||
resultImage.transparentIndex = frame.gce.transparentColorIndex;
|
||||
}
|
||||
}
|
||||
if (buildImagePatch) {
|
||||
resultImage.patch = generatePatch(resultImage);
|
||||
}
|
||||
return resultImage;
|
||||
};
|
||||
exports.decompressFrame = decompressFrame;
|
||||
var decompressFrames = function decompressFrames2(parsedGif, buildImagePatches) {
|
||||
return parsedGif.frames.filter(function(f) {
|
||||
return f.image;
|
||||
}).map(function(f) {
|
||||
return decompressFrame(f, parsedGif.gct, buildImagePatches);
|
||||
});
|
||||
};
|
||||
exports.decompressFrames = decompressFrames;
|
||||
}
|
||||
});
|
||||
return require_index();
|
||||
})();
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
// icons.js - Centralized SVG definitions
|
||||
|
||||
//Subset czcionki Nunito (A-Z, 0-9, kropka, spacja) w base64.
|
||||
//SVG wczytany przez <img> nie ma dostępu do fontów strony (@font-face),
|
||||
//więc bez osadzenia czcionki inicjały na fallback awatarach renderują się
|
||||
//systemową czcionką zamiast Nunito.
|
||||
const AVATAR_FONT_B64 = "d09GMgABAAAAAAuEAA8AAAAAFeAAAAsrAAOaHQAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbIBwwBmA/U1RBVEgATBEICqAMmQcBNgIkA4EcC1AABCAFg2wHIBsKEaOiblFWZIq/OLCJTFfgT7TnToqww17mCUVr1EAo7VdCE7WmevjB8NvbNYqgIZYH9azRD98Lc+zakT3K4d9LUsApBE6dhZl/gpZFAg/P36vn3h9MOJsClraAqdda2hMkYPUKTZCChLrAox37/c84G//7o9KWtkwl2GnXLkKKDBFuV6F6pXGIbXZf5TjAdaoTM0BRlwbqovqjo37Qyw/K90pyu6QQV2jqrQpanzC1/49WcP3aq70NAEgkITNRMT7G5L39tAchuhDcDyG1rDKVcXFFdISulQjjayusroVOpciQBv39SKdCE6LZU8ZsiQACoA4gVqkE0+IeAFWYs0Rx82l5UIZA7fm/0ENBbjxwTX99tKfN5EAHtb4OWGaAmwsWH6sUALaSaYBUchCqYB426qQE/m+Pb4srpJLq8/98AoR/uIH+9CQfVIkvu70yGAD9rmBXMbjd1KeMf4IRCSIBAI/PSNAqsAuMBZppPbnAEBX1UyTuAkGS6iT9GSQPwUp85Iy7l+jDqf+/y7QcQN4QySUOlRx/IGOwHyj4g+I2mqZ8AMCBjdNIDBknS2MEOwgKeP7KJPrFBgbmh2N6YmVaSGxWZH5aREhkYGhESHZgXWxqfH5eUkpIUFphS6pvdW5YXFpQcPEmgph5ixA7EJ2otcf39JwJrhwJ6fp6o4azbWRjG3njOO8gfc+ciFpZJsavGuIjHdJsV5UcU4BO9v3m5hXFpNYMv1qznudMVzGXCsxLEjHWZKoFjUazbEfUteSWC1lyKKu5AXAL1tpPewZBRH9BjNqa1Ok8XHUmFAVFJNgoP7Miik55ykWdSy7xeXYw3adawgbz6SAqQciY2GUftT8TY1WfL5MG3NUuSeoS/riyY23NRTVTN/V85W+JaUNCHMufdlpzdA8G2oYFaL44uqnr7zfE5JEyF9KrG5eEE4h9aJT3urASm3GhC8BsJugZQE9posDkr7e1xdlMlpi08A0yCz+LWYvlEkhQV2X03Jwq0p5v4PGsiWvRv8AgiEZr9IU8dBT/2PRBCW+Kn5jUxOLcVHhgRU4M3TIFvJ2hhB/dGjk0rUjoC2IwcVt5eOv4qC5nG84qC9f81BJUDvrpcUXIAghjgWkvxBaI/DTxaDxzUGjko7JkztO6PGY3TYtcjy5XLqvMU0xbCWLsgGCWeFo7mXkuqksK9sEFbiIGGAUXgFxcwhc1mxaDIMZmroWM5ggxmfJfU65D066Oa2WTZqyZLaEX33/pQ4pPRzmxuVP/VJEE98CBDCoSEzsLsUq8PmdiyYxF4jT6malFOtP9GfmKllLgnecEv0t1frp2BmGIZfk9hUE12GffKTYMVrNrCaJ3ovzwZvQWp02sHi/yWGvIJ+J9ooBVw2zWlsYfOA9c3/PqBW5J3k7E6cVpaOipQTnH6vvHBCCoPd+yqmndEPOqQYRe6YJeDQ32diswJ27wLBU/x9v+olixTUkmufXd2P6OyfSEnJ6FtVf156ypWj2ZqrGeI/elWLUqjVWTsg/MYHeyO2VhHcMZwmT1FiRrakMHnlxqYElfNuX7CVKrSWn/zEzpgGoylXD7J72s+1vf2TG5LnzKZzvoPLt5jYs3KDN9MLdgtnEaXRJmMIZU0GNHjhk1DdWaIERgotq+hJEqqPq3wJeBcxApUY2iVaVGf/PmhfTf0RBI+HEBh8NZztSF9QxnqCurjyBV2xx69XV+8bv87eMz/WXWGkrWP2vMoMlD+KXogMCfF6iOB2KFboY6sJ3Hxt2EJk9wAsIEvCfbiONGNr4c71NPCfvcF7g8UF+CYfE4EvCsiEe6Pv7wNaiQPoqOw3ED+3G9oz2FOxiXe0leR0BrY/zuXiwByc8zyi6F5rf+NphH0hmnrHW0vgScCQvwOssvxGFGBRAKUHAMfD+sPy5EEU4/2CZQu00/fwiFeH8sHEuvIvD++XDwisX+y/y/2vJNrg6OQOCH98HFCPLq5ozBW8K9WAwNx/pjbREtv41NBi8xLgLHWJ+QlrKwluEMcWS1CBKKJrhTFtQy3KGO7Oic5qQySjowc2SvaQ0JtGFA5ojeMzy5+0Y2g7/d61lTXNSztv7oV6EBK9pts5tpU+dfz7YbjEzOi8bh/Dhdye8xFeUfn7f6iOdkz6u8QCPJKOclzMEdp/fO3dS3M1cH5varOlmxh5hqLwzA6nfaHQYjFX8B9p3AgvHFHsGAZ68X6Pe24+db/roMFynlPupndfBsLCsjKT4iiZJkB8i1VaGbVrQwb/Nv9HX6isRJQWuivZaTXTvsIjyPsXDCLJQo1w8UAVaAux6ivggkUDZKHMdNH7I4DdrQXRZuModV0sP3VRtcU1xVWH/yVTWD2Xk4ZmAPz9YM7KsDRSDozkeb8lzJLIHM7dAMbl9cw0dZ3MRatCk/HwQBmlNwp6UoyQvy88/lhF2aKBBAJLTgfGbQhWmdHrKVzXdbCkgMJRtOkJ6nOgoEYBh40TFrPo7kjysPfaI92NbDM8amp+O2lJpIlO+6obNbfRwdxnjDbklrb7nI4t43s4Az4Alss/evpcXOam1W1tgr9gZXsg+N/rNpd573tYheDmC5qpaqffMtwWMxHwgJbBOSVly9br45aDbmDVECA4XYs+/ODS3SBTU2a68+2za2SBdW2600Yx03dZp1FOOwjZ461TYOHPB9IOSjg6SuE3KP5b9jWo6PLDnesytboZvbA+VDlEDBmTgO7bLk8v57pVsWX2XW0NxUv6ST+r/5rR211ZFyU34cwahjfa0Sx54rDyTZWDBEQzCQiYq+kAUnBgz/qj/3FPrEC40BijiNTZtiwgQQEWA6ni5IESszKQzcVJ+aHXlviltO1leGyQ15sUKrPC6YXjiri7dLNMeslqWV88EDCPLxpiC8AO0y7d7r/NhsD26t58F+jICQwCxNuFP1+NLYaKYnGlDeFXL5Vb/zX/XjmEI/PkGHh5NcY6qbjjMFXVqJ7QptkCxMgDJYCIQhWIyP58xMjpzKk4qKKFJeXC4HnZhosipnVmdFjLbklRMULy9Mt5xm3lp3+Cd6R+gztLwB+dmdshK7M06iyZWKi/SktLRYQRs5VGQEzeNFGKkoDoeKijAyJlpyXmAFtj2pn64mvAw9sC8JHYtmQ1ZRljCtOhu4YmodV+HWEFFJpvinoa5uwRy9SmYv55jMZRy5XRUXav6/guXulM6zaBTuOm4K5XTpKdKlczJuHUm59Y5EwhCYHqWykxaLjVSpbBT4hdzqMP1yI/Nu5pY+dgPzvDMSx6HFOkpWUqQg15sfznALwzoCuBwyKjI9kiZnA/tk2b0443vxJiUnWe5EbTdZ3ua3AMDX+n/L8/gyJ+wNK/4CgNvN40cA7g2P50rn/ylVVDMCKIIBAAj4/ZWu4InYFcBiVHovjvHFMG+yyQOFuSs8nxFK7IAdXWOFn3ZBrSKuvOapJL6VwqpQcT7JyP+QMkxg874aeWdJ+csXOXlCQ6RzyBX2mUeLMWbnLOLfgX4lJgZPyIQoCAPvTL/wofCJsPwPwVENJXRS+JEEeys1rOAgoQJgD5gQgjr2Qhg0cRjC4eNCiIC5dyEKMA3VYEWYh10MAtQpCsg6bYZwrJE1VUofNFanLsN6NGvUpI/kw4s3P1IeD/XmDP06MOozcZOyGKEWuVRrRNEI9eHUKaVeVnRyxnLq4gpCeDZSowNqgs01PNTmYruMEerMzG3qNRTUAVKvdGbHlLOdF0A5ODUmBW2qs3P58hDAi49QlDJTiqejjOZwz0cDiUm1QL0XUvNyZsgpJu6WCgtD+Ci5U1CtLVFr5XjarloHjjq5/1fMAQA=";
|
||||
|
||||
const ICONS = {
|
||||
_create: function(content, defaultProps, customProps = {}) {
|
||||
let p = { ...defaultProps, ...customProps };
|
||||
if (customProps.width && !customProps.height) p.height = customProps.width;
|
||||
let strokeAttrs = p.strokeWidth ? ` stroke-width="${p.strokeWidth}" stroke-linecap="${p.strokeLinecap}" stroke-linejoin="${p.strokeLinejoin}"` : "";
|
||||
let classAttr = p.className ? ` class="${p.className}"` : "";
|
||||
let styleAttr = p.style ? ` style="${p.style}"` : "";
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${p.width}" height="${p.height}" viewBox="${p.viewBox}" fill="${p.fill}" stroke="${p.stroke}"${strokeAttrs}${classAttr}${styleAttr}>${content}</svg>`;
|
||||
},
|
||||
_material: function(content, props) {
|
||||
return this._create(content, { viewBox: "0 -960 960 960", width: "1.4rem", height: "1.4rem", fill: "var(--text-color)", stroke: "none" }, props);
|
||||
},
|
||||
_feather: function(content, props) {
|
||||
return this._create(content, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "none", stroke: "var(--text-color)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props);
|
||||
},
|
||||
addChat: (props) => ICONS._material(`<path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/>`, props),
|
||||
createGroup: (props) => ICONS._material(`<path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/>`, props),
|
||||
createSpace: (props) => ICONS._material(`<path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/>`, props),
|
||||
joinSpace: (props) => ICONS._material(`<path d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/>`, props),
|
||||
invites: (props) => ICONS._material(`<path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/>`, props),
|
||||
notifications: (props) => ICONS._material(`<path d="M160-200v-80h80v-280q0-83 50-147.5T420-792v-28q0-25 17.5-42.5T480-880q25 0 42.5 17.5T540-820v28q80 20 130 84.5T720-560v280h80v80H160Zm320-300Zm0 420q-33 0-56.5-23.5T400-160h160q0 33-23.5 56.5T480-80ZM320-280h320v-280q0-66-47-113t-113-47q-66 0-113 47t-47 113v280Z"/>`, props),
|
||||
close: (props) => ICONS._material(`<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>`, props),
|
||||
check: (props) => ICONS._material(`<path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/>`, props),
|
||||
delete: (props) => ICONS._material(`<path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/>`, props),
|
||||
menu: (props) => ICONS._material(`<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>`, props),
|
||||
arrowBack: (props) => ICONS._material(`<path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/>`, props),
|
||||
pin: (props) => ICONS._material(`<path d="m640-480 80 80v80H520v240l-40 40-40-40v-240H240v-80l80-80v-280h-40v-80h400v80h-40v280Zm-286 80h252l-46-46v-314H400v314l-46 46Zm126 0Z"/>`, props),
|
||||
call: (props) => ICONS._material(`<path d="M798-120q-125 0-247-54.5T329-329Q229-429 174.5-551T120-798q0-18 12-30t30-12h162q14 0 25 9.5t13 22.5l26 140q2 16-1 27t-11 19l-97 98q20 37 47.5 71.5T387-386q31 31 65 57.5t72 48.5l94-94q9-9 23.5-13.5T670-390l138 28q14 4 23 14.5t9 23.5v162q0 18-12 30t-30 12ZM241-600l66-66-17-94h-89q5 41 14 81t26 79Zm358 358q39 17 79.5 27t81.5 13v-88l-94-19-67 67ZM241-600Zm358 358Z"/>`, props),
|
||||
account: (props) => ICONS._material(`<path d="M200-200h560v-560H200v560Zm0 80q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm40-160h480L570-480 450-320l-90-120-120 160Zm-40 80v-560 560Z"/>`, props),
|
||||
profile: (props) => ICONS._material(`<path d="M480-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM160-160v-112q0-34 17.5-62.5T224-378q62-31 126-46.5T480-440q66 0 130 15.5T736-378q29 15 46.5 43.5T800-272v112H160Zm80-80h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56 0-111 13.5T260-306q-9 5-14.5 14t-5.5 20v32Zm240-320q33 0 56.5-23.5T560-640q0-33-23.5-56.5T480-720q-33 0-56.5 23.5T400-640q0 33 23.5 56.5T480-560Zm0-80Zm0 400Z"/>`, props),
|
||||
replyAction: (props) => ICONS._material(`<path d="M760-200v-160q0-50-35-85t-85-35H273l144 144-57 56-240-240 240-240 57 56-144 144h367q83 0 141.5 58.5T840-360v160h-80Z"/>`, props),
|
||||
react: (props) => ICONS._material(`<path d="M480-260q70 0 126.5-40.5T682-400H278q19 59 75.5 99.5T480-260Zm-160-220q25 0 42.5-17.5T380-540q0-25-17.5-42.5T320-600q-25 0-42.5 17.5T260-540q0 25 17.5 42.5T320-480Zm320 0q25 0 42.5-17.5T700-540q0-25-17.5-42.5T640-600q-25 0-42.5 17.5T580-540q0 25 17.5 42.5T640-480ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/>`, props),
|
||||
settings: (props) => ICONS._material(`<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/>`, props),
|
||||
|
||||
download: (props) => ICONS._feather(`<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>`, props),
|
||||
camera: (props) => ICONS._feather(`<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle>`, props),
|
||||
image: (props) => ICONS._feather(`<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline>`, props),
|
||||
fileText: (props) => ICONS._feather(`<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline>`, props),
|
||||
poll: (props) => ICONS._feather(`<line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line>`, props),
|
||||
send: (props) => ICONS._create(`<path stroke="var(--main-bg-color)" stroke-linecap="round" stroke-linejoin="round" d="M 4.65 8 l -1.875 5.625 l 11.25 -5.625 L 2.775 2.375 l 1.875 5.625 z m 0 0 h 3.75" stroke-width="1.1"></path>`, { viewBox: "0 0 16 16", width: "1.5rem", height: "1.5rem", fill: "none", stroke: "none" }, props),
|
||||
plus: (props) => ICONS._feather(`<line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line>`, props),
|
||||
chevronDown: (props) => ICONS._feather(`<polyline points="6 9 12 15 18 9"></polyline>`, props),
|
||||
lockOpen: (props) => ICONS._feather(`<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 9.9-1"></path>`, props),
|
||||
cornerUpLeft: (props) => ICONS._feather(`<polyline points="9 17 4 12 9 7"></polyline><path d="M20 18v-2a4 4 0 0 0-4-4H4"></path>`, props),
|
||||
spinner: (props) => ICONS._create(`<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>`, { viewBox: "0 0 50 50", width: "1.5rem", height: "1.5rem", fill: "none", stroke: "none", className: "spinner" }, props),
|
||||
video: (props) => ICONS._feather(`<polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>`, props),
|
||||
file: (props) => ICONS._feather(`<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>`, props),
|
||||
mic: (props) => ICONS._feather(`<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>`, props),
|
||||
headphones: (props) => ICONS._feather(`<path d="M3 18v-6a9 9 0 0 1 18 0v6"></path><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z"></path>`, props),
|
||||
speaker: (props) => ICONS._create(`<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path>`, { viewBox: "0 0 24 24", width: "1.4rem", height: "1.4rem", fill: "var(--text-color)", stroke: "none" }, props),
|
||||
|
||||
home: (props) => ICONS._create(`<path d="M1 6V15H6V11C6 9.89543 6.89543 9 8 9C9.10457 9 10 9.89543 10 11V15H15V6L8 0L1 6Z" stroke="currentColor" stroke-width="1.28" stroke-linejoin="round" stroke-linecap="round" transform="translate(0 0.5)"/>`, { viewBox: "-1 -1 18 18", width: "1.5rem", height: "1.5rem", fill: "none", stroke: "none" }, props),
|
||||
inbox: (props) => ICONS._material(`<path d="M216-144q-29 0-50.5-21.5T144-216v-528q0-29.7 21.5-50.85Q187-816 216-816h528q29.7 0 50.85 21.15Q816-773.7 816-744v528q0 29-21.15 50.5T744-144H216Zm0-72h528v-144H632q-23 43-63.5 69.5T480-264q-49 0-89.5-26T328-360H216v144Zm332-148q28-28 28-68h168v-312H216v312h168q0 40 28 68t68 28q40 0 68-28ZM216-216h528-528Z"/>`, props),
|
||||
|
||||
avatar: function(initials, color, size) {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}"><defs><style>@font-face{font-family:'NunitoAvatar';src:url(data:font/woff2;base64,${AVATAR_FONT_B64}) format('woff2');}</style></defs><rect width="100%" height="100%" fill="${color}" /><text x="50%" y="50%" fill="white" font-family="NunitoAvatar, Nunito, sans-serif" font-size="${size * 0.45}" text-anchor="middle" dy=".35em">${initials}</text></svg>`;
|
||||
}
|
||||
};
|
||||
|
|
@ -6,13 +6,6 @@
|
|||
<title>Miarven</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
||||
<script src="crypto-pq.js"></script>
|
||||
|
||||
<!-- debug
|
||||
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||
<script>eruda.init();</script> -->
|
||||
|
||||
<script src="icons.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<loading>
|
||||
|
|
@ -27,20 +20,20 @@
|
|||
<indicator class="active">
|
||||
</indicator>
|
||||
<button class="icon-button" onclick="gotoHome()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.home());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
<svg viewBox="-1 -1 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1 6V15H6V11C6 9.89543 6.89543 9 8 9C9.10457 9 10 9.89543 10 11V15H15V6L8 0L1 6Z" stroke="var(--text-color)" stroke-width="1.28" stroke-linejoin="round" stroke-linecap="round" transform="translate(0 0.5)"/>
|
||||
</svg>
|
||||
</button>
|
||||
</sidebarelement>
|
||||
<sidebarelement id="sidebar-add">
|
||||
<indicator>
|
||||
</indicator>
|
||||
<button class="icon-button">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.plus());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="var(--text-color)"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</sidebarelement>
|
||||
<hr>
|
||||
|
|
@ -49,16 +42,13 @@
|
|||
<indicator>
|
||||
</indicator>
|
||||
<button class="icon-button" onclick="gotoInbox()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.inbox());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M216-144q-29 0-50.5-21.5T144-216v-528q0-29.7 21.5-50.85Q187-816 216-816h528q29.7 0 50.85 21.15Q816-773.7 816-744v528q0 29-21.15 50.5T744-144H216Zm0-72h528v-144H632q-23 43-63.5 69.5T480-264q-49 0-89.5-26T328-360H216v144Zm332-148q28-28 28-68h168v-312H216v312h168q0 40 28 68t68 28q40 0 68-28ZM216-216h528-528Z"/></svg>
|
||||
</button>
|
||||
</sidebarelement>
|
||||
<sidebarelement id="sidebar-profile">
|
||||
<indicator>
|
||||
</indicator>
|
||||
<button class="icon-button">
|
||||
<button class="icon-button" onclick="gotoSideProfilePopup()">
|
||||
<img id="sidebar-pfp">
|
||||
</button>
|
||||
</sidebarelement>
|
||||
|
|
@ -88,57 +78,14 @@
|
|||
<div id="fixed-context-menu" class="context-menu">
|
||||
|
||||
</div>
|
||||
|
||||
<div id="profile-popup-container" class="profile-popup-container" style="display: none;">
|
||||
<div class="profile-popup-bg" onclick="closeProfile()"></div>
|
||||
<div class="profile-popup-content">
|
||||
<button class="profile-popup-close-btn" onclick="closeProfile()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.close());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
</button>
|
||||
<div id="profile-popup-inner"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cropper-popup-container" class="profile-popup-container" style="z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeCropper()"></div>
|
||||
<div class="profile-popup-content" style="max-width: 40rem; padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; height: auto;">
|
||||
<h2><blah>title.crop.image</blah></h2>
|
||||
<div id="cropper-viewport-container" style="position: relative; overflow: hidden; background: #000; width: 100%; max-width: 30rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255,255,255,0.2);">
|
||||
<img id="cropper-image" src="" style="position: absolute; transform-origin: 0 0; cursor: move;">
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem; margin-top: 1rem; width: 100%; max-width: 30rem;">
|
||||
<button class="submit-button blah" style="background: rgba(255,255,255,0.1); color: var(--text-color); flex: 1;" onclick="closeCropper()">action.cancel</button>
|
||||
<button class="submit-button blah" style="flex: 1;" onclick="confirmCropper()">action.save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="generic-popup-container" class="profile-popup-container" style="display: none; z-index: 9999;">
|
||||
<div class="profile-popup-bg" onclick="closeGenericPopup()"></div>
|
||||
<div class="profile-popup-content popup-auto-height">
|
||||
<button class="profile-popup-close-btn" onclick="closeGenericPopup()">
|
||||
<script>
|
||||
document.currentScript.insertAdjacentHTML('beforebegin', ICONS.close());
|
||||
document.currentScript.remove();
|
||||
</script>
|
||||
</button>
|
||||
<div id="generic-popup-inner" style="padding: 2rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; width: 100%; box-sizing: border-box;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script src="version.js"></script>
|
||||
<script src="screens.js"></script>
|
||||
<script>
|
||||
async function start() {
|
||||
updateLoadingStatus("loading.loading");
|
||||
try {
|
||||
gotoHome();
|
||||
if (host != null)
|
||||
{
|
||||
await updateProtocolAndUrl(host);
|
||||
|
|
@ -147,21 +94,13 @@
|
|||
{
|
||||
await updateProtocolAndUrl(window.location.hostname);
|
||||
}
|
||||
let localIdRes = await fetchAsync(`${url}/nametoid?u=${username}`);
|
||||
let localId = localIdRes.trim();
|
||||
sidebarPfp.src = await getAvatarUrl(localId, `${username}:${host}`);
|
||||
sidebarPfp.src = await getAvatarUrl(username);
|
||||
|
||||
showAction("action.auth", "startauth");
|
||||
showAction("Authenticating...", "startauth");
|
||||
let res = await Auth(username, password);
|
||||
clearAction("startauth");
|
||||
if (res.startsWith("success:")) {
|
||||
try {
|
||||
await ensureUserKeys();
|
||||
} catch (e) {
|
||||
//if fails continue loading encryption may be broken
|
||||
console.error(e);
|
||||
}
|
||||
gotoHome();
|
||||
await refreshDms();
|
||||
} else {
|
||||
showBlahNotification("error:auth.failed.redirect.to.login");
|
||||
await delay(2000);
|
||||
|
|
@ -177,39 +116,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function refreshDms(waittime = 0, showLoading = true) {
|
||||
async function refreshDms() {
|
||||
try {
|
||||
if (showLoading) showAction("action.dm.fetch", "dmrefresh");
|
||||
await delay(waittime);
|
||||
if (window.cachedDms && typeof renderDms === 'function') {
|
||||
await renderDms(window.cachedDms);
|
||||
}
|
||||
let res = await fetchEncrypted("user/dm/list");
|
||||
window.cachedDms = res;
|
||||
if (typeof renderDms === 'function') {
|
||||
await renderDms(res);
|
||||
}
|
||||
if (showLoading) clearAction("dmrefresh");
|
||||
|
||||
|
||||
showAction("Refreshing dms...", "dmrefresh");
|
||||
let res = await fetchEncrypted("user/dm/list", "");
|
||||
console.log(res);
|
||||
clearAction("dmrefresh");
|
||||
}
|
||||
catch (e) {
|
||||
if (showLoading) clearAction("dmrefresh");
|
||||
clearAction("dmrefresh");
|
||||
showBlahNotification("error:dm.refresh.failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function addDm() {
|
||||
try {
|
||||
showAction("action.dm.adding", "dmadd");
|
||||
let username = document.getElementById("addchat-username").value;
|
||||
let idRes = await fetchAsync(`${url}/nametoid?u=${username}`);
|
||||
if (idRes.startsWith("error") || idRes.trim() === "0" || idRes.trim() === "") {
|
||||
clearAction("dmadd");
|
||||
showBlahNotification("error:user.not.found");
|
||||
return;
|
||||
}
|
||||
let targetId = idRes.trim();
|
||||
let res = await fetchEncrypted("user/dm/invite", targetId);
|
||||
|
||||
showAction("Adding...", "dmadd");
|
||||
let res = await fetchEncrypted("user/dm/invite", document.getElementById("addchat-username").value);
|
||||
console.log(res);
|
||||
clearAction("dmadd");
|
||||
showBlahNotification(res);
|
||||
}
|
||||
|
|
@ -220,65 +146,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function refreshGroups(waittime = 0, showLoading = true) {
|
||||
try {
|
||||
if (showLoading) showAction("action.group.fetch", "grouprefresh");
|
||||
await delay(waittime);
|
||||
if (window.cachedGroups && typeof renderGroups === 'function') {
|
||||
await renderGroups(window.cachedGroups);
|
||||
}
|
||||
let res = await fetchEncrypted("user/group/list");
|
||||
window.cachedGroups = res;
|
||||
if (typeof renderGroups === 'function') {
|
||||
await renderGroups(res);
|
||||
}
|
||||
if (showLoading) clearAction("grouprefresh");
|
||||
}
|
||||
catch (e) {
|
||||
if (showLoading) clearAction("grouprefresh");
|
||||
showBlahNotification("error:group.refresh.failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function createGroup() {
|
||||
try {
|
||||
let groupName = document.getElementById("creategroup-name").value;
|
||||
if (!groupName || groupName.trim() === "") {
|
||||
showBlahNotification("error:group.name.empty");
|
||||
return;
|
||||
}
|
||||
|
||||
showAction("action.group.creating", "group.action");
|
||||
|
||||
await ensureUserKeys();
|
||||
const roomKey = randomRoomKey256Chars();
|
||||
const selfWrapped = await wrapRoomKeyForSelf(roomKey);
|
||||
|
||||
let historyVisible = document.getElementById("creategroup-history") ? document.getElementById("creategroup-history").checked.toString() : "false";
|
||||
|
||||
let payload = JSON.stringify({
|
||||
string1: groupName,
|
||||
string2: selfWrapped,
|
||||
string3: historyVisible
|
||||
});
|
||||
|
||||
let res = await fetchEncrypted("group/create", payload);
|
||||
clearAction("group.action");
|
||||
|
||||
if (res.startsWith("success:")) {
|
||||
showBlahNotification("success:group.created");
|
||||
await refreshGroups(200, false);
|
||||
} else {
|
||||
showBlahNotification(res || "error:something.wrong");
|
||||
}
|
||||
} catch (e) {
|
||||
clearAction("group.action");
|
||||
showBlahNotification("error:something.wrong");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script src="gifuct.js"></script>
|
||||
<script src="voice.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script src="userscript.js"></script>
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
<title>Miarven - Login</title>
|
||||
<link rel="stylesheet" href="../style.css">
|
||||
<link rel="icon" type="image/svg+xml" href="../favicon.svg">
|
||||
<script src="../crypto-pq.js"></script>
|
||||
<style>
|
||||
html {
|
||||
font-size: max(17px, calc(100vw / 100));
|
||||
|
|
@ -163,79 +162,79 @@
|
|||
<main style="display: none;">
|
||||
<div class="auth-card">
|
||||
<div class="auth-container" id="auth-container">
|
||||
|
||||
|
||||
<div class="auth-box" id="login-box">
|
||||
<div class="auth-header">
|
||||
<h1>Larpix</h1>
|
||||
<p><blah>title.sign.in.to</blah></p>
|
||||
</div>
|
||||
|
||||
|
||||
<form id="form-login">
|
||||
<div class="input-group">
|
||||
<label for="login-host"><blah>title.server.host</blah></label>
|
||||
<input type="text" id="login-host" placeholder="example.com" autocomplete="url" onchange="loginHostChanged()">
|
||||
<p class="mininote serverstatus"><blah>loading.connecting</blah></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="login-username"><blah>title.username</blah></label>
|
||||
<input type="text" id="login-username" placeholder="{blah(placeholder.username)}" autocomplete="username">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="login-password"><blah>title.password</blah></label>
|
||||
<input type="password" id="login-password" placeholder="••••••••" autocomplete="current-password">
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="submit-button blah">title.sign.in</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="footer-links">
|
||||
<span class="footer-link-toggle" id="to-register">
|
||||
<blah>title.new.here.?</blah> <strong class="blah">title.create.an.account</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="auth-box" id="register-box">
|
||||
<div class="auth-header">
|
||||
<h1><blah>join.larpix</blah></h1>
|
||||
<p><blah>title.create.new.account</blah></p>
|
||||
</div>
|
||||
|
||||
|
||||
<form id="form-register">
|
||||
<div class="input-group">
|
||||
<label for="reg-host"><blah>title.server.host</blah></label>
|
||||
<input type="text" id="reg-host" placeholder="example.com" autocomplete="url" onchange="regHostChanged()">
|
||||
<p class="mininote serverstatus"><blah>loading.connecting</blah></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="reg-username"><blah>title.username</blah></label>
|
||||
<input type="text" id="reg-username" placeholder="{blah(placeholder.username)}" autocomplete="username">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="reg-password"><blah>title.password</blah></label>
|
||||
<input type="password" id="reg-password" placeholder="••••••••" autocomplete="new-password">
|
||||
<p class="red mininote"><blah>notice.use.strong.pass</blah></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="input-group">
|
||||
<label for="reg-confirm"><blah>title.confirm.password</blah></label>
|
||||
<input type="password" id="reg-confirm" placeholder="••••••••" autocomplete="new-password">
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="submit-button blah" id="register-button">title.sign.up</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="footer-links">
|
||||
<span class="footer-link-toggle" id="to-login">
|
||||
<blah>title.already.registered.?</blah> <strong class="blah">title.back.to.login</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="auth-box" id="captcha-box">
|
||||
<div class="auth-header">
|
||||
<h1><blah>title.verify</blah></h1>
|
||||
|
|
@ -268,9 +267,10 @@
|
|||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<script src="../main.js"></script>
|
||||
<script>
|
||||
|
||||
const authContainer = document.getElementById('auth-container');
|
||||
const container = document.getElementById('auth-container');
|
||||
|
||||
|
||||
const toRegister = document.getElementById('to-register');
|
||||
|
|
@ -285,70 +285,66 @@
|
|||
const registerHost = document.getElementById("reg-host");
|
||||
const registerUsername = document.getElementById("reg-username");
|
||||
const registerPassword = document.getElementById("reg-password");
|
||||
const registerPasswordConfirm = document.getElementById("reg-confirm");
|
||||
const registerPasswordConfirm = document.getElementById("reg-password");
|
||||
const loginHost = document.getElementById("login-host");
|
||||
const loginUsername = document.getElementById("login-username");
|
||||
const loginPassword = document.getElementById("login-password");
|
||||
const captchaCode = document.getElementById("captcha-input");
|
||||
const regKeyInput = document.getElementById("regkey-input");
|
||||
|
||||
|
||||
const registerButton = document.getElementById("register-button");
|
||||
const regKeyGroup = document.getElementById("regkey-group");
|
||||
|
||||
|
||||
|
||||
|
||||
async function start() {
|
||||
updateLoadingStatus("loading.loading");
|
||||
//
|
||||
updateLoadingStatus("loading.done");
|
||||
await loadingFadeOut();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
toRegister.addEventListener('click', () => {
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
});
|
||||
|
||||
toLogin.addEventListener('click', () => {
|
||||
authContainer.className = 'auth-container';
|
||||
container.className = 'auth-container';
|
||||
});
|
||||
|
||||
backToReg.addEventListener('click', () => {
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
});
|
||||
|
||||
|
||||
formLogin.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await updateProtocolAndUrl(loginHost.value);
|
||||
let res = await Auth(loginUsername.value, loginPassword.value);
|
||||
|
||||
let res = await Auth(loginUsername.value, loginPassword.value);
|
||||
|
||||
if (res.startsWith("success:"))
|
||||
{
|
||||
let parts = res.split("|");
|
||||
showBlahNotification(parts[0]);
|
||||
showBlahNotification(res);
|
||||
await delay(800);
|
||||
localStorage.setItem("username", loginUsername.value);
|
||||
localStorage.setItem("password", loginPassword.value);
|
||||
localStorage.setItem("host", loginHost.value);
|
||||
if (parts.length > 1) {
|
||||
localStorage.setItem("id", parts[1]);
|
||||
}
|
||||
|
||||
await loadingFadeIn();
|
||||
|
||||
|
||||
window.location.href = "../";
|
||||
}
|
||||
else
|
||||
{
|
||||
showBlahNotification(res);
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
showBlahNotification("error:something.wrong.mayb.pass");
|
||||
authContainer.className = 'auth-container';
|
||||
container.className = 'auth-container';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -362,41 +358,30 @@
|
|||
|
||||
if (!registerUsername.value || registerUsername.value.trim() === '') {
|
||||
showBlahNotification("error:username.cant.empty");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
return;
|
||||
}
|
||||
if (!registerPassword.value || registerPassword.value.trim() === '') {
|
||||
showBlahNotification("error:password.cant.empty");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
return;
|
||||
}
|
||||
if (registerPassword.value != registerPasswordConfirm.value) {
|
||||
showBlahNotification("error:passwords.not.match");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
return;
|
||||
}
|
||||
|
||||
await updateProtocolAndUrl(registerHost.value);
|
||||
|
||||
let dataarray = keyDataFromServerJson(await fetchAsync(`${url}/createaccount?step=init`));
|
||||
var sharedkey = await calcHybridSharedKeyClient(dataarray[0], dataarray[1]);
|
||||
sharedpvkey = sharedkey[2];
|
||||
createId = dataarray[2];
|
||||
//sól z init jest wymagana - hasło zawsze przez PBKDF2
|
||||
if (!dataarray[3]) {
|
||||
showBlahNotification("error:something.wrong");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
return;
|
||||
}
|
||||
let regCredHash = await deriveCredentialKdf(registerPassword.value, dataarray[3]);
|
||||
var sharedkey = await calcCommunicationKeyClient(dataarray[0], dataarray[1], dataarray[2]);
|
||||
sharedpvkey = sharedkey[1];
|
||||
createId = dataarray[3];
|
||||
const captchaimage = await fetch(`${url}/createaccount?step=register`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
pubX25519: sharedkey[0],
|
||||
ciphertextMlKem: sharedkey[1],
|
||||
pubClient: sharedkey[0],
|
||||
idKey: createId,
|
||||
username: await encrypt(registerUsername.value, sharedpvkey),
|
||||
password: await encrypt(regCredHash, sharedpvkey)
|
||||
password: await encrypt(await hashSHA3_512(registerPassword.value), sharedpvkey)
|
||||
})
|
||||
});
|
||||
const clone = captchaimage.clone();
|
||||
|
|
@ -408,20 +393,20 @@
|
|||
}
|
||||
const imageObjectURL = URL.createObjectURL(imageBlob);
|
||||
document.getElementById('captcha-image').src = imageObjectURL;
|
||||
authContainer.className = 'auth-container show-captcha';
|
||||
container.className = 'auth-container show-captcha';
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
if (res.length > 64 || res.length <= 1) {
|
||||
throw new Error();
|
||||
}
|
||||
showBlahNotification(res);
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
showBlahNotification("error:something.wrong");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -443,12 +428,6 @@
|
|||
if (res.startsWith("success")) {
|
||||
showBlahNotification(res);
|
||||
await delay(1000);
|
||||
|
||||
let resolvedId = await fetchAsync(`${url}/nametoid?u=${registerUsername.value}`);
|
||||
if (resolvedId && !resolvedId.startsWith("error:")) {
|
||||
localStorage.setItem("id", resolvedId.split(":")[0]);
|
||||
}
|
||||
|
||||
localStorage.setItem("username", registerUsername.value);
|
||||
localStorage.setItem("password", registerPassword.value);
|
||||
localStorage.setItem("host", registerHost.value);
|
||||
|
|
@ -457,15 +436,15 @@
|
|||
|
||||
} else {
|
||||
showBlahNotification(res);
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
showBlahNotification("error:something.wrong");
|
||||
authContainer.className = 'auth-container show-register';
|
||||
container.className = 'auth-container show-register';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function regHostChanged()
|
||||
{
|
||||
loginHost.value = registerHost.value;
|
||||
|
|
@ -502,7 +481,7 @@
|
|||
setServerStatus("Can't ping this server", "red");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setServerStatus(status, color)
|
||||
{
|
||||
let elements = document.getElementsByClassName("serverstatus");
|
||||
|
|
@ -513,5 +492,4 @@
|
|||
}
|
||||
|
||||
</script>
|
||||
<script src="../main.js"></script>
|
||||
<script src="../userscript.js"></script>
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,807 +0,0 @@
|
|||
// (c) Dean McNamee <dean@gmail.com>, 2013.
|
||||
//
|
||||
// https://github.com/deanm/omggif
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
// omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
|
||||
// including animation and compression. It does not rely on any specific
|
||||
// underlying system, so should run in the browser, Node, or Plask.
|
||||
|
||||
"use strict";
|
||||
|
||||
function GifWriter(buf, width, height, gopts) {
|
||||
var p = 0;
|
||||
|
||||
var gopts = gopts === undefined ? { } : gopts;
|
||||
var loop_count = gopts.loop === undefined ? null : gopts.loop;
|
||||
var global_palette = gopts.palette === undefined ? null : gopts.palette;
|
||||
|
||||
if (width <= 0 || height <= 0 || width > 65535 || height > 65535)
|
||||
throw new Error("Width/Height invalid.");
|
||||
|
||||
function check_palette_and_num_colors(palette) {
|
||||
var num_colors = palette.length;
|
||||
if (num_colors < 2 || num_colors > 256 || num_colors & (num_colors-1)) {
|
||||
throw new Error(
|
||||
"Invalid code/color length, must be power of 2 and 2 .. 256.");
|
||||
}
|
||||
return num_colors;
|
||||
}
|
||||
|
||||
// - Header.
|
||||
buf[p++] = 0x47; buf[p++] = 0x49; buf[p++] = 0x46; // GIF
|
||||
buf[p++] = 0x38; buf[p++] = 0x39; buf[p++] = 0x61; // 89a
|
||||
|
||||
// Handling of Global Color Table (palette) and background index.
|
||||
var gp_num_colors_pow2 = 0;
|
||||
var background = 0;
|
||||
if (global_palette !== null) {
|
||||
var gp_num_colors = check_palette_and_num_colors(global_palette);
|
||||
while (gp_num_colors >>= 1) ++gp_num_colors_pow2;
|
||||
gp_num_colors = 1 << gp_num_colors_pow2;
|
||||
--gp_num_colors_pow2;
|
||||
if (gopts.background !== undefined) {
|
||||
background = gopts.background;
|
||||
if (background >= gp_num_colors)
|
||||
throw new Error("Background index out of range.");
|
||||
// The GIF spec states that a background index of 0 should be ignored, so
|
||||
// this is probably a mistake and you really want to set it to another
|
||||
// slot in the palette. But actually in the end most browsers, etc end
|
||||
// up ignoring this almost completely (including for dispose background).
|
||||
if (background === 0)
|
||||
throw new Error("Background index explicitly passed as 0.");
|
||||
}
|
||||
}
|
||||
|
||||
// - Logical Screen Descriptor.
|
||||
// NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
|
||||
buf[p++] = width & 0xff; buf[p++] = width >> 8 & 0xff;
|
||||
buf[p++] = height & 0xff; buf[p++] = height >> 8 & 0xff;
|
||||
// NOTE: Indicates 0-bpp original color resolution (unused?).
|
||||
buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
|
||||
gp_num_colors_pow2; // NOTE: No sort flag (unused?).
|
||||
buf[p++] = background; // Background Color Index.
|
||||
buf[p++] = 0; // Pixel aspect ratio (unused?).
|
||||
|
||||
// - Global Color Table
|
||||
if (global_palette !== null) {
|
||||
for (var i = 0, il = global_palette.length; i < il; ++i) {
|
||||
var rgb = global_palette[i];
|
||||
buf[p++] = rgb >> 16 & 0xff;
|
||||
buf[p++] = rgb >> 8 & 0xff;
|
||||
buf[p++] = rgb & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_count !== null) { // Netscape block for looping.
|
||||
if (loop_count < 0 || loop_count > 65535)
|
||||
throw new Error("Loop count invalid.")
|
||||
// Extension code, label, and length.
|
||||
buf[p++] = 0x21; buf[p++] = 0xff; buf[p++] = 0x0b;
|
||||
// NETSCAPE2.0
|
||||
buf[p++] = 0x4e; buf[p++] = 0x45; buf[p++] = 0x54; buf[p++] = 0x53;
|
||||
buf[p++] = 0x43; buf[p++] = 0x41; buf[p++] = 0x50; buf[p++] = 0x45;
|
||||
buf[p++] = 0x32; buf[p++] = 0x2e; buf[p++] = 0x30;
|
||||
// Sub-block
|
||||
buf[p++] = 0x03; buf[p++] = 0x01;
|
||||
buf[p++] = loop_count & 0xff; buf[p++] = loop_count >> 8 & 0xff;
|
||||
buf[p++] = 0x00; // Terminator.
|
||||
}
|
||||
|
||||
|
||||
var ended = false;
|
||||
|
||||
this.addFrame = function(x, y, w, h, indexed_pixels, opts) {
|
||||
if (ended === true) { --p; ended = false; } // Un-end.
|
||||
|
||||
opts = opts === undefined ? { } : opts;
|
||||
|
||||
// TODO(deanm): Bounds check x, y. Do they need to be within the virtual
|
||||
// canvas width/height, I imagine?
|
||||
if (x < 0 || y < 0 || x > 65535 || y > 65535)
|
||||
throw new Error("x/y invalid.")
|
||||
|
||||
if (w <= 0 || h <= 0 || w > 65535 || h > 65535)
|
||||
throw new Error("Width/Height invalid.")
|
||||
|
||||
if (indexed_pixels.length < w * h)
|
||||
throw new Error("Not enough pixels for the frame size.");
|
||||
|
||||
var using_local_palette = true;
|
||||
var palette = opts.palette;
|
||||
if (palette === undefined || palette === null) {
|
||||
using_local_palette = false;
|
||||
palette = global_palette;
|
||||
}
|
||||
|
||||
if (palette === undefined || palette === null)
|
||||
throw new Error("Must supply either a local or global palette.");
|
||||
|
||||
var num_colors = check_palette_and_num_colors(palette);
|
||||
|
||||
// Compute the min_code_size (power of 2), destroying num_colors.
|
||||
var min_code_size = 0;
|
||||
while (num_colors >>= 1) ++min_code_size;
|
||||
num_colors = 1 << min_code_size; // Now we can easily get it back.
|
||||
|
||||
var delay = opts.delay === undefined ? 0 : opts.delay;
|
||||
|
||||
// From the spec:
|
||||
// 0 - No disposal specified. The decoder is
|
||||
// not required to take any action.
|
||||
// 1 - Do not dispose. The graphic is to be left
|
||||
// in place.
|
||||
// 2 - Restore to background color. The area used by the
|
||||
// graphic must be restored to the background color.
|
||||
// 3 - Restore to previous. The decoder is required to
|
||||
// restore the area overwritten by the graphic with
|
||||
// what was there prior to rendering the graphic.
|
||||
// 4-7 - To be defined.
|
||||
// NOTE(deanm): Dispose background doesn't really work, apparently most
|
||||
// browsers ignore the background palette index and clear to transparency.
|
||||
var disposal = opts.disposal === undefined ? 0 : opts.disposal;
|
||||
if (disposal < 0 || disposal > 3) // 4-7 is reserved.
|
||||
throw new Error("Disposal out of range.");
|
||||
|
||||
var use_transparency = false;
|
||||
var transparent_index = 0;
|
||||
if (opts.transparent !== undefined && opts.transparent !== null) {
|
||||
use_transparency = true;
|
||||
transparent_index = opts.transparent;
|
||||
if (transparent_index < 0 || transparent_index >= num_colors)
|
||||
throw new Error("Transparent color index.");
|
||||
}
|
||||
|
||||
if (disposal !== 0 || use_transparency || delay !== 0) {
|
||||
// - Graphics Control Extension
|
||||
buf[p++] = 0x21; buf[p++] = 0xf9; // Extension / Label.
|
||||
buf[p++] = 4; // Byte size.
|
||||
|
||||
buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
|
||||
buf[p++] = delay & 0xff; buf[p++] = delay >> 8 & 0xff;
|
||||
buf[p++] = transparent_index; // Transparent color index.
|
||||
buf[p++] = 0; // Block Terminator.
|
||||
}
|
||||
|
||||
// - Image Descriptor
|
||||
buf[p++] = 0x2c; // Image Seperator.
|
||||
buf[p++] = x & 0xff; buf[p++] = x >> 8 & 0xff; // Left.
|
||||
buf[p++] = y & 0xff; buf[p++] = y >> 8 & 0xff; // Top.
|
||||
buf[p++] = w & 0xff; buf[p++] = w >> 8 & 0xff;
|
||||
buf[p++] = h & 0xff; buf[p++] = h >> 8 & 0xff;
|
||||
// NOTE: No sort flag (unused?).
|
||||
// TODO(deanm): Support interlace.
|
||||
buf[p++] = using_local_palette === true ? (0x80 | (min_code_size-1)) : 0;
|
||||
|
||||
// - Local Color Table
|
||||
if (using_local_palette === true) {
|
||||
for (var i = 0, il = palette.length; i < il; ++i) {
|
||||
var rgb = palette[i];
|
||||
buf[p++] = rgb >> 16 & 0xff;
|
||||
buf[p++] = rgb >> 8 & 0xff;
|
||||
buf[p++] = rgb & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
p = GifWriterOutputLZWCodeStream(
|
||||
buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
this.end = function() {
|
||||
if (ended === false) {
|
||||
buf[p++] = 0x3b; // Trailer.
|
||||
ended = true;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
this.getOutputBuffer = function() { return buf; };
|
||||
this.setOutputBuffer = function(v) { buf = v; };
|
||||
this.getOutputBufferPosition = function() { return p; };
|
||||
this.setOutputBufferPosition = function(v) { p = v; };
|
||||
}
|
||||
|
||||
// Main compression routine, palette indexes -> LZW code stream.
|
||||
// |index_stream| must have at least one entry.
|
||||
function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
|
||||
buf[p++] = min_code_size;
|
||||
var cur_subblock = p++; // Pointing at the length field.
|
||||
|
||||
var clear_code = 1 << min_code_size;
|
||||
var code_mask = clear_code - 1;
|
||||
var eoi_code = clear_code + 1;
|
||||
var next_code = eoi_code + 1;
|
||||
|
||||
var cur_code_size = min_code_size + 1; // Number of bits per code.
|
||||
var cur_shift = 0;
|
||||
// We have at most 12-bit codes, so we should have to hold a max of 19
|
||||
// bits here (and then we would write out).
|
||||
var cur = 0;
|
||||
|
||||
function emit_bytes_to_buffer(bit_block_size) {
|
||||
while (cur_shift >= bit_block_size) {
|
||||
buf[p++] = cur & 0xff;
|
||||
cur >>= 8; cur_shift -= 8;
|
||||
if (p === cur_subblock + 256) { // Finished a subblock.
|
||||
buf[cur_subblock] = 255;
|
||||
cur_subblock = p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emit_code(c) {
|
||||
cur |= c << cur_shift;
|
||||
cur_shift += cur_code_size;
|
||||
emit_bytes_to_buffer(8);
|
||||
}
|
||||
|
||||
// I am not an expert on the topic, and I don't want to write a thesis.
|
||||
// However, it is good to outline here the basic algorithm and the few data
|
||||
// structures and optimizations here that make this implementation fast.
|
||||
// The basic idea behind LZW is to build a table of previously seen runs
|
||||
// addressed by a short id (herein called output code). All data is
|
||||
// referenced by a code, which represents one or more values from the
|
||||
// original input stream. All input bytes can be referenced as the same
|
||||
// value as an output code. So if you didn't want any compression, you
|
||||
// could more or less just output the original bytes as codes (there are
|
||||
// some details to this, but it is the idea). In order to achieve
|
||||
// compression, values greater then the input range (codes can be up to
|
||||
// 12-bit while input only 8-bit) represent a sequence of previously seen
|
||||
// inputs. The decompressor is able to build the same mapping while
|
||||
// decoding, so there is always a shared common knowledge between the
|
||||
// encoding and decoder, which is also important for "timing" aspects like
|
||||
// how to handle variable bit width code encoding.
|
||||
//
|
||||
// One obvious but very important consequence of the table system is there
|
||||
// is always a unique id (at most 12-bits) to map the runs. 'A' might be
|
||||
// 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
|
||||
// can be used for an effecient lookup strategy for the code mapping. We
|
||||
// need to know if a run has been seen before, and be able to map that run
|
||||
// to the output code. Since we start with known unique ids (input bytes),
|
||||
// and then from those build more unique ids (table entries), we can
|
||||
// continue this chain (almost like a linked list) to always have small
|
||||
// integer values that represent the current byte chains in the encoder.
|
||||
// This means instead of tracking the input bytes (AAAABCD) to know our
|
||||
// current state, we can track the table entry for AAAABC (it is guaranteed
|
||||
// to exist by the nature of the algorithm) and the next character D.
|
||||
// Therefor the tuple of (table_entry, byte) is guaranteed to also be
|
||||
// unique. This allows us to create a simple lookup key for mapping input
|
||||
// sequences to codes (table indices) without having to store or search
|
||||
// any of the code sequences. So if 'AAAA' has a table entry of 12, the
|
||||
// tuple of ('AAAA', K) for any input byte K will be unique, and can be our
|
||||
// key. This leads to a integer value at most 20-bits, which can always
|
||||
// fit in an SMI value and be used as a fast sparse array / object key.
|
||||
|
||||
// Output code for the current contents of the index buffer.
|
||||
var ib_code = index_stream[0] & code_mask; // Load first input index.
|
||||
var code_table = { }; // Key'd on our 20-bit "tuple".
|
||||
|
||||
emit_code(clear_code); // Spec says first code should be a clear code.
|
||||
|
||||
// First index already loaded, process the rest of the stream.
|
||||
for (var i = 1, il = index_stream.length; i < il; ++i) {
|
||||
var k = index_stream[i] & code_mask;
|
||||
var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
|
||||
var cur_code = code_table[cur_key]; // buffer + k.
|
||||
|
||||
// Check if we have to create a new code table entry.
|
||||
if (cur_code === undefined) { // We don't have buffer + k.
|
||||
// Emit index buffer (without k).
|
||||
// This is an inline version of emit_code, because this is the core
|
||||
// writing routine of the compressor (and V8 cannot inline emit_code
|
||||
// because it is a closure here in a different context). Additionally
|
||||
// we can call emit_byte_to_buffer less often, because we can have
|
||||
// 30-bits (from our 31-bit signed SMI), and we know our codes will only
|
||||
// be 12-bits, so can safely have 18-bits there without overflow.
|
||||
// emit_code(ib_code);
|
||||
cur |= ib_code << cur_shift;
|
||||
cur_shift += cur_code_size;
|
||||
while (cur_shift >= 8) {
|
||||
buf[p++] = cur & 0xff;
|
||||
cur >>= 8; cur_shift -= 8;
|
||||
if (p === cur_subblock + 256) { // Finished a subblock.
|
||||
buf[cur_subblock] = 255;
|
||||
cur_subblock = p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_code === 4096) { // Table full, need a clear.
|
||||
emit_code(clear_code);
|
||||
next_code = eoi_code + 1;
|
||||
cur_code_size = min_code_size + 1;
|
||||
code_table = { };
|
||||
} else { // Table not full, insert a new entry.
|
||||
// Increase our variable bit code sizes if necessary. This is a bit
|
||||
// tricky as it is based on "timing" between the encoding and
|
||||
// decoder. From the encoders perspective this should happen after
|
||||
// we've already emitted the index buffer and are about to create the
|
||||
// first table entry that would overflow our current code bit size.
|
||||
if (next_code >= (1 << cur_code_size)) ++cur_code_size;
|
||||
code_table[cur_key] = next_code++; // Insert into code table.
|
||||
}
|
||||
|
||||
ib_code = k; // Index buffer to single input k.
|
||||
} else {
|
||||
ib_code = cur_code; // Index buffer to sequence in code table.
|
||||
}
|
||||
}
|
||||
|
||||
emit_code(ib_code); // There will still be something in the index buffer.
|
||||
emit_code(eoi_code); // End Of Information.
|
||||
|
||||
// Flush / finalize the sub-blocks stream to the buffer.
|
||||
emit_bytes_to_buffer(1);
|
||||
|
||||
// Finish the sub-blocks, writing out any unfinished lengths and
|
||||
// terminating with a sub-block of length 0. If we have already started
|
||||
// but not yet used a sub-block it can just become the terminator.
|
||||
if (cur_subblock + 1 === p) { // Started but unused.
|
||||
buf[cur_subblock] = 0;
|
||||
} else { // Started and used, write length and additional terminator block.
|
||||
buf[cur_subblock] = p - cur_subblock - 1;
|
||||
buf[p++] = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
function GifReader(buf) {
|
||||
var p = 0;
|
||||
|
||||
// - Header (GIF87a or GIF89a).
|
||||
if (buf[p++] !== 0x47 || buf[p++] !== 0x49 || buf[p++] !== 0x46 ||
|
||||
buf[p++] !== 0x38 || (buf[p++]+1 & 0xfd) !== 0x38 || buf[p++] !== 0x61) {
|
||||
throw new Error("Invalid GIF 87a/89a header.");
|
||||
}
|
||||
|
||||
// - Logical Screen Descriptor.
|
||||
var width = buf[p++] | buf[p++] << 8;
|
||||
var height = buf[p++] | buf[p++] << 8;
|
||||
var pf0 = buf[p++]; // <Packed Fields>.
|
||||
var global_palette_flag = pf0 >> 7;
|
||||
var num_global_colors_pow2 = pf0 & 0x7;
|
||||
var num_global_colors = 1 << (num_global_colors_pow2 + 1);
|
||||
var background = buf[p++];
|
||||
buf[p++]; // Pixel aspect ratio (unused?).
|
||||
|
||||
var global_palette_offset = null;
|
||||
var global_palette_size = null;
|
||||
|
||||
if (global_palette_flag) {
|
||||
global_palette_offset = p;
|
||||
global_palette_size = num_global_colors;
|
||||
p += num_global_colors * 3; // Seek past palette.
|
||||
}
|
||||
|
||||
var no_eof = true;
|
||||
|
||||
var frames = [ ];
|
||||
|
||||
var delay = 0;
|
||||
var transparent_index = null;
|
||||
var disposal = 0; // 0 - No disposal specified.
|
||||
var loop_count = null;
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
while (no_eof && p < buf.length) {
|
||||
switch (buf[p++]) {
|
||||
case 0x21: // Graphics Control Extension Block
|
||||
switch (buf[p++]) {
|
||||
case 0xff: // Application specific block
|
||||
// Try if it's a Netscape block (with animation loop counter).
|
||||
if (buf[p ] !== 0x0b || // 21 FF already read, check block size.
|
||||
// NETSCAPE2.0
|
||||
buf[p+1 ] == 0x4e && buf[p+2 ] == 0x45 && buf[p+3 ] == 0x54 &&
|
||||
buf[p+4 ] == 0x53 && buf[p+5 ] == 0x43 && buf[p+6 ] == 0x41 &&
|
||||
buf[p+7 ] == 0x50 && buf[p+8 ] == 0x45 && buf[p+9 ] == 0x32 &&
|
||||
buf[p+10] == 0x2e && buf[p+11] == 0x30 &&
|
||||
// Sub-block
|
||||
buf[p+12] == 0x03 && buf[p+13] == 0x01 && buf[p+16] == 0) {
|
||||
p += 14;
|
||||
loop_count = buf[p++] | buf[p++] << 8;
|
||||
p++; // Skip terminator.
|
||||
} else { // We don't know what it is, just try to get past it.
|
||||
p += 12;
|
||||
while (true) { // Seek through subblocks.
|
||||
var block_size = buf[p++];
|
||||
// Bad block size (ex: undefined from an out of bounds read).
|
||||
if (!(block_size >= 0)) throw Error("Invalid block size");
|
||||
if (block_size === 0) break; // 0 size is terminator
|
||||
p += block_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xf9: // Graphics Control Extension
|
||||
if (buf[p++] !== 0x4 || buf[p+4] !== 0)
|
||||
throw new Error("Invalid graphics extension block.");
|
||||
var pf1 = buf[p++];
|
||||
delay = buf[p++] | buf[p++] << 8;
|
||||
transparent_index = buf[p++];
|
||||
if ((pf1 & 1) === 0) transparent_index = null;
|
||||
disposal = pf1 >> 2 & 0x7;
|
||||
p++; // Skip terminator.
|
||||
break;
|
||||
|
||||
case 0xfe: // Comment Extension.
|
||||
while (true) { // Seek through subblocks.
|
||||
var block_size = buf[p++];
|
||||
// Bad block size (ex: undefined from an out of bounds read).
|
||||
if (!(block_size >= 0)) throw Error("Invalid block size");
|
||||
if (block_size === 0) break; // 0 size is terminator
|
||||
// console.log(buf.slice(p, p+block_size).toString('ascii'));
|
||||
p += block_size;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
"Unknown graphic control label: 0x" + buf[p-1].toString(16));
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2c: // Image Descriptor.
|
||||
var x = buf[p++] | buf[p++] << 8;
|
||||
var y = buf[p++] | buf[p++] << 8;
|
||||
var w = buf[p++] | buf[p++] << 8;
|
||||
var h = buf[p++] | buf[p++] << 8;
|
||||
var pf2 = buf[p++];
|
||||
var local_palette_flag = pf2 >> 7;
|
||||
var interlace_flag = pf2 >> 6 & 1;
|
||||
var num_local_colors_pow2 = pf2 & 0x7;
|
||||
var num_local_colors = 1 << (num_local_colors_pow2 + 1);
|
||||
var palette_offset = global_palette_offset;
|
||||
var palette_size = global_palette_size;
|
||||
var has_local_palette = false;
|
||||
if (local_palette_flag) {
|
||||
var has_local_palette = true;
|
||||
palette_offset = p; // Override with local palette.
|
||||
palette_size = num_local_colors;
|
||||
p += num_local_colors * 3; // Seek past palette.
|
||||
}
|
||||
|
||||
var data_offset = p;
|
||||
|
||||
p++; // codesize
|
||||
while (true) {
|
||||
var block_size = buf[p++];
|
||||
// Bad block size (ex: undefined from an out of bounds read).
|
||||
if (!(block_size >= 0)) throw Error("Invalid block size");
|
||||
if (block_size === 0) break; // 0 size is terminator
|
||||
p += block_size;
|
||||
}
|
||||
|
||||
frames.push({x: x, y: y, width: w, height: h,
|
||||
has_local_palette: has_local_palette,
|
||||
palette_offset: palette_offset,
|
||||
palette_size: palette_size,
|
||||
data_offset: data_offset,
|
||||
data_length: p - data_offset,
|
||||
transparent_index: transparent_index,
|
||||
interlaced: !!interlace_flag,
|
||||
delay: delay,
|
||||
disposal: disposal});
|
||||
break;
|
||||
|
||||
case 0x3b: // Trailer Marker (end of file).
|
||||
no_eof = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error("Unknown gif block: 0x" + buf[p-1].toString(16));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.numFrames = function() {
|
||||
return frames.length;
|
||||
};
|
||||
|
||||
this.loopCount = function() {
|
||||
return loop_count;
|
||||
};
|
||||
|
||||
this.frameInfo = function(frame_num) {
|
||||
if (frame_num < 0 || frame_num >= frames.length)
|
||||
throw new Error("Frame index out of range.");
|
||||
return frames[frame_num];
|
||||
}
|
||||
|
||||
this.decodeAndBlitFrameBGRA = function(frame_num, pixels) {
|
||||
var frame = this.frameInfo(frame_num);
|
||||
var num_pixels = frame.width * frame.height;
|
||||
var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices.
|
||||
GifReaderLZWOutputIndexStream(
|
||||
buf, frame.data_offset, index_stream, num_pixels);
|
||||
var palette_offset = frame.palette_offset;
|
||||
|
||||
// NOTE(deanm): It seems to be much faster to compare index to 256 than
|
||||
// to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in
|
||||
// the profile, not sure if it's related to using a Uint8Array.
|
||||
var trans = frame.transparent_index;
|
||||
if (trans === null) trans = 256;
|
||||
|
||||
// We are possibly just blitting to a portion of the entire frame.
|
||||
// That is a subrect within the framerect, so the additional pixels
|
||||
// must be skipped over after we finished a scanline.
|
||||
var framewidth = frame.width;
|
||||
var framestride = width - framewidth;
|
||||
var xleft = framewidth; // Number of subrect pixels left in scanline.
|
||||
|
||||
// Output indicies of the top left and bottom right corners of the subrect.
|
||||
var opbeg = ((frame.y * width) + frame.x) * 4;
|
||||
var opend = ((frame.y + frame.height) * width + frame.x) * 4;
|
||||
var op = opbeg;
|
||||
|
||||
var scanstride = framestride * 4;
|
||||
|
||||
// Use scanstride to skip past the rows when interlacing. This is skipping
|
||||
// 7 rows for the first two passes, then 3 then 1.
|
||||
if (frame.interlaced === true) {
|
||||
scanstride += width * 4 * 7; // Pass 1.
|
||||
}
|
||||
|
||||
var interlaceskip = 8; // Tracking the row interval in the current pass.
|
||||
|
||||
for (var i = 0, il = index_stream.length; i < il; ++i) {
|
||||
var index = index_stream[i];
|
||||
|
||||
if (xleft === 0) { // Beginning of new scan line
|
||||
op += scanstride;
|
||||
xleft = framewidth;
|
||||
if (op >= opend) { // Catch the wrap to switch passes when interlacing.
|
||||
scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
|
||||
// interlaceskip / 2 * 4 is interlaceskip << 1.
|
||||
op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
|
||||
interlaceskip >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (index === trans) {
|
||||
op += 4;
|
||||
} else {
|
||||
var r = buf[palette_offset + index * 3];
|
||||
var g = buf[palette_offset + index * 3 + 1];
|
||||
var b = buf[palette_offset + index * 3 + 2];
|
||||
pixels[op++] = b;
|
||||
pixels[op++] = g;
|
||||
pixels[op++] = r;
|
||||
pixels[op++] = 255;
|
||||
}
|
||||
--xleft;
|
||||
}
|
||||
};
|
||||
|
||||
// I will go to copy and paste hell one day...
|
||||
this.decodeAndBlitFrameRGBA = function(frame_num, pixels) {
|
||||
var frame = this.frameInfo(frame_num);
|
||||
var num_pixels = frame.width * frame.height;
|
||||
var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices.
|
||||
GifReaderLZWOutputIndexStream(
|
||||
buf, frame.data_offset, index_stream, num_pixels);
|
||||
var palette_offset = frame.palette_offset;
|
||||
|
||||
// NOTE(deanm): It seems to be much faster to compare index to 256 than
|
||||
// to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in
|
||||
// the profile, not sure if it's related to using a Uint8Array.
|
||||
var trans = frame.transparent_index;
|
||||
if (trans === null) trans = 256;
|
||||
|
||||
// We are possibly just blitting to a portion of the entire frame.
|
||||
// That is a subrect within the framerect, so the additional pixels
|
||||
// must be skipped over after we finished a scanline.
|
||||
var framewidth = frame.width;
|
||||
var framestride = width - framewidth;
|
||||
var xleft = framewidth; // Number of subrect pixels left in scanline.
|
||||
|
||||
// Output indicies of the top left and bottom right corners of the subrect.
|
||||
var opbeg = ((frame.y * width) + frame.x) * 4;
|
||||
var opend = ((frame.y + frame.height) * width + frame.x) * 4;
|
||||
var op = opbeg;
|
||||
|
||||
var scanstride = framestride * 4;
|
||||
|
||||
// Use scanstride to skip past the rows when interlacing. This is skipping
|
||||
// 7 rows for the first two passes, then 3 then 1.
|
||||
if (frame.interlaced === true) {
|
||||
scanstride += width * 4 * 7; // Pass 1.
|
||||
}
|
||||
|
||||
var interlaceskip = 8; // Tracking the row interval in the current pass.
|
||||
|
||||
for (var i = 0, il = index_stream.length; i < il; ++i) {
|
||||
var index = index_stream[i];
|
||||
|
||||
if (xleft === 0) { // Beginning of new scan line
|
||||
op += scanstride;
|
||||
xleft = framewidth;
|
||||
if (op >= opend) { // Catch the wrap to switch passes when interlacing.
|
||||
scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
|
||||
// interlaceskip / 2 * 4 is interlaceskip << 1.
|
||||
op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
|
||||
interlaceskip >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (index === trans) {
|
||||
op += 4;
|
||||
} else {
|
||||
var r = buf[palette_offset + index * 3];
|
||||
var g = buf[palette_offset + index * 3 + 1];
|
||||
var b = buf[palette_offset + index * 3 + 2];
|
||||
pixels[op++] = r;
|
||||
pixels[op++] = g;
|
||||
pixels[op++] = b;
|
||||
pixels[op++] = 255;
|
||||
}
|
||||
--xleft;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function GifReaderLZWOutputIndexStream(code_stream, p, output, output_length) {
|
||||
var min_code_size = code_stream[p++];
|
||||
|
||||
var clear_code = 1 << min_code_size;
|
||||
var eoi_code = clear_code + 1;
|
||||
var next_code = eoi_code + 1;
|
||||
|
||||
var cur_code_size = min_code_size + 1; // Number of bits per code.
|
||||
// NOTE: This shares the same name as the encoder, but has a different
|
||||
// meaning here. Here this masks each code coming from the code stream.
|
||||
var code_mask = (1 << cur_code_size) - 1;
|
||||
var cur_shift = 0;
|
||||
var cur = 0;
|
||||
|
||||
var op = 0; // Output pointer.
|
||||
|
||||
var subblock_size = code_stream[p++];
|
||||
|
||||
// TODO(deanm): Would using a TypedArray be any faster? At least it would
|
||||
// solve the fast mode / backing store uncertainty.
|
||||
// var code_table = Array(4096);
|
||||
var code_table = new Int32Array(4096); // Can be signed, we only use 20 bits.
|
||||
|
||||
var prev_code = null; // Track code-1.
|
||||
|
||||
while (true) {
|
||||
// Read up to two bytes, making sure we always 12-bits for max sized code.
|
||||
while (cur_shift < 16) {
|
||||
if (subblock_size === 0) break; // No more data to be read.
|
||||
|
||||
cur |= code_stream[p++] << cur_shift;
|
||||
cur_shift += 8;
|
||||
|
||||
if (subblock_size === 1) { // Never let it get to 0 to hold logic above.
|
||||
subblock_size = code_stream[p++]; // Next subblock.
|
||||
} else {
|
||||
--subblock_size;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(deanm): We should never really get here, we should have received
|
||||
// and EOI.
|
||||
if (cur_shift < cur_code_size)
|
||||
break;
|
||||
|
||||
var code = cur & code_mask;
|
||||
cur >>= cur_code_size;
|
||||
cur_shift -= cur_code_size;
|
||||
|
||||
// TODO(deanm): Maybe should check that the first code was a clear code,
|
||||
// at least this is what you're supposed to do. But actually our encoder
|
||||
// now doesn't emit a clear code first anyway.
|
||||
if (code === clear_code) {
|
||||
// We don't actually have to clear the table. This could be a good idea
|
||||
// for greater error checking, but we don't really do any anyway. We
|
||||
// will just track it with next_code and overwrite old entries.
|
||||
|
||||
next_code = eoi_code + 1;
|
||||
cur_code_size = min_code_size + 1;
|
||||
code_mask = (1 << cur_code_size) - 1;
|
||||
|
||||
// Don't update prev_code ?
|
||||
prev_code = null;
|
||||
continue;
|
||||
} else if (code === eoi_code) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We have a similar situation as the decoder, where we want to store
|
||||
// variable length entries (code table entries), but we want to do in a
|
||||
// faster manner than an array of arrays. The code below stores sort of a
|
||||
// linked list within the code table, and then "chases" through it to
|
||||
// construct the dictionary entries. When a new entry is created, just the
|
||||
// last byte is stored, and the rest (prefix) of the entry is only
|
||||
// referenced by its table entry. Then the code chases through the
|
||||
// prefixes until it reaches a single byte code. We have to chase twice,
|
||||
// first to compute the length, and then to actually copy the data to the
|
||||
// output (backwards, since we know the length). The alternative would be
|
||||
// storing something in an intermediate stack, but that doesn't make any
|
||||
// more sense. I implemented an approach where it also stored the length
|
||||
// in the code table, although it's a bit tricky because you run out of
|
||||
// bits (12 + 12 + 8), but I didn't measure much improvements (the table
|
||||
// entries are generally not the long). Even when I created benchmarks for
|
||||
// very long table entries the complexity did not seem worth it.
|
||||
// The code table stores the prefix entry in 12 bits and then the suffix
|
||||
// byte in 8 bits, so each entry is 20 bits.
|
||||
|
||||
var chase_code = code < next_code ? code : prev_code;
|
||||
|
||||
// Chase what we will output, either {CODE} or {CODE-1}.
|
||||
var chase_length = 0;
|
||||
var chase = chase_code;
|
||||
while (chase > clear_code) {
|
||||
chase = code_table[chase] >> 8;
|
||||
++chase_length;
|
||||
}
|
||||
|
||||
var k = chase;
|
||||
|
||||
var op_end = op + chase_length + (chase_code !== code ? 1 : 0);
|
||||
if (op_end > output_length) {
|
||||
console.log("Warning, gif stream longer than expected.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Already have the first byte from the chase, might as well write it fast.
|
||||
output[op++] = k;
|
||||
|
||||
op += chase_length;
|
||||
var b = op; // Track pointer, writing backwards.
|
||||
|
||||
if (chase_code !== code) // The case of emitting {CODE-1} + k.
|
||||
output[op++] = k;
|
||||
|
||||
chase = chase_code;
|
||||
while (chase_length--) {
|
||||
chase = code_table[chase];
|
||||
output[--b] = chase & 0xff; // Write backwards.
|
||||
chase >>= 8; // Pull down to the prefix code.
|
||||
}
|
||||
|
||||
if (prev_code !== null && next_code < 4096) {
|
||||
code_table[next_code++] = prev_code << 8 | k;
|
||||
// TODO(deanm): Figure out this clearing vs code growth logic better. I
|
||||
// have an feeling that it should just happen somewhere else, for now it
|
||||
// is awkward between when we grow past the max and then hit a clear code.
|
||||
// For now just check if we hit the max 12-bits (then a clear code should
|
||||
// follow, also of course encoded in 12-bits).
|
||||
if (next_code >= code_mask+1 && cur_code_size < 12) {
|
||||
++cur_code_size;
|
||||
code_mask = code_mask << 1 | 1;
|
||||
}
|
||||
}
|
||||
|
||||
prev_code = code;
|
||||
}
|
||||
|
||||
if (op !== output_length) {
|
||||
console.log("Warning, gif stream shorter than expected.");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// CommonJS.
|
||||
try { exports.GifWriter = GifWriter; exports.GifReader = GifReader } catch(e) {}
|
||||
|
|
@ -3,12 +3,12 @@ var splashScreen = `
|
|||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
<img src="favicon.svg" style="width: 6rem">
|
||||
<herotitle><blah>title.welcome.splash</blah></herotitle>
|
||||
<p><blah>desc.welcome.splash</blah>. <span class="aqua">v<span class="clientver aqua">${typeof CLIENT_VERSION !== 'undefined' ? CLIENT_VERSION : '1.0'}</span></span></p>
|
||||
<p><blah>desc.welcome.splash</blah>. <span class="aqua">v<span class="clientver aqua">1.0</span></span></p>
|
||||
</div>
|
||||
`;
|
||||
var addDmScreen = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.addChat({ width: "3rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="3rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M120-160v-600q0-33 23.5-56.5T200-840h480q33 0 56.5 23.5T760-760v203q-10-2-20-2.5t-20-.5q-10 0-20 .5t-20 2.5v-203H200v400h283q-2 10-2.5 20t-.5 20q0 10 .5 20t2.5 20H240L120-160Zm160-440h320v-80H280v80Zm0 160h200v-80H280v80Zm400 280v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM200-360v-400 400Z"/></svg>
|
||||
<herotitle><blah>title.add.chat</blah></herotitle>
|
||||
<p><blah>desc.add.chat</blah></p>
|
||||
<br>
|
||||
|
|
@ -24,25 +24,15 @@ var addDmScreen = `
|
|||
|
||||
var createGroupScreen = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.createGroup({ width: "3rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="3rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M500-482q29-32 44.5-73t15.5-85q0-44-15.5-85T500-798q60 8 100 53t40 105q0 60-40 105t-100 53Zm220 322v-120q0-36-16-68.5T662-406q51 18 94.5 46.5T800-280v120h-80Zm80-280v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80Zm-593-87q-47-47-47-113t47-113q47-47 113-47t113 47q47 47 47 113t-47 113q-47 47-113 47t-113-47ZM0-160v-112q0-34 17.5-62.5T64-378q62-31 126-46.5T320-440q66 0 130 15.5T576-378q29 15 46.5 43.5T640-272v112H0Zm320-400q33 0 56.5-23.5T400-640q0-33-23.5-56.5T320-720q-33 0-56.5 23.5T240-640q0 33 23.5 56.5T320-560ZM80-240h480v-32q0-11-5.5-20T540-306q-54-27-109-40.5T320-360q-56 0-111 13.5T100-306q-9 5-14.5 14T80-272v32Zm240-400Zm0 400Z"/></svg>
|
||||
<herotitle><blah>title.create.group</blah></herotitle>
|
||||
<p><blah>desc.create.group</blah></p>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<label for="creategroup-name"><blah>title.name</blah></label>
|
||||
<input type="text" id="creategroup-name" placeholder="{blah(placeholder.create.group.input)}" class="forminput">
|
||||
|
||||
<div class="setting-row" style="text-align: left;">
|
||||
<div class="setting-info">
|
||||
<span class="setting-title"><blah>title.group.history.visible</blah></span>
|
||||
<span class="setting-desc"><blah>desc.group.history.visible</blah></span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="creategroup-history">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button class="submit-button blah" onclick="createGroup()">title.create.group</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,7 +40,7 @@ var createGroupScreen = `
|
|||
|
||||
var createSpaceScreen = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.createSpace({ width: "3rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="3rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/></svg>
|
||||
<herotitle><blah>title.create.space</blah></herotitle>
|
||||
<p><blah>desc.create.space</blah></p>
|
||||
<br>
|
||||
|
|
@ -65,7 +55,7 @@ var createSpaceScreen = `
|
|||
`;
|
||||
var joinSpaceScreen = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.joinSpace({ width: "3rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="3rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/></svg>
|
||||
<herotitle><blah>title.join.space</blah></herotitle>
|
||||
<p><blah>desc.join.space</blah></p>
|
||||
<br>
|
||||
|
|
@ -83,83 +73,30 @@ var joinSpaceScreen = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
var invitesEmptyState = `
|
||||
${ICONS.invites({ width: "4rem" })}
|
||||
<br>
|
||||
<p><blah>desc.no.invites</blah></p>
|
||||
`;
|
||||
var invitesLoadingState = `
|
||||
${ICONS.invites({ width: "4rem" })}
|
||||
<br>
|
||||
<p><blah>desc.fetching.invites</blah></p>
|
||||
`;
|
||||
|
||||
var invitesScreen = `
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div style="display: flex; gap: 0.5rem; padding: 0.5rem 1rem; border-bottom: var(--border-width) solid var(--light-border-color); flex-shrink: 0;">
|
||||
<button id="tab-invites-received" class="submit-button blah tab-inactive" onclick="switchInvitesTab('received')" style="margin:0; padding: 0 1.5rem;">title.received</button>
|
||||
<button id="tab-invites-sent" class="submit-button blah tab-inactive" onclick="switchInvitesTab('sent')" style="margin:0; padding: 0 1.5rem;">title.sent</button>
|
||||
</div>
|
||||
<div id="invites-container" class="list-container empty">
|
||||
${invitesLoadingState}
|
||||
<div id="invites-container" style="flex-grow: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; opacity: 0.5;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="4rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/></svg>
|
||||
<br>
|
||||
<p><blah>desc.no.invites</blah></p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var notifisEmptyState = `
|
||||
${ICONS.notifications({ width: "4rem" })}
|
||||
<br>
|
||||
<p><blah>desc.no.notifications</blah></p>
|
||||
`;
|
||||
|
||||
var notifisScreen = `
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div style="display: flex; gap: 0.5rem; padding: 0.5rem 1rem; border-bottom: var(--border-width) solid var(--light-border-color); flex-shrink: 0;">
|
||||
<button id="tab-notifis-all" class="submit-button blah tab-inactive" onclick="switchNotifisTab('all')" style="margin:0; padding: 0 1.5rem;">title.all</button>
|
||||
<button id="tab-notifis-unread" class="submit-button blah tab-inactive" onclick="switchNotifisTab('unread')" style="margin:0; padding: 0 1.5rem;">title.unread</button>
|
||||
</div>
|
||||
<div id="notifis-container" class="list-container empty">
|
||||
${notifisEmptyState}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var invitesEntry = `
|
||||
<div class="invite-entry">
|
||||
<div class="invite-info">
|
||||
<img src="{pfp}" class="invite-pfp" {onclick_attr}>
|
||||
<div class="invite-details">
|
||||
<span class="invite-name" {onclick_attr}>{username}</span>
|
||||
<span class="invite-desc">{desc}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invite-actions">
|
||||
{actions}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var chatScreen = `
|
||||
<div style="display: flex; flex-direction: column; height: 100%; width: 100%;">
|
||||
<div id="chat-messages" style="flex-grow: 1; overflow-y: auto; overflow-x: hidden; padding: 1rem; display: flex; flex-direction: column; gap: 0.5rem;">
|
||||
</div>
|
||||
<div id="replying-bar" style="display: none; padding: 0 1rem; border-top: var(--border-width) solid var(--light-border-color); font-size: 0.85rem; justify-content: space-between; align-items: center; background: var(--main-bg-color);">
|
||||
<div style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-grow: 1;">
|
||||
<span style="opacity: 0.7;"><blah>title.replying.to</blah> </span><strong id="replying-to-name"></strong>: <span id="replying-to-text" style="opacity: 0.7;"></span>
|
||||
</div>
|
||||
<button onclick="cancelReply()" style="background: none; border: none; color: var(--text-color); cursor: pointer; padding: 0.2rem; margin-left: 0.5rem;">
|
||||
${ICONS.close({ width: "1.2rem", fill: "currentColor" })}
|
||||
</button>
|
||||
</div>
|
||||
<div id="attachments-preview" style="display: none; padding: 0.5rem 1rem; gap: 0.8rem; overflow-x: auto; border-top: var(--border-width) solid var(--light-border-color); background: var(--main-bg-color); align-items: center; flex-shrink: 0;"></div>
|
||||
<div style="padding: 1rem; border-top: var(--border-width) solid var(--light-border-color); display: flex; gap: 0.5rem; align-items: flex-end; flex-shrink: 0;">
|
||||
<button id="chat-add-attachment" class="submit-button" onclick="clickAddAttachment(this, arguments[0])" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||
${ICONS.plus({ width: "1.5rem", height: "1.5rem", stroke: "var(--main-bg-color)" })}
|
||||
</button>
|
||||
<textarea id="chat-input" class="forminput" style="flex-grow: 1; margin: 0; resize: none; min-height: 2.5rem; max-height: 8rem; padding: 0.5rem;" placeholder="{blah(placeholder.message.input)}" onkeydown="handleChatInputKey(arguments[0])" oninput="handleChatInputResize(this)"></textarea>
|
||||
<button id="chat-sendmessage" class="submit-button" onclick="sendMessage(this)" style="margin: 0; padding: 0; width: 2.5rem; flex-shrink: 0; min-height: 2.5rem; max-height: 8rem;">
|
||||
${ICONS.send()}
|
||||
</button>
|
||||
<div id="notifis-container" style="flex-grow: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center; opacity: 0.5;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="4rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M160-200v-80h80v-280q0-83 50-147.5T420-792v-28q0-25 17.5-42.5T480-880q25 0 42.5 17.5T540-820v28q80 20 130 84.5T720-560v280h80v80H160Zm320-300Zm0 420q-33 0-56.5-23.5T400-160h160q0 33-23.5 56.5T480-80ZM320-280h320v-280q0-66-47-113t-113-47q-66 0-113 47t-47 113v280Z"/></svg>
|
||||
<br>
|
||||
<p><blah>desc.no.notifications</blah></p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -169,45 +106,49 @@ var homeRoomBar = `
|
|||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="collapse-dms" onclick="clickCollapseDms()">
|
||||
<span><blah>title.dms</blah></span>
|
||||
${ICONS.chevronDown({ className: "chevron" })}
|
||||
<svg class="chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="var(--text-color)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="add-action-button" id="add-dm-btn" onclick="clickAddDm()">
|
||||
${ICONS.plus()}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="var(--text-color)"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="dms-wrapper" class="collapsible-wrapper">
|
||||
<div class="collapsible-inner">
|
||||
<div id="dms-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="collapse-groups" onclick="clickCollapseGroups()">
|
||||
<span><blah>title.groups</blah></span>
|
||||
${ICONS.chevronDown({ className: "chevron" })}
|
||||
<svg class="chevron" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||||
stroke="var(--text-color)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="add-action-button" id="add-group-btn" onclick="clickAddGroup()">
|
||||
${ICONS.plus()}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="var(--text-color)"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="groups-wrapper" class="collapsible-wrapper">
|
||||
<div class="collapsible-inner">
|
||||
<div id="groups-list" class="list-container no-flex-grow empty" style="padding: 0 0.5rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var inboxRoomBar = `
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="collapse-invites" onclick="gotoInvites()">
|
||||
${ICONS.invites({ className: "chevron", width: "1.5rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="chevron" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm320-280L160-640v400h640v-400L480-440Zm0-80 320-200H160l320 200ZM160-640v-80 480-400Z"/></svg>
|
||||
<span><blah>title.invites</blah></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="collapse-notifis" onclick="gotoNotifis()">
|
||||
${ICONS.notifications({ className: "chevron", width: "1.5rem" })}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="chevron" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M160-200v-80h80v-280q0-83 50-147.5T420-792v-28q0-25 17.5-42.5T480-880q25 0 42.5 17.5T540-820v28q80 20 130 84.5T720-560v280h80v80H160Zm320-300Zm0 420q-33 0-56.5-23.5T400-160h160q0 33-23.5 56.5T480-80ZM320-280h320v-280q0-66-47-113t-113-47q-66 0-113 47t-47 113v280Z"/></svg>
|
||||
<span><blah>title.notifications</blah></span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -215,215 +156,18 @@ var inboxRoomBar = `
|
|||
|
||||
|
||||
//context menus
|
||||
var profileContextMenu = `
|
||||
<button onclick="openProfile(window.id)">
|
||||
${ICONS.profile()}
|
||||
<span class="blah">title.profile</span>
|
||||
</button>
|
||||
<button onclick="gotoSettings()">
|
||||
${ICONS.settings()}
|
||||
<span class="blah">title.settings</span>
|
||||
</button>
|
||||
<button onclick="logout()">
|
||||
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||
<span class="blah" style="color: var(--big-red)">title.logout</span>
|
||||
</button>
|
||||
`;
|
||||
var attachmentMenu = `
|
||||
<button onclick="document.getElementById('attach-take-photo').click(); fixedContextMenu.classList.remove('show');">
|
||||
${ICONS.camera()}
|
||||
<span class="blah">action.attachment.take.photo</span>
|
||||
</button>
|
||||
<button onclick="document.getElementById('attach-media').click(); fixedContextMenu.classList.remove('show');">
|
||||
${ICONS.image()}
|
||||
<span class="blah">action.attachment.media</span>
|
||||
</button>
|
||||
<button onclick="document.getElementById('attach-file').click(); fixedContextMenu.classList.remove('show');">
|
||||
${ICONS.fileText()}
|
||||
<span class="blah">action.attachment.file</span>
|
||||
</button>
|
||||
<button onclick="console.log('poll'); fixedContextMenu.classList.remove('show');">
|
||||
${ICONS.poll()}
|
||||
<span class="blah">action.attachment.poll</span>
|
||||
</button>
|
||||
<input type="file" id="attach-take-photo" accept="image/*" capture="environment" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
||||
<input type="file" id="attach-media" accept="image/*,video/*" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
||||
<input type="file" id="attach-file" style="display: none;" onchange="handleAttachmentSelection(arguments[0])" multiple>
|
||||
`;
|
||||
|
||||
var addSpaceMenu = `
|
||||
<button onclick="gotoJoinSpace()">
|
||||
${ICONS.joinSpace()}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.4rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm200 160v-80h160q50 0 85-35t35-85q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480q0 83-58.5 141.5T680-280H520Z"/></svg>
|
||||
<span class="blah">title.join.space</span>
|
||||
</button>
|
||||
<button onclick="gotoCreateSpace()">
|
||||
${ICONS.createSpace()}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1.4rem" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M80-120v-720h400v160h400v320h-80v-240H480v80h80v80h-80v80h80v80h-80v80h160v80H80Zm80-80h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm160 480h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80Zm0-160h80v-80h-80v80ZM800-40v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM640-440v-80h80v80h-80Zm0 160v-80h80v80h-80Z"/></svg>
|
||||
<span class="blah">title.create.space</span>
|
||||
</button>`
|
||||
var groupContextMenu = `
|
||||
<button onclick="gotoGroupSettings(currentContextMenuGroupId)">
|
||||
${ICONS.settings()}
|
||||
<span class="blah">title.group.settings</span>
|
||||
</button>
|
||||
<button onclick="leaveGroupPrompt(currentContextMenuGroupId)">
|
||||
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||
<span class="blah" style="color: var(--big-red)">title.leave.group</span>
|
||||
</button>
|
||||
`;
|
||||
var messageContextMenu = `
|
||||
<button onclick="replyMessage(currentContextMenuMsgId)">
|
||||
${ICONS.replyAction()}
|
||||
<span class="blah">title.reply.message</span>
|
||||
</button>
|
||||
<button id="context-react-btn" onclick="openReactionPicker(arguments[0])">
|
||||
${ICONS.react()}
|
||||
<span class="blah">title.react.message</span>
|
||||
</button>
|
||||
<button id="context-delete-btn" onclick="deleteMessage(currentContextMenuMsgId)">
|
||||
${ICONS.delete({ fill: "var(--big-red)" })}
|
||||
<span class="blah" style="color: var(--big-red)">title.delete.message</span>
|
||||
</button>
|
||||
<div id="context-pin-container"></div>
|
||||
`;
|
||||
|
||||
|
||||
//elements
|
||||
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()">${ICONS.arrowBack({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||
var pinBtnHtml = `<button class="topbar-action-btn room-action-pin right" onclick="showPinnedMessages()">${ICONS.pin({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||
var callBtnHtml = `<button class="topbar-action-btn room-action-call right" onclick="initiateVoiceCall()">${ICONS.call({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||
var groupMenuBtnHtml = `<button class="topbar-action-btn right" onclick="handleGroupContextMenu(arguments[0], currentDmId)">${ICONS.menu({ width: "1.5rem", height: "1.5rem" })}</button>`;
|
||||
|
||||
var settingsBar = `
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="tab-settings-account" onclick="switchSettingsTab('account')">
|
||||
${ICONS.account({ className: "chevron", width: "1.5rem" })}
|
||||
<span><blah>title.account</blah></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="tab-settings-profile" onclick="switchSettingsTab('profile')">
|
||||
${ICONS.account({ className: "chevron", width: "1.5rem" })}
|
||||
<span><blah>title.profile</blah></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-section-header">
|
||||
<button class="collapse-text-button" id="tab-settings-appearance" onclick="switchSettingsTab('appearance')">
|
||||
${ICONS.settings({ className: "chevron", width: "1.5rem" })}
|
||||
<span><blah>title.appearance</blah></span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var accountSettings = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.profile({ width: "3rem" })}
|
||||
<herotitle><blah>title.account</blah></herotitle>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<label for="settings-username"><blah>title.username</blah></label>
|
||||
<input type="text" id="settings-username" class="forminput">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button class="submit-button blah" onclick="changeUsername()">action.save</button>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="settings-password"><blah>title.password</blah></label>
|
||||
<input type="password" id="settings-password" class="forminput">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button class="submit-button blah" onclick="changePassword()">action.save</button>
|
||||
</div>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<label style="color: var(--big-red);"><blah>title.delete.account</blah></label>
|
||||
<p style="font-size: 0.85rem; opacity: 0.7; margin-bottom: 0.5rem; margin-left: 0.6rem;"><blah>desc.delete.account</blah></p>
|
||||
<button class="submit-button popup-btn-danger blah" onclick="deleteAccount()">action.delete.account</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var profileSettings = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.account({ width: "3rem" })}
|
||||
<herotitle><blah>title.profile</blah></herotitle>
|
||||
<br>
|
||||
|
||||
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
|
||||
<img id="settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
|
||||
<div style="font-weight: bold;"><blah>title.avatar</blah></div>
|
||||
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-avatar-input').click()">action.change.avatar</button>
|
||||
<input type="file" id="settings-avatar-input" style="display: none;" accept="image/*" onchange="openCropper(this, 'avatar')">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
|
||||
<div style="width: 12.4rem; aspect-ratio: 494 / 200; background: var(--main-bg-color); border-radius: 0.4rem; overflow: hidden; display: flex; align-items: center; justify-content: center; border: var(--border-width) solid rgba(255, 255, 255, 0.2);">
|
||||
<img id="settings-banner-preview" src="" style="width: 100%; height: 100%; object-fit: cover;" onerror="this.style.display='none'" onload="this.style.display='block'">
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
|
||||
<div style="font-weight: bold;"><blah>title.banner</blah></div>
|
||||
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('settings-banner-input').click()">action.change.banner</button>
|
||||
<input type="file" id="settings-banner-input" style="display: none;" accept="image/*" onchange="openCropper(this, 'banner')">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="settings-bio"><blah>title.bio</blah></label>
|
||||
<textarea id="settings-bio" class="forminput" style="min-height: 8rem; padding-top: 0.8rem;"></textarea>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<button class="submit-button blah" onclick="saveProfile()">action.save.bio</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
var groupSettingsScreen = `
|
||||
<div class="auth-box popup-auto-height" style="margin-top:2rem">
|
||||
<div class="input-group" style="display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border: var(--border-width) solid var(--light-border-color); border-radius: 0.8rem; padding: 1rem; width: 100%; max-width: 30rem;">
|
||||
<img id="group-settings-avatar-preview" src="" style="width: 4.5rem; height: 4.5rem; border-radius: 0.8rem; border: var(--border-width) solid rgba(255, 255, 255, 0.2); object-fit: cover; background: var(--main-bg-color); aspect-ratio: 1 / 1; flex-shrink: 0;">
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; padding: 0.1rem 0;">
|
||||
<div style="font-weight: bold;"><blah>title.avatar</blah></div>
|
||||
<button class="submit-button blah" style="margin: 0; padding: 0.5rem 1rem; width: auto;" onclick="document.getElementById('group-settings-avatar-input').click()">action.change.avatar</button>
|
||||
<input type="file" id="group-settings-avatar-input" style="display: none;" accept="image/*" onchange="openCropper(this, 'group-avatar')">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<label for="group-settings-name"><blah>title.group.name</blah></label>
|
||||
<input type="text" id="group-settings-name" class="forminput">
|
||||
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-title"><blah>title.group.history.visible</blah></span>
|
||||
<span class="setting-desc"><blah>desc.group.history.visible</blah></span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="group-settings-history">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button class="submit-button blah" onclick="saveGroupSettings()">action.save</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var appearanceSettings = `
|
||||
<div style="display: flex;justify-content: center;align-items: center;height:100%;flex-direction: column;text-align: center;">
|
||||
${ICONS.settings({ width: "3rem" })}
|
||||
<herotitle><blah>title.appearance</blah></herotitle>
|
||||
<br>
|
||||
<div class="input-group">
|
||||
<label><blah>title.language</blah></label>
|
||||
<div class="custom-select" id="settings-language">
|
||||
<div class="custom-select-value" onclick="this.parentElement.classList.toggle('open')">
|
||||
<span id="settings-language-display" class="blah">title.loading</span>
|
||||
${ICONS.chevronDown({ className: "chevron", style: "width: 1.2rem; height: 1.2rem; opacity: 0.7;" })}
|
||||
</div>
|
||||
<div class="custom-select-options" id="settings-language-options">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
var detailsBtn = `<button class="mobile-nav-btn right" onclick="mobileNavDetails()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg></button>`;
|
||||
var backBtnHtml = `<button class="mobile-nav-btn" onclick="mobileNavBack()"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="var(--text-color)"><path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/></svg></button>`;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,4 @@
|
|||
//write your custom deployment scripts here
|
||||
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
loginHost.value = "olcxja.ovh";
|
||||
loginHostChanged();
|
||||
});
|
||||
loginHost.value = "olcxja.ovh";
|
||||
loginHostChanged();
|
||||
|
|
@ -1 +0,0 @@
|
|||
const CLIENT_VERSION = "0.2.2";
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,75 +0,0 @@
|
|||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
// @ts-ignore
|
||||
import Module from './webp-wasm.js';
|
||||
// default webp config
|
||||
const defaultWebpConfig = {
|
||||
lossless: 0,
|
||||
quality: 100,
|
||||
};
|
||||
export const encoderVersion = () => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
return module.encoder_version();
|
||||
});
|
||||
export const encodeRGB = (rgb, width, height, quality) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
quality = typeof quality !== 'number' ? 100 : Math.min(100, Math.max(0, quality));
|
||||
return module.encodeRGB(rgb, width, height, quality);
|
||||
});
|
||||
export const encodeRGBA = (rgba, width, height, quality) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
quality = typeof quality !== 'number' ? 100 : Math.min(100, Math.max(0, quality));
|
||||
return module.encodeRGBA(rgba, width, height, quality);
|
||||
});
|
||||
export const encode = (data, width, height, hasAlpha, config) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
const webpConfig = Object.assign(Object.assign({}, defaultWebpConfig), config);
|
||||
webpConfig.lossless = Math.min(1, Math.max(0, webpConfig.lossless));
|
||||
webpConfig.quality = Math.min(100, Math.max(0, webpConfig.quality));
|
||||
return module.encode(data, width, height, hasAlpha, webpConfig);
|
||||
});
|
||||
export const encodeAnimation = (width, height, hasAlpha, frames) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
const frameVector = new module.VectorWebPAnimationFrame();
|
||||
frames.forEach((frame) => {
|
||||
const hasConfig = frame.config !== undefined;
|
||||
const config = Object.assign(Object.assign({}, defaultWebpConfig), frame.config);
|
||||
config.lossless = Math.min(1, Math.max(0, config.lossless));
|
||||
config.quality = Math.min(100, Math.max(0, config.quality));
|
||||
frameVector.push_back({
|
||||
duration: frame.duration,
|
||||
data: frame.data,
|
||||
config,
|
||||
has_config: hasConfig,
|
||||
});
|
||||
});
|
||||
return module.encodeAnimation(width, height, hasAlpha, frameVector);
|
||||
});
|
||||
export const decoderVersion = () => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
return module.decoder_version();
|
||||
});
|
||||
export const decodeRGB = (data) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
return module.decodeRGB(data);
|
||||
});
|
||||
export const decodeRGBA = (data) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
return module.decodeRGBA(data);
|
||||
});
|
||||
// TODO:
|
||||
// export const decode = async (data: Uint8Array, hasAlpha: boolean) => {
|
||||
// const module = await Module()
|
||||
// return module.decode(data, hasAlpha)
|
||||
// }
|
||||
export const decodeAnimation = (data, hasAlpha) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const module = yield Module();
|
||||
return module.decodeAnimation(data, hasAlpha);
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -1,18 +0,0 @@
|
|||
import { encodeAnimation, decodeAnimation } from './wasm-webp/index.js';
|
||||
|
||||
self.onmessage = async (e) => {
|
||||
let action = e.data.action || 'encode';
|
||||
try {
|
||||
if (action === 'decode') {
|
||||
let decoded = await decodeAnimation(e.data.data, true);
|
||||
let transferables = decoded.map(f => f.data.buffer);
|
||||
self.postMessage({ success: true, data: decoded, action }, transferables);
|
||||
} else {
|
||||
let { targetWidth, targetHeight, webpFrames } = e.data;
|
||||
let webpUint8 = await encodeAnimation(targetWidth, targetHeight, true, webpFrames);
|
||||
self.postMessage({ success: true, data: webpUint8, action }, [webpUint8.buffer]);
|
||||
}
|
||||
} catch (err) {
|
||||
self.postMessage({ success: false, error: err.toString(), action });
|
||||
}
|
||||
};
|
||||
|
|
@ -9,13 +9,6 @@ public class MainActivity extends BridgeActivity {
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() {
|
||||
// Must be added to the builder BEFORE super.load() creates the bridge,
|
||||
// otherwise the plugin is silently dropped (bridge already built).
|
||||
registerPlugin(VoiceRoutingPlugin.class);
|
||||
super.load();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,496 +0,0 @@
|
|||
package olcxja.miarven;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
import com.getcapacitor.annotation.Permission;
|
||||
import com.getcapacitor.annotation.PermissionCallback;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@CapacitorPlugin(name = "VoiceRouting", permissions = {
|
||||
@Permission(alias = "bluetooth", strings = { Manifest.permission.BLUETOOTH_CONNECT })
|
||||
})
|
||||
public class VoiceRoutingPlugin extends Plugin {
|
||||
|
||||
private static final String TAG = "VoiceRouting";
|
||||
private static final long MODE_GUARD_INTERVAL_MS = 2500;
|
||||
|
||||
private AudioManager audioManager;
|
||||
private boolean inCall = false;
|
||||
private boolean speakerOn = false;
|
||||
private AudioFocusRequest focusRequest26 = null;
|
||||
private PluginCall pendingStartCall = null;
|
||||
private Handler mainHandler;
|
||||
private boolean modeListenerAdded = false;
|
||||
|
||||
private AudioTrack nativeTrack = null;
|
||||
private Thread nativeFeedThread = null;
|
||||
private final LinkedBlockingQueue<PcmChunk> pcmQueue = new LinkedBlockingQueue<>();
|
||||
private volatile boolean nativePlaying = false;
|
||||
private double pipeDelayEmaMs = -1;
|
||||
private long lastPipeLogNanos = 0;
|
||||
|
||||
private static final class PcmChunk {
|
||||
final byte[] data;
|
||||
final long arrivalNanos;
|
||||
PcmChunk(byte[] data) { this.data = data; this.arrivalNanos = System.nanoTime(); }
|
||||
}
|
||||
|
||||
private final AudioManager.OnAudioFocusChangeListener focusChangeListener = fi -> {};
|
||||
|
||||
private void log(String msg) {
|
||||
// android.util.Log is always visible in logcat, even for release builds
|
||||
Log.i(TAG, msg);
|
||||
}
|
||||
|
||||
private AudioManager am() {
|
||||
if (audioManager == null) {
|
||||
audioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
return audioManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holding call audio focus is what makes Android treat the app as being in
|
||||
* a real call: without it the system reverts MODE_IN_COMMUNICATION back to
|
||||
* MODE_NORMAL every few seconds (AudioManager.MSG_CHECK_MODE_FOR_UID).
|
||||
*/
|
||||
private void requestCallFocus() {
|
||||
AudioManager am = am();
|
||||
if (am == null) return;
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
focusRequest26 = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
||||
.setAudioAttributes(new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build())
|
||||
.setOnAudioFocusChangeListener(focusChangeListener)
|
||||
.build();
|
||||
int res = am.requestAudioFocus(focusRequest26);
|
||||
log("audio focus request (26+): " + res);
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
int res = am.requestAudioFocus(focusChangeListener, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN);
|
||||
log("audio focus request (legacy): " + res);
|
||||
}
|
||||
}
|
||||
|
||||
private void abandonCallFocus() {
|
||||
AudioManager am = am();
|
||||
if (am == null) return;
|
||||
if (Build.VERSION.SDK_INT >= 26 && focusRequest26 != null) {
|
||||
am.abandonAudioFocusRequest(focusRequest26);
|
||||
focusRequest26 = null;
|
||||
} else {
|
||||
@SuppressWarnings("deprecation")
|
||||
int res = am.abandonAudioFocus(focusChangeListener);
|
||||
log("audio focus abandoned: " + res);
|
||||
}
|
||||
}
|
||||
|
||||
private Handler handler() {
|
||||
if (mainHandler == null) {
|
||||
mainHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
return mainHandler;
|
||||
}
|
||||
|
||||
private boolean needsBluetoothPermission() {
|
||||
return Build.VERSION.SDK_INT >= 31
|
||||
&& ContextCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT)
|
||||
!= PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Android 12+ silently reverts MODE_IN_COMMUNICATION back to MODE_NORMAL.
|
||||
* When that happens, Chromium rebuilds its playout track with media usage
|
||||
* and the call stops following the call volume stream. This guard restores
|
||||
* the mode (and the route with it).
|
||||
*/
|
||||
private void assertModeAndRoute() {
|
||||
AudioManager am = am();
|
||||
if (am == null || !inCall) return;
|
||||
if (am.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
|
||||
log("MODE_IN_COMMUNICATION was reverted, restoring");
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
applyRoute();
|
||||
// Tell the web layer so it re-initializes its WebRTC playout track,
|
||||
// which Chromium otherwise leaves tagged as media after the revert.
|
||||
try {
|
||||
getBridge().triggerWindowJSEvent("voicemodechanged");
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "failed to notify web layer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startModeGuard() {
|
||||
AudioManager am = am();
|
||||
// Instant restore on API 30+ (fires the moment the system touches the mode)
|
||||
if (Build.VERSION.SDK_INT >= 30 && am != null && !modeListenerAdded) {
|
||||
try {
|
||||
am.addOnModeChangedListener(getContext().getMainExecutor(), modeChangedListener);
|
||||
modeListenerAdded = true;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "addOnModeChangedListener failed", e);
|
||||
}
|
||||
}
|
||||
handler().postDelayed(guardRunnable, MODE_GUARD_INTERVAL_MS);
|
||||
}
|
||||
|
||||
private void stopModeGuard() {
|
||||
AudioManager am = am();
|
||||
if (modeListenerAdded && am != null) {
|
||||
try {
|
||||
am.removeOnModeChangedListener(modeChangedListener);
|
||||
} catch (Exception e) {}
|
||||
modeListenerAdded = false;
|
||||
}
|
||||
handler().removeCallbacks(guardRunnable);
|
||||
}
|
||||
|
||||
private final Runnable guardRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!inCall) return;
|
||||
assertModeAndRoute();
|
||||
handler().postDelayed(this, MODE_GUARD_INTERVAL_MS);
|
||||
}
|
||||
};
|
||||
|
||||
private final AudioManager.OnModeChangedListener modeChangedListener = new AudioManager.OnModeChangedListener() {
|
||||
@Override
|
||||
public void onModeChanged(int mode) {
|
||||
if (!inCall) return;
|
||||
log("onModeChanged -> " + mode);
|
||||
assertModeAndRoute();
|
||||
}
|
||||
};
|
||||
|
||||
@PluginMethod
|
||||
public void startCall(PluginCall call) {
|
||||
if (needsBluetoothPermission()) {
|
||||
pendingStartCall = call;
|
||||
requestAllPermissions(call, "onBtPermissionResult");
|
||||
return;
|
||||
}
|
||||
beginCall(call);
|
||||
}
|
||||
|
||||
@PermissionCallback
|
||||
private void onBtPermissionResult(PluginCall call) {
|
||||
// Bluetooth permission is optional - continue regardless of the outcome.
|
||||
if (pendingStartCall == call) {
|
||||
pendingStartCall = null;
|
||||
beginCall(call);
|
||||
}
|
||||
}
|
||||
|
||||
private void beginCall(PluginCall call) {
|
||||
AudioManager am = am();
|
||||
if (am == null) {
|
||||
call.reject("AudioManager unavailable");
|
||||
return;
|
||||
}
|
||||
inCall = true;
|
||||
speakerOn = false;
|
||||
requestCallFocus();
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
applyRoute();
|
||||
startModeGuard();
|
||||
setVolumeControlStreamCompat(AudioManager.STREAM_VOICE_CALL);
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
private void setVolumeControlStreamCompat(int stream) {
|
||||
try {
|
||||
Context ctx = getContext();
|
||||
if (ctx instanceof Activity) {
|
||||
((Activity) ctx).setVolumeControlStream(stream);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "setVolumeControlStream failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void nativeAudioStart(PluginCall call) {
|
||||
Integer sr = call.getInt("sampleRate");
|
||||
int sampleRate = (sr == null || sr < 8000) ? 48000 : sr;
|
||||
|
||||
stopNativeAudioInternal();
|
||||
|
||||
int minBuf = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
|
||||
if (minBuf <= 0) {
|
||||
call.reject("getMinBufferSize failed: " + minBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
AudioAttributes attrs = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build();
|
||||
AudioFormat format = new AudioFormat.Builder()
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setSampleRate(sampleRate)
|
||||
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
||||
.build();
|
||||
|
||||
// Keep the buffer tight - its capacity directly adds to playback latency,
|
||||
// because blocking writes keep it filled close to 100% in steady state.
|
||||
int bufBytes = Math.max(minBuf, 1920); // >= 20 ms @ 48 kHz mono 16-bit
|
||||
|
||||
AudioTrack.Builder builder = new AudioTrack.Builder()
|
||||
.setAudioAttributes(attrs)
|
||||
.setAudioFormat(format)
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.setBufferSizeInBytes(bufBytes);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
builder.setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY);
|
||||
}
|
||||
|
||||
nativeTrack = builder.build();
|
||||
pcmQueue.clear();
|
||||
pipeDelayEmaMs = -1;
|
||||
lastPipeLogNanos = 0;
|
||||
nativePlaying = true;
|
||||
try {
|
||||
nativeTrack.play();
|
||||
} catch (IllegalStateException e) {
|
||||
stopNativeAudioInternal();
|
||||
call.reject("AudioTrack.play failed", e);
|
||||
return;
|
||||
}
|
||||
|
||||
nativeFeedThread = new Thread(() -> {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
|
||||
while (nativePlaying) {
|
||||
PcmChunk chunk;
|
||||
try {
|
||||
chunk = pcmQueue.poll(100, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException ie) {
|
||||
break;
|
||||
}
|
||||
if (chunk == null) continue;
|
||||
AudioTrack track = nativeTrack;
|
||||
if (track == null || !nativePlaying) break;
|
||||
double delayMs = (System.nanoTime() - chunk.arrivalNanos) / 1e6;
|
||||
pipeDelayEmaMs = pipeDelayEmaMs < 0 ? delayMs : (pipeDelayEmaMs * 0.9 + delayMs * 0.1);
|
||||
long now = System.nanoTime();
|
||||
if (lastPipeLogNanos == 0) lastPipeLogNanos = now;
|
||||
if (now - lastPipeLogNanos >= 1_000_000_000L) {
|
||||
lastPipeLogNanos = now;
|
||||
int underruns = 0;
|
||||
try {
|
||||
underruns = track.getUnderrunCount();
|
||||
} catch (Exception ignored) {}
|
||||
log(String.format(java.util.Locale.US,
|
||||
"pipe: queue=%d emaDelay=%.1f ms buffer=%.0f ms underruns=%d",
|
||||
pcmQueue.size(), pipeDelayEmaMs,
|
||||
track.getBufferSizeInFrames() * 1000.0 / track.getSampleRate(), underruns));
|
||||
}
|
||||
int written = track.write(chunk.data, 0, chunk.data.length, AudioTrack.WRITE_BLOCKING);
|
||||
if (written < 0) {
|
||||
Log.w(TAG, "AudioTrack.write error: " + written);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, "VoiceRoutingFeed");
|
||||
nativeFeedThread.start();
|
||||
|
||||
log("native audio started, rate=" + sampleRate
|
||||
+ " minBuf=" + minBuf
|
||||
+ " bufBytes=" + bufBytes
|
||||
+ " (~" + (bufBytes * 500 / sampleRate) + " ms)");
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void nativeAudioFeed(PluginCall call) {
|
||||
String b64 = call.getString("data");
|
||||
if (!nativePlaying || b64 == null || nativeTrack == null) {
|
||||
call.resolve();
|
||||
return;
|
||||
}
|
||||
byte[] pcm;
|
||||
try {
|
||||
pcm = android.util.Base64.decode(b64, android.util.Base64.NO_WRAP);
|
||||
} catch (IllegalArgumentException e) {
|
||||
call.resolve();
|
||||
return;
|
||||
}
|
||||
while (pcmQueue.size() > 10) {
|
||||
pcmQueue.poll(); // drop oldest to avoid unbounded growth
|
||||
}
|
||||
pcmQueue.offer(new PcmChunk(pcm));
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void nativeAudioStop(PluginCall call) {
|
||||
stopNativeAudioInternal();
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
private void stopNativeAudioInternal() {
|
||||
nativePlaying = false;
|
||||
if (nativeFeedThread != null) {
|
||||
nativeFeedThread.interrupt();
|
||||
nativeFeedThread = null;
|
||||
}
|
||||
if (nativeTrack != null) {
|
||||
try {
|
||||
nativeTrack.pause();
|
||||
nativeTrack.flush();
|
||||
nativeTrack.release();
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "AudioTrack release failed", e);
|
||||
}
|
||||
nativeTrack = null;
|
||||
}
|
||||
pcmQueue.clear();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void stopCall(PluginCall call) {
|
||||
inCall = false;
|
||||
stopModeGuard();
|
||||
abandonCallFocus();
|
||||
stopNativeAudioInternal();
|
||||
setVolumeControlStreamCompat(AudioManager.STREAM_MUSIC);
|
||||
|
||||
AudioManager am = am();
|
||||
if (am != null) {
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
am.clearCommunicationDevice();
|
||||
}
|
||||
am.setSpeakerphoneOn(false);
|
||||
am.setMode(AudioManager.MODE_NORMAL);
|
||||
}
|
||||
speakerOn = false;
|
||||
call.resolve();
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void setSpeaker(PluginCall call) {
|
||||
speakerOn = Boolean.TRUE.equals(call.getBoolean("enabled", false));
|
||||
applyRoute();
|
||||
JSObject ret = new JSObject();
|
||||
ret.put("enabled", speakerOn);
|
||||
call.resolve(ret);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void debugInfo(PluginCall call) {
|
||||
AudioManager am = am();
|
||||
JSObject ret = new JSObject();
|
||||
if (am == null) {
|
||||
ret.put("error", "no audio manager");
|
||||
call.resolve(ret);
|
||||
return;
|
||||
}
|
||||
ret.put("sdkInt", Build.VERSION.SDK_INT);
|
||||
ret.put("mode", am.getMode());
|
||||
ret.put("inCall", inCall);
|
||||
ret.put("speakerOn", speakerOn);
|
||||
|
||||
java.util.List<String> comm = new java.util.ArrayList<>();
|
||||
for (AudioDeviceInfo d : am.getAvailableCommunicationDevices()) {
|
||||
comm.add(String.valueOf(d.getType()));
|
||||
}
|
||||
ret.put("commDevices", String.join(",", comm));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
AudioDeviceInfo cur = am.getCommunicationDevice();
|
||||
ret.put("commDeviceType", cur == null ? -1 : cur.getType());
|
||||
}
|
||||
call.resolve(ret);
|
||||
}
|
||||
|
||||
private boolean applyRoute() {
|
||||
AudioManager am = am();
|
||||
if (am == null) return false;
|
||||
|
||||
// Communication mode must be active for the whole duration of the call,
|
||||
// otherwise Chromium tags its playout track as media.
|
||||
if (am.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
}
|
||||
|
||||
boolean routed = false;
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
AudioDeviceInfo target;
|
||||
if (speakerOn) {
|
||||
target = findDevice(am, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
|
||||
} else {
|
||||
target = findPreferredNonSpeaker(am);
|
||||
}
|
||||
if (target != null) {
|
||||
routed = am.setCommunicationDevice(target);
|
||||
}
|
||||
log("route -> speaker=" + speakerOn
|
||||
+ " device=" + (target == null ? "default" : String.valueOf(target.getType()))
|
||||
+ " applied=" + routed
|
||||
+ " mode=" + am.getMode());
|
||||
if (target == null && !speakerOn) {
|
||||
// No earpiece/headset available (e.g. tablet): keep default comm routing
|
||||
routed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!routed) {
|
||||
// Legacy path (Android < 12, or communication device rejected)
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
am.setSpeakerphoneOn(speakerOn);
|
||||
routed = true;
|
||||
}
|
||||
return routed;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo findPreferredNonSpeaker(AudioManager am) {
|
||||
int[] preference = {
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADSET,
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
|
||||
AudioDeviceInfo.TYPE_USB_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
};
|
||||
for (int type : preference) {
|
||||
AudioDeviceInfo d = findDevice(am, type);
|
||||
if (d != null) return d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo findDevice(AudioManager am, int type) {
|
||||
List<AudioDeviceInfo> devices = am.getAvailableCommunicationDevices();
|
||||
for (AudioDeviceInfo d : devices) {
|
||||
if (d.getType() == type) return d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
8
assets/Miarven.desktop
Executable file
8
assets/Miarven.desktop
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
[Desktop Entry]
|
||||
Name=Miarven
|
||||
Exec=bash -c 'DIR="$(dirname "%k")"; mkdir -p ~/.local/share/icons; cp "$DIR/icon.png" ~/.local/share/icons/olcxjamiarven.png; cd "$DIR" && ./miarven'
|
||||
Icon=olcxjamiarven
|
||||
Type=Application
|
||||
Categories=Internet
|
||||
StartupNotify=true
|
||||
StartupWMClass=olcxja.miarven
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "WARNING: Make sure you don't run this script in a folder that contains anything more than Miarven"
|
||||
echo "otherwise it may result in data loss"
|
||||
|
||||
read -r -p "Press Enter to continue (or Ctrl+C, to cancel)..."
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
BIN_DIR="$HOME/.local/bin/olcxja.miarven"
|
||||
APP_DIR="$HOME/.local/share/applications"
|
||||
|
||||
echo "1. Copying files..."
|
||||
mkdir -p "$BIN_DIR"
|
||||
mkdir -p "$APP_DIR"
|
||||
|
||||
cp -r "$SCRIPT_DIR/"* "$BIN_DIR/"
|
||||
|
||||
echo "2. Creating shortcut..."
|
||||
|
||||
mv "$BIN_DIR/olcxja.miarven.desktop" "$APP_DIR/olcxja.miarven.desktop"
|
||||
|
||||
echo "3. Adding executable permissions..."
|
||||
chmod +x "$APP_DIR/olcxja.miarven.desktop"
|
||||
chmod +x "$BIN_DIR/miarven"
|
||||
|
||||
echo "4. Cleaning installation..."
|
||||
|
||||
rm -f "$BIN_DIR/olcxja.miarven.desktop"
|
||||
rm -f "$BIN_DIR/install.sh"
|
||||
|
||||
echo "5. Installation completed. Cleaning up and launching..."
|
||||
|
||||
rm -rf "$SCRIPT_DIR"/*
|
||||
|
||||
"$BIN_DIR/miarven" &
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Name=Miarven
|
||||
Exec=~/.local/bin/olcxja.miarven/miarven
|
||||
Icon=olcxja.miarven
|
||||
Type=Application
|
||||
Categories=Internet
|
||||
StartupNotify=true
|
||||
StartupWMClass=olcxja.miarven
|
||||
|
|
@ -1,3 +1,2 @@
|
|||
cd android
|
||||
./gradlew assembleRelease
|
||||
mv app/build/outputs/apk/release/app-release.apk ../build/miarven-android.apk
|
||||
./gradlew assembleRelease
|
||||
|
|
@ -2,6 +2,4 @@ mkdir build
|
|||
cd build
|
||||
npx electron-packager ../electron miarven --platform=linux --arch=x64 --icon=../electron/assets/icon.png --overwrite --asar
|
||||
cp ../icons/icon.png ./miarven-linux-x64/icon.png
|
||||
cp ../assets/olcxja.miarven.desktop ./miarven-linux-x64/olcxja.miarven.desktop
|
||||
cp ../assets/install.sh ./miarven-linux-x64/install.sh
|
||||
chmod +x ./miarven-linux-x64/install.sh
|
||||
cp ../assets/Miarven.desktop ./miarven-linux-x64/Miarven.desktop
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
2
electron/node_modules/.bin/asar
generated
vendored
2
electron/node_modules/.bin/asar
generated
vendored
|
|
@ -1 +1 @@
|
|||
../@electron/asar/bin/asar.js
|
||||
../asar/bin/asar.js
|
||||
1
electron/node_modules/.bin/dircompare
generated
vendored
Symbolic link
1
electron/node_modules/.bin/dircompare
generated
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../dir-compare/src/cli/dircompare.js
|
||||
1
electron/node_modules/.bin/electron-fuses
generated
vendored
1
electron/node_modules/.bin/electron-fuses
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../@electron/fuses/dist/bin.js
|
||||
2
electron/node_modules/.bin/electron-osx-flat
generated
vendored
2
electron/node_modules/.bin/electron-osx-flat
generated
vendored
|
|
@ -1 +1 @@
|
|||
../@electron/osx-sign/bin/electron-osx-flat.js
|
||||
../electron-osx-sign/bin/electron-osx-flat.js
|
||||
2
electron/node_modules/.bin/electron-osx-sign
generated
vendored
2
electron/node_modules/.bin/electron-osx-sign
generated
vendored
|
|
@ -1 +1 @@
|
|||
../@electron/osx-sign/bin/electron-osx-sign.js
|
||||
../electron-osx-sign/bin/electron-osx-sign.js
|
||||
2
electron/node_modules/.bin/electron-rebuild
generated
vendored
2
electron/node_modules/.bin/electron-rebuild
generated
vendored
|
|
@ -1 +1 @@
|
|||
../@electron/rebuild/lib/cli.js
|
||||
../electron-rebuild/lib/src/cli.js
|
||||
1
electron/node_modules/.bin/electron-windows-sign
generated
vendored
1
electron/node_modules/.bin/electron-windows-sign
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../@electron/windows-sign/bin/electron-windows-sign.js
|
||||
1
electron/node_modules/.bin/extract-zip
generated
vendored
Symbolic link
1
electron/node_modules/.bin/extract-zip
generated
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../extract-zip/cli.js
|
||||
1
electron/node_modules/.bin/install-electron
generated
vendored
1
electron/node_modules/.bin/install-electron
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../electron/install.js
|
||||
1
electron/node_modules/.bin/is-ci
generated
vendored
Symbolic link
1
electron/node_modules/.bin/is-ci
generated
vendored
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../is-ci/bin.js
|
||||
1
electron/node_modules/.bin/jiti
generated
vendored
1
electron/node_modules/.bin/jiti
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../jiti/lib/jiti-cli.mjs
|
||||
1
electron/node_modules/.bin/postject
generated
vendored
1
electron/node_modules/.bin/postject
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../postject/dist/cli.js
|
||||
1
electron/node_modules/.bin/read-binary-file-arch
generated
vendored
1
electron/node_modules/.bin/read-binary-file-arch
generated
vendored
|
|
@ -1 +0,0 @@
|
|||
../read-binary-file-arch/cli.js
|
||||
2600
electron/node_modules/.package-lock.json
generated
vendored
2600
electron/node_modules/.package-lock.json
generated
vendored
File diff suppressed because it is too large
Load diff
9
electron/node_modules/7zip-bin/7x.sh
generated
vendored
Executable file
9
electron/node_modules/7zip-bin/7x.sh
generated
vendored
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
sz_program=${SZA_PATH:-7za}
|
||||
sz_type=${SZA_ARCHIVE_TYPE:-xz}
|
||||
|
||||
case $1 in
|
||||
-d) "$sz_program" e -si -so -t${sz_type} ;;
|
||||
*) "$sz_program" a f -si -so -t${sz_type} -mx${SZA_COMPRESSION_LEVEL:-9} ;;
|
||||
esac 2> /dev/null
|
||||
22
electron/node_modules/7zip-bin/LICENSE.txt
generated
vendored
Normal file
22
electron/node_modules/7zip-bin/LICENSE.txt
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Vladimir Krivosheev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
1
electron/node_modules/7zip-bin/README.md
generated
vendored
Normal file
1
electron/node_modules/7zip-bin/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
7-Zip precompiled binaries.
|
||||
2
electron/node_modules/7zip-bin/index.d.ts
generated
vendored
Normal file
2
electron/node_modules/7zip-bin/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export const path7za: string
|
||||
export const path7x: string
|
||||
22
electron/node_modules/7zip-bin/index.js
generated
vendored
Normal file
22
electron/node_modules/7zip-bin/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
"use strict"
|
||||
|
||||
const path = require("path")
|
||||
|
||||
function getPath() {
|
||||
if (process.env.USE_SYSTEM_7ZA === "true") {
|
||||
return "7za"
|
||||
}
|
||||
|
||||
if (process.platform === "darwin") {
|
||||
return path.join(__dirname, "mac", process.arch, "7za")
|
||||
}
|
||||
else if (process.platform === "win32") {
|
||||
return path.join(__dirname, "win", process.arch, "7za.exe")
|
||||
}
|
||||
else {
|
||||
return path.join(__dirname, "linux", process.arch, "7za")
|
||||
}
|
||||
}
|
||||
|
||||
exports.path7za = getPath()
|
||||
exports.path7x = path.join(__dirname, "7x.sh")
|
||||
BIN
electron/node_modules/7zip-bin/linux/arm/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/linux/arm/7za
generated
vendored
Executable file
Binary file not shown.
BIN
electron/node_modules/7zip-bin/linux/arm64/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/linux/arm64/7za
generated
vendored
Executable file
Binary file not shown.
BIN
electron/node_modules/7zip-bin/linux/ia32/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/linux/ia32/7za
generated
vendored
Executable file
Binary file not shown.
BIN
electron/node_modules/7zip-bin/linux/x64/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/linux/x64/7za
generated
vendored
Executable file
Binary file not shown.
9
electron/node_modules/7zip-bin/linux/x64/build.sh
generated
vendored
Executable file
9
electron/node_modules/7zip-bin/linux/x64/build.sh
generated
vendored
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
rm -rf /tmp/7z-linux
|
||||
mkdir /tmp/7z-linux
|
||||
cp "$BASEDIR/do-build.sh" /tmp/7z-linux/do-build.sh
|
||||
docker run --rm -v /tmp/7z-linux:/project buildpack-deps:xenial /project/do-build.sh
|
||||
20
electron/node_modules/7zip-bin/linux/x64/do-build.sh
generated
vendored
Executable file
20
electron/node_modules/7zip-bin/linux/x64/do-build.sh
generated
vendored
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
apt-get update -qq
|
||||
apt-get upgrade -qq
|
||||
|
||||
echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main" > /etc/apt/sources.list.d/llvm.list
|
||||
curl -L http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
apt-get update -qq
|
||||
apt-get install -qq bzip2 yasm clang-5.0 lldb-5.0 lld-5.0
|
||||
|
||||
ln -s /usr/bin/clang-5.0 /usr/bin/clang
|
||||
ln -s /usr/bin/clang++-5.0 /usr/bin/clang++
|
||||
|
||||
mkdir -p /tmp/7z
|
||||
cd /tmp/7z
|
||||
curl -L http://downloads.sourceforge.net/project/p7zip/p7zip/16.02/p7zip_16.02_src_all.tar.bz2 | tar -xj -C . --strip-components 1
|
||||
cp makefile.linux_clang_amd64_asm makefile.machine
|
||||
make -j4
|
||||
mv bin/7za /project/7za
|
||||
BIN
electron/node_modules/7zip-bin/mac/arm64/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/mac/arm64/7za
generated
vendored
Executable file
Binary file not shown.
BIN
electron/node_modules/7zip-bin/mac/x64/7za
generated
vendored
Executable file
BIN
electron/node_modules/7zip-bin/mac/x64/7za
generated
vendored
Executable file
Binary file not shown.
20
electron/node_modules/7zip-bin/package.json
generated
vendored
Normal file
20
electron/node_modules/7zip-bin/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "7zip-bin",
|
||||
"description": "7-Zip precompiled binaries",
|
||||
"version": "5.1.1",
|
||||
"files": [
|
||||
"*.js",
|
||||
"7x.sh",
|
||||
"index.d.ts",
|
||||
"linux",
|
||||
"mac",
|
||||
"win"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": "develar/7zip-bin",
|
||||
"keywords": [
|
||||
"7zip",
|
||||
"7z",
|
||||
"7za"
|
||||
]
|
||||
}
|
||||
BIN
electron/node_modules/7zip-bin/win/ia32/7za.exe
generated
vendored
Normal file
BIN
electron/node_modules/7zip-bin/win/ia32/7za.exe
generated
vendored
Normal file
Binary file not shown.
BIN
electron/node_modules/7zip-bin/win/x64/7za.exe
generated
vendored
Normal file
BIN
electron/node_modules/7zip-bin/win/x64/7za.exe
generated
vendored
Normal file
Binary file not shown.
BIN
electron/node_modules/@capacitor/cli/assets/android-template.tar.gz
generated
vendored
BIN
electron/node_modules/@capacitor/cli/assets/android-template.tar.gz
generated
vendored
Binary file not shown.
BIN
electron/node_modules/@capacitor/cli/assets/capacitor-cordova-android-plugins.tar.gz
generated
vendored
BIN
electron/node_modules/@capacitor/cli/assets/capacitor-cordova-android-plugins.tar.gz
generated
vendored
Binary file not shown.
BIN
electron/node_modules/@capacitor/cli/assets/capacitor-cordova-ios-plugins.tar.gz
generated
vendored
BIN
electron/node_modules/@capacitor/cli/assets/capacitor-cordova-ios-plugins.tar.gz
generated
vendored
Binary file not shown.
BIN
electron/node_modules/@capacitor/cli/assets/ios-pods-template.tar.gz
generated
vendored
BIN
electron/node_modules/@capacitor/cli/assets/ios-pods-template.tar.gz
generated
vendored
Binary file not shown.
BIN
electron/node_modules/@capacitor/cli/assets/ios-spm-template.tar.gz
generated
vendored
BIN
electron/node_modules/@capacitor/cli/assets/ios-spm-template.tar.gz
generated
vendored
Binary file not shown.
25
electron/node_modules/@capacitor/cli/dist/declarations.d.ts
generated
vendored
25
electron/node_modules/@capacitor/cli/dist/declarations.d.ts
generated
vendored
|
|
@ -511,15 +511,6 @@ export interface CapacitorConfig {
|
|||
packageTraits?: {
|
||||
[pluginId: string]: string[];
|
||||
};
|
||||
/**
|
||||
* Define options to apply to the package.
|
||||
* The key is the plugin ID (e.g. `@capacitor-community/device`)
|
||||
*
|
||||
* @since 8.4.0
|
||||
*/
|
||||
packageOptions?: {
|
||||
[pluginId: string]: PackageOptions;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -555,7 +546,7 @@ export interface CapacitorConfig {
|
|||
* Configure the local scheme on Android.
|
||||
*
|
||||
* Custom schemes on Android are unable to change the URL path as of Webview 117. Changing this value from anything other than `http` or `https` can result in your
|
||||
* application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarantees that this
|
||||
* application unable to resolve routing. If you must change this for some reason, consider using a hash-based url strategy, but there are no guarentees that this
|
||||
* will continue to work long term as allowing non-standard schemes to modify query parameters and url fragments is only allowed for compatibility reasons.
|
||||
* https://ionic.io/blog/capacitor-android-customscheme-issue-with-chrome-117
|
||||
*
|
||||
|
|
@ -741,17 +732,3 @@ export interface PluginsConfig {
|
|||
animation?: 'FADE' | 'NONE';
|
||||
};
|
||||
}
|
||||
export interface PackageOptions {
|
||||
/**
|
||||
* Create a symlink to the plugin folder instead of pointing to the plugin path.
|
||||
* Useful when plugin names conflict.
|
||||
*/
|
||||
symlink?: boolean;
|
||||
/**
|
||||
* Useful to avoid target name conflicts in dependencies
|
||||
* [see](https://docs.swift.org/swiftpm/documentation/packagemanagerdocs/modulealiasing/)
|
||||
*/
|
||||
moduleAliases?: {
|
||||
[target: string]: string;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
206
electron/node_modules/@capacitor/cli/dist/ios/update.js
generated
vendored
206
electron/node_modules/@capacitor/cli/dist/ios/update.js
generated
vendored
|
|
@ -4,7 +4,6 @@ exports.installCocoaPodsPlugins = exports.updateIOS = void 0;
|
|||
const tslib_1 = require("tslib");
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = require("path");
|
||||
const semver_1 = require("semver");
|
||||
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
||||
const common_1 = require("../common");
|
||||
const cordova_1 = require("../cordova");
|
||||
|
|
@ -12,7 +11,6 @@ const errors_1 = require("../errors");
|
|||
const log_1 = require("../log");
|
||||
const plugin_1 = require("../plugin");
|
||||
const copy_1 = require("../tasks/copy");
|
||||
const migrate_1 = require("../tasks/migrate");
|
||||
const fs_1 = require("../util/fs");
|
||||
const iosplugin_1 = require("../util/iosplugin");
|
||||
const node_1 = require("../util/node");
|
||||
|
|
@ -45,22 +43,6 @@ async function updatePluginFiles(config, plugins, deployment) {
|
|||
if ((await config.ios.packageManager) === 'SPM') {
|
||||
await generateCordovaPackageFiles(cordovaPlugins, config);
|
||||
const validSPMPackages = await (0, spm_1.checkPluginsForPackageSwift)(config, plugins);
|
||||
await Promise.all(validSPMPackages.map(async (plugin) => {
|
||||
var _a;
|
||||
const iosPlatformVersion = await (0, common_1.getCapacitorPackageVersion)(config, config.ios.name);
|
||||
const packageSwiftPath = (0, path_1.join)(plugin.rootPath, 'Package.swift');
|
||||
let content = await (0, fs_extra_1.readFile)(packageSwiftPath, { encoding: 'utf-8' });
|
||||
const regex = new RegExp('url:\\s*"https://github.com/ionic-team/capacitor-swift-pm\\.git",\\s*from:\\s*"([^"]+)"');
|
||||
const version = (_a = content.match(regex)) === null || _a === void 0 ? void 0 : _a[1];
|
||||
const majorCapVersion = (0, semver_1.major)(iosPlatformVersion);
|
||||
if (version && (0, semver_1.major)(version) != majorCapVersion) {
|
||||
const preCapVersion = (0, semver_1.prerelease)(iosPlatformVersion);
|
||||
const forceVersion = preCapVersion ? iosPlatformVersion : `${majorCapVersion}.0.0`;
|
||||
content = (0, migrate_1.setAllStringIn)(content, `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, `)`, ` from: "${forceVersion}"`);
|
||||
await (0, fs_extra_1.writeFile)(packageSwiftPath, content);
|
||||
log_1.logger.warn(`${plugin.id} is built for Capacitor ${(0, semver_1.major)(version)}, it might cause issues`);
|
||||
}
|
||||
}));
|
||||
await (0, spm_1.generatePackageFile)(config, validSPMPackages.concat(cordovaPlugins));
|
||||
}
|
||||
else {
|
||||
|
|
@ -77,159 +59,14 @@ async function generateCordovaPackageFiles(cordovaPlugins, config) {
|
|||
});
|
||||
}
|
||||
async function generateCordovaPackageFile(p, config) {
|
||||
var _a;
|
||||
const iosPlatformVersion = await (0, common_1.getCapacitorPackageVersion)(config, config.ios.name);
|
||||
const platformTag = (0, plugin_1.getPluginPlatform)(p, platform);
|
||||
if ((_a = platformTag.$) === null || _a === void 0 ? void 0 : _a.package) {
|
||||
const packageSwiftPath = (0, path_1.join)(p.rootPath, 'Package.swift');
|
||||
let content = await (0, fs_extra_1.readFile)(packageSwiftPath, { encoding: 'utf-8' });
|
||||
content = content.replace(`apache`, `ionic-team`).replaceAll(`cordova-ios`, `capacitor-swift-pm`);
|
||||
content = (0, migrate_1.setAllStringIn)(content, `url: "https://github.com/ionic-team/capacitor-swift-pm.git",`, `)`, ` from: "${iosPlatformVersion}"`);
|
||||
await (0, fs_extra_1.writeFile)(packageSwiftPath, content);
|
||||
}
|
||||
else {
|
||||
await writeGeneratedPackageSwift(p, config, iosPlatformVersion);
|
||||
}
|
||||
}
|
||||
function buildBinaryTargetEntries(p, frameworks) {
|
||||
const customXcframeworks = frameworks.filter((f) => f.$.custom === 'true' && f.$.src.endsWith('.xcframework'));
|
||||
const customFrameworks = frameworks.filter((f) => f.$.custom === 'true' && f.$.src.endsWith('.framework'));
|
||||
if (customFrameworks.length > 0) {
|
||||
customFrameworks.forEach((f) => {
|
||||
log_1.logger.warn(`${p.id}: custom .framework files are not supported as binaryTarget in SPM (${f.$.src}). Convert to .xcframework for SPM compatibility.`);
|
||||
});
|
||||
}
|
||||
const binaryTargetsText = customXcframeworks
|
||||
.map((f) => {
|
||||
const name = f.$.src.split('/').pop().replace('.xcframework', '');
|
||||
return `,
|
||||
.binaryTarget(
|
||||
name: "${name}",
|
||||
path: "${f.$.src}"
|
||||
)`;
|
||||
})
|
||||
.join('');
|
||||
const binaryDepsText = customXcframeworks
|
||||
.map((f) => {
|
||||
const name = f.$.src.split('/').pop().replace('.xcframework', '');
|
||||
return `,\n .target(name: "${name}")`;
|
||||
})
|
||||
.join('');
|
||||
return { binaryTargetsText, binaryDepsText };
|
||||
}
|
||||
function buildResourcesText(resources) {
|
||||
const resourceEntry = [];
|
||||
for (const resource of resources) {
|
||||
resourceEntry.push(`.copy("resources/${resource.$.src.split('/').pop()}")`);
|
||||
}
|
||||
return resources.length > 0
|
||||
? `,
|
||||
resources: [
|
||||
${resourceEntry.join(',\n ')}
|
||||
]`
|
||||
: '';
|
||||
}
|
||||
async function buildDependencyTexts(p, config) {
|
||||
let allDependencies = (0, plugin_1.getPlatformElement)(p, platform, 'dependency');
|
||||
if (p.xml['dependency']) {
|
||||
allDependencies = allDependencies.concat(p.xml['dependency']);
|
||||
}
|
||||
let packageText = '';
|
||||
let productText = '';
|
||||
await Promise.all(allDependencies.map(async (dep) => {
|
||||
let plugin = dep.$.id;
|
||||
if (plugin.includes('@') && plugin.indexOf('@') !== 0) {
|
||||
[plugin] = plugin.split('@');
|
||||
}
|
||||
const depPlugin = await (0, plugin_1.resolvePlugin)(config, plugin);
|
||||
if (depPlugin) {
|
||||
const headerFiles = (0, plugin_1.getPlatformElement)(depPlugin, platform, 'header-file');
|
||||
const resources = (0, plugin_1.getPlatformElement)(depPlugin, platform, 'resource-file');
|
||||
const sourceFiles = (0, plugin_1.getPlatformElement)(depPlugin, platform, 'source-file');
|
||||
if (sourceFiles.length === 0 && headerFiles.length === 0 && resources.length === 0) {
|
||||
return;
|
||||
}
|
||||
packageText += `,\n .package(name: "${depPlugin.name}", path: "../${depPlugin.name}")`;
|
||||
productText += `,\n .product(name: "${depPlugin.name}", package: "${depPlugin.name}")`;
|
||||
}
|
||||
}));
|
||||
return { packageText, productText };
|
||||
}
|
||||
function buildCSettingsText(p, sourceFiles) {
|
||||
var _a;
|
||||
const pluginId = p.id;
|
||||
const allFlags = new Set();
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const flags = (_a = sourceFile.$) === null || _a === void 0 ? void 0 : _a['compiler-flags'];
|
||||
if (flags) {
|
||||
flags
|
||||
.split(/\s+/)
|
||||
.map((f) => f.trim())
|
||||
.filter((f) => f.length > 0)
|
||||
.forEach((f) => allFlags.add(f));
|
||||
}
|
||||
}
|
||||
if (allFlags.size === 0) {
|
||||
return '';
|
||||
}
|
||||
const entries = [];
|
||||
const unsupportedFlags = [];
|
||||
for (const flag of allFlags) {
|
||||
if (flag.startsWith('-D')) {
|
||||
const definition = flag.slice(2);
|
||||
const eqIndex = definition.indexOf('=');
|
||||
if (eqIndex !== -1) {
|
||||
const name = definition.slice(0, eqIndex);
|
||||
const value = definition.slice(eqIndex + 1);
|
||||
entries.push(` .define("${name}", to: "${value}")`);
|
||||
}
|
||||
else {
|
||||
entries.push(` .define("${definition}")`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsupportedFlags.push(flag);
|
||||
}
|
||||
}
|
||||
if (unsupportedFlags.length > 0) {
|
||||
log_1.logger.warn(`${pluginId}: the following compiler flags are not supported in SPM and were ignored: ${unsupportedFlags.join(', ')}. ` +
|
||||
`This might cause the plugin to fail to compile.`);
|
||||
}
|
||||
if (entries.length === 0) {
|
||||
return '';
|
||||
}
|
||||
return `,
|
||||
cSettings: [
|
||||
${entries.join(',\n')}
|
||||
]`;
|
||||
}
|
||||
async function writeGeneratedPackageSwift(p, config, iosPlatformVersion) {
|
||||
const iosVersion = (0, common_2.getMajoriOSVersion)(config);
|
||||
const headerFiles = (0, plugin_1.getPlatformElement)(p, platform, 'header-file');
|
||||
const headersText = headerFiles.length > 0
|
||||
? `,
|
||||
publicHeadersPath: "."`
|
||||
: '';
|
||||
const resources = (0, plugin_1.getPlatformElement)(p, platform, 'resource-file');
|
||||
const sourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'source-file');
|
||||
if (sourceFiles.length === 0 && headerFiles.length === 0 && resources.length === 0) {
|
||||
return;
|
||||
let headersText = '';
|
||||
if (headerFiles.length > 0) {
|
||||
headersText = `,
|
||||
publicHeadersPath: "."`;
|
||||
}
|
||||
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
|
||||
const { binaryTargetsText, binaryDepsText } = buildBinaryTargetEntries(p, frameworks);
|
||||
const cSettingsText = buildCSettingsText(p, sourceFiles);
|
||||
const systemFrameworks = frameworks.filter((f) => !f.$.custom && f.$.src.endsWith('.framework'));
|
||||
const hasWeakFrameworks = systemFrameworks.some((f) => f.$.weak === 'true');
|
||||
const requiredSystemFrameworks = systemFrameworks.filter((f) => f.$.weak !== 'true');
|
||||
const resourcesText = buildResourcesText(resources);
|
||||
const libraryTypeText = hasWeakFrameworks ? `\n type: .dynamic,` : '';
|
||||
const linkerSettingsText = requiredSystemFrameworks.length > 0
|
||||
? `,
|
||||
linkerSettings: [
|
||||
${requiredSystemFrameworks.map((f) => ` .linkedFramework("${f.$.src.replace('.framework', '')}")`).join(',\n')}
|
||||
]`
|
||||
: '';
|
||||
const { packageText, productText } = await buildDependencyTexts(p, config);
|
||||
const content = `// swift-tools-version: 5.9
|
||||
|
||||
import PackageDescription
|
||||
|
|
@ -239,21 +76,21 @@ let package = Package(
|
|||
platforms: [.iOS(.v${iosVersion})],
|
||||
products: [
|
||||
.library(
|
||||
name: "${p.name}",${libraryTypeText}
|
||||
name: "${p.name}",
|
||||
targets: ["${p.name}"]
|
||||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "${iosPlatformVersion}")${packageText}
|
||||
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "${iosPlatformVersion}")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "${p.name}",
|
||||
dependencies: [
|
||||
.product(name: "Cordova", package: "capacitor-swift-pm")${binaryDepsText}${productText}
|
||||
.product(name: "Cordova", package: "capacitor-swift-pm")
|
||||
],
|
||||
path: "."${resourcesText}${headersText}${cSettingsText}${linkerSettingsText}
|
||||
)${binaryTargetsText}
|
||||
path: "."${headersText}
|
||||
)
|
||||
]
|
||||
)`;
|
||||
await (0, fs_extra_1.writeFile)((0, path_1.join)(config.ios.cordovaPluginsDirAbs, 'sources', p.name, 'Package.swift'), content);
|
||||
|
|
@ -517,19 +354,13 @@ function getLinkerFlags(config) {
|
|||
return '';
|
||||
}
|
||||
async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
||||
var _a;
|
||||
const isSPM = (await config.ios.packageManager) === 'SPM';
|
||||
for (const p of cordovaPlugins) {
|
||||
const platformTag = (0, plugin_1.getPluginPlatform)(p, platform);
|
||||
if (isSPM && ((_a = platformTag.$) === null || _a === void 0 ? void 0 : _a.package)) {
|
||||
continue;
|
||||
}
|
||||
const sourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'source-file');
|
||||
const headerFiles = (0, plugin_1.getPlatformElement)(p, platform, 'header-file');
|
||||
const codeFiles = sourceFiles.concat(headerFiles);
|
||||
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
|
||||
let sourcesFolderName = 'sources';
|
||||
if (!isSPM && (0, cordova_1.needsStaticPod)(p)) {
|
||||
if ((0, cordova_1.needsStaticPod)(p)) {
|
||||
sourcesFolderName += 'static';
|
||||
}
|
||||
const sourcesFolder = (0, path_1.join)(config.ios.cordovaPluginsDirAbs, sourcesFolderName, p.name);
|
||||
|
|
@ -540,7 +371,7 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|||
fileName = 'lib' + fileName;
|
||||
}
|
||||
let destFolder = sourcesFolderName;
|
||||
if (!isSPM && codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') {
|
||||
if (codeFile.$['compiler-flags'] && codeFile.$['compiler-flags'] === '-fno-objc-arc') {
|
||||
destFolder = 'noarc';
|
||||
}
|
||||
const filePath = (0, plugin_1.getFilePath)(config, p, codeFile.$.src);
|
||||
|
|
@ -559,13 +390,8 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|||
}
|
||||
if (fileContent.includes('[NSBundle bundleForClass:[self class]]') ||
|
||||
fileContent.includes('[NSBundle bundleForClass:[CDVCapture class]]')) {
|
||||
const bundleName = isSPM ? 'SWIFTPM_MODULE_BUNDLE' : '[NSBundle mainBundle]';
|
||||
fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', bundleName);
|
||||
fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', bundleName);
|
||||
await (0, fs_extra_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
|
||||
}
|
||||
if (isSPM && fileContent.includes('[NSBundle mainBundle] URLForResource')) {
|
||||
fileContent = fileContent.replace('[NSBundle mainBundle] URLForResource', 'SWIFTPM_MODULE_BUNDLE URLForResource');
|
||||
fileContent = fileContent.replace('[NSBundle bundleForClass:[self class]]', '[NSBundle mainBundle]');
|
||||
fileContent = fileContent.replace('[NSBundle bundleForClass:[CDVCapture class]]', '[NSBundle mainBundle]');
|
||||
await (0, fs_extra_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
|
||||
}
|
||||
if (fileContent.includes('[self.webView superview]') || fileContent.includes('self.webView.superview')) {
|
||||
|
|
@ -579,8 +405,7 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|||
const resourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'resource-file');
|
||||
for (const resourceFile of resourceFiles) {
|
||||
const fileName = resourceFile.$.src.split('/').pop();
|
||||
const rootResFolder = isSPM ? sourcesFolder : config.ios.cordovaPluginsDirAbs;
|
||||
await (0, fs_extra_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(rootResFolder, 'resources', fileName));
|
||||
await (0, fs_extra_1.copy)((0, plugin_1.getFilePath)(config, p, resourceFile.$.src), (0, path_1.join)(config.ios.cordovaPluginsDirAbs, 'resources', fileName));
|
||||
}
|
||||
for (const framework of frameworks) {
|
||||
if (framework.$.custom && framework.$.custom === 'true') {
|
||||
|
|
@ -591,9 +416,6 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|||
}
|
||||
async function removePluginsNativeFiles(config) {
|
||||
await (0, fs_extra_1.remove)(config.ios.cordovaPluginsDirAbs);
|
||||
if ((await config.ios.packageManager) === 'SPM') {
|
||||
await (0, fs_extra_1.remove)((0, path_1.join)(config.ios.nativeProjectDirAbs, 'CapApp-SPM', 'symlinks'));
|
||||
}
|
||||
await (0, template_1.extractTemplate)(config.cli.assets.ios.cordovaPluginsTemplateArchiveAbs, config.ios.cordovaPluginsDirAbs);
|
||||
}
|
||||
function filterResources(plugin) {
|
||||
|
|
|
|||
6
electron/node_modules/@capacitor/cli/dist/ipc.js
generated
vendored
6
electron/node_modules/@capacitor/cli/dist/ipc.js
generated
vendored
|
|
@ -36,16 +36,16 @@ async function receive(msg) {
|
|||
const { data } = msg;
|
||||
// This request is only made if telemetry is on.
|
||||
const req = (0, https_1.request)({
|
||||
hostname: 'metrics-capacitor.outsystems.com',
|
||||
hostname: 'api.ionicjs.com',
|
||||
port: 443,
|
||||
path: '/metrics',
|
||||
path: '/events/metrics',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}, (response) => {
|
||||
debug('Sent %O metric to events service (status: %O)', data.name, response.statusCode);
|
||||
if (response.statusCode !== 202) {
|
||||
if (response.statusCode !== 204) {
|
||||
response.on('data', (chunk) => {
|
||||
debug('Bad response from events service. Request body: %O', chunk.toString());
|
||||
});
|
||||
|
|
|
|||
3
electron/node_modules/@capacitor/cli/dist/tasks/migrate.js
generated
vendored
3
electron/node_modules/@capacitor/cli/dist/tasks/migrate.js
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.setAllStringIn = exports.migrateCommand = void 0;
|
||||
exports.migrateCommand = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = require("path");
|
||||
|
|
@ -497,7 +497,6 @@ function setAllStringIn(data, start, end, replacement) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
exports.setAllStringIn = setAllStringIn;
|
||||
async function updateAndroidManifest(filename) {
|
||||
const txt = readFile(filename);
|
||||
if (!txt) {
|
||||
|
|
|
|||
3
electron/node_modules/@capacitor/cli/dist/tasks/run.js
generated
vendored
3
electron/node_modules/@capacitor/cli/dist/tasks/run.js
generated
vendored
|
|
@ -85,9 +85,6 @@ async function runCommand(config, selectedPlatformName, options) {
|
|||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (options.liveReload) {
|
||||
await livereload_1.CapLiveReloadHelper.revertCapConfigForLiveReload();
|
||||
}
|
||||
if (!(0, errors_1.isFatal)(e)) {
|
||||
(0, errors_1.fatal)((_c = e.stack) !== null && _c !== void 0 ? _c : e);
|
||||
}
|
||||
|
|
|
|||
23
electron/node_modules/@capacitor/cli/dist/telemetry.js
generated
vendored
23
electron/node_modules/@capacitor/cli/dist/telemetry.js
generated
vendored
|
|
@ -1,10 +1,9 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getIOSPackageManager = exports.sendMetric = exports.telemetryAction = void 0;
|
||||
exports.sendMetric = exports.telemetryAction = void 0;
|
||||
const tslib_1 = require("tslib");
|
||||
const commander_1 = require("commander");
|
||||
const debug_1 = tslib_1.__importDefault(require("debug"));
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const colors_1 = tslib_1.__importDefault(require("./colors"));
|
||||
const ipc_1 = require("./ipc");
|
||||
const log_1 = require("./log");
|
||||
|
|
@ -52,10 +51,8 @@ function telemetryAction(config, action) {
|
|||
error: error ? (error.message ? error.message : String(error)) : null,
|
||||
node_version: process.version,
|
||||
os: config.cli.os,
|
||||
ios_package_manager: await getIOSPackageManager(config),
|
||||
...Object.fromEntries(versions),
|
||||
};
|
||||
debug('metric payload: %O', data);
|
||||
if ((0, term_1.isInteractive)()) {
|
||||
let sysconfig = await (0, sysconfig_1.readConfig)();
|
||||
if (!error && typeof sysconfig.telemetry === 'undefined') {
|
||||
|
|
@ -91,24 +88,6 @@ async function sendMetric(sysconfig, name, data) {
|
|||
}
|
||||
}
|
||||
exports.sendMetric = sendMetric;
|
||||
/**
|
||||
* Resolve the iOS dependency manager for telemetry. Returns 'unknown' for
|
||||
* non-iOS projects or when detection throws, so callers never have to handle
|
||||
* errors from this signal.
|
||||
*/
|
||||
async function getIOSPackageManager(config) {
|
||||
try {
|
||||
if (!(await (0, fs_extra_1.pathExists)(config.ios.platformDirAbs))) {
|
||||
return 'unknown';
|
||||
}
|
||||
return await config.ios.packageManager;
|
||||
}
|
||||
catch (e) {
|
||||
debug('Could not resolve iOS package manager for telemetry: %O', e);
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
exports.getIOSPackageManager = getIOSPackageManager;
|
||||
/**
|
||||
* Get a unique anonymous identifier for this app.
|
||||
*/
|
||||
|
|
|
|||
61
electron/node_modules/@capacitor/cli/dist/util/spm.js
generated
vendored
61
electron/node_modules/@capacitor/cli/dist/util/spm.js
generated
vendored
|
|
@ -45,12 +45,12 @@ exports.generatePackageFile = generatePackageFile;
|
|||
async function checkPluginsForPackageSwift(config, plugins) {
|
||||
const iOSCapacitorPlugins = plugins.filter((p) => (0, plugin_1.getPluginType)(p, 'ios') === 0 /* PluginType.Core */);
|
||||
const packageSwiftPluginList = await pluginsWithPackageSwift(iOSCapacitorPlugins);
|
||||
if (iOSCapacitorPlugins.length == packageSwiftPluginList.length) {
|
||||
log_1.logger.debug(`Found ${iOSCapacitorPlugins.length} Capacitor iOS plugins, ${packageSwiftPluginList.length} have a Package.swift file`);
|
||||
log_1.logger.info('All Capacitor plugins have a Package.swift file and will be included in Package.swift');
|
||||
if (plugins.length == packageSwiftPluginList.length) {
|
||||
log_1.logger.debug(`Found ${plugins.length} iOS plugins, ${packageSwiftPluginList.length} have a Package.swift file`);
|
||||
log_1.logger.info('All plugins have a Package.swift file and will be included in Package.swift');
|
||||
}
|
||||
else {
|
||||
log_1.logger.warn('Some installed Capacitor plugins are not compatible with SPM');
|
||||
log_1.logger.warn('Some installed packages are not compatable with SPM');
|
||||
}
|
||||
return packageSwiftPluginList;
|
||||
}
|
||||
|
|
@ -84,12 +84,11 @@ async function removeCocoapodsFiles(config) {
|
|||
}
|
||||
exports.removeCocoapodsFiles = removeCocoapodsFiles;
|
||||
async function generatePackageText(config, plugins) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
||||
const iosPlatformVersion = await (0, common_1.getCapacitorPackageVersion)(config, config.ios.name);
|
||||
const iosVersion = (0, common_2.getMajoriOSVersion)(config);
|
||||
const packageTraits = (_d = (_c = (_b = (_a = config.app.extConfig.experimental) === null || _a === void 0 ? void 0 : _a.ios) === null || _b === void 0 ? void 0 : _b.spm) === null || _c === void 0 ? void 0 : _c.packageTraits) !== null && _d !== void 0 ? _d : {};
|
||||
const packageOptions = (_h = (_g = (_f = (_e = config.app.extConfig.experimental) === null || _e === void 0 ? void 0 : _e.ios) === null || _f === void 0 ? void 0 : _f.spm) === null || _g === void 0 ? void 0 : _g.packageOptions) !== null && _h !== void 0 ? _h : {};
|
||||
const swiftToolsVersion = (_m = (_l = (_k = (_j = config.app.extConfig.experimental) === null || _j === void 0 ? void 0 : _j.ios) === null || _k === void 0 ? void 0 : _k.spm) === null || _l === void 0 ? void 0 : _l.swiftToolsVersion) !== null && _m !== void 0 ? _m : '5.9';
|
||||
const swiftToolsVersion = (_h = (_g = (_f = (_e = config.app.extConfig.experimental) === null || _e === void 0 ? void 0 : _e.ios) === null || _f === void 0 ? void 0 : _f.spm) === null || _g === void 0 ? void 0 : _g.swiftToolsVersion) !== null && _h !== void 0 ? _h : '5.9';
|
||||
let packageSwiftText = `// swift-tools-version: ${swiftToolsVersion}
|
||||
import PackageDescription
|
||||
|
||||
|
|
@ -106,28 +105,10 @@ let package = Package(
|
|||
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "${iosPlatformVersion}")`;
|
||||
for (const plugin of plugins) {
|
||||
if ((0, plugin_1.getPluginType)(plugin, config.ios.name) === 1 /* PluginType.Cordova */) {
|
||||
const platformTag = (0, plugin_1.getPluginPlatform)(plugin, config.ios.name);
|
||||
if ((_o = platformTag.$) === null || _o === void 0 ? void 0 : _o.package) {
|
||||
const relPath = (0, path_1.relative)(config.ios.nativeXcodeProjDirAbs, plugin.rootPath);
|
||||
packageSwiftText += `,\n .package(name: "${plugin.id}", path: "${relPath}")`;
|
||||
}
|
||||
else {
|
||||
const sourceFiles = (0, plugin_1.getPlatformElement)(plugin, config.ios.name, 'source-file');
|
||||
const headerFiles = (0, plugin_1.getPlatformElement)(plugin, config.ios.name, 'header-file');
|
||||
if (sourceFiles.length === 0 && headerFiles.length === 0) {
|
||||
continue;
|
||||
}
|
||||
packageSwiftText += `,\n .package(name: "${plugin.name}", path: "../../capacitor-cordova-ios-plugins/sources/${plugin.name}")`;
|
||||
}
|
||||
packageSwiftText += `,\n .package(name: "${plugin.name}", path: "../../capacitor-cordova-ios-plugins/sources/${plugin.name}")`;
|
||||
}
|
||||
else {
|
||||
const options = packageOptions[plugin.id];
|
||||
const symlink = options === null || options === void 0 ? void 0 : options.symlink;
|
||||
const symlinkFolder = (0, path_1.join)('symlinks', plugin.name);
|
||||
const relPath = symlink ? symlinkFolder : (0, path_1.relative)(config.ios.nativeXcodeProjDirAbs, plugin.rootPath);
|
||||
if (symlink) {
|
||||
await (0, fs_extra_1.ensureSymlink)(plugin.rootPath, (0, path_1.resolve)(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder));
|
||||
}
|
||||
const relPath = (0, path_1.relative)(config.ios.nativeXcodeProjDirAbs, plugin.rootPath);
|
||||
const traits = packageTraits[plugin.id];
|
||||
const traitsSuffix = (traits === null || traits === void 0 ? void 0 : traits.length)
|
||||
? `, traits: [${traits
|
||||
|
|
@ -137,7 +118,7 @@ let package = Package(
|
|||
})
|
||||
.join(', ')}]`
|
||||
: '';
|
||||
packageSwiftText += `,\n .package(name: "${(_p = plugin.ios) === null || _p === void 0 ? void 0 : _p.name}", path: "${relPath}"${traitsSuffix})`;
|
||||
packageSwiftText += `,\n .package(name: "${(_j = plugin.ios) === null || _j === void 0 ? void 0 : _j.name}", path: "${relPath}"${traitsSuffix})`;
|
||||
}
|
||||
}
|
||||
packageSwiftText += `
|
||||
|
|
@ -149,29 +130,7 @@ let package = Package(
|
|||
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
||||
.product(name: "Cordova", package: "capacitor-swift-pm")`;
|
||||
for (const plugin of plugins) {
|
||||
const aliases = Object.entries((_r = (_q = packageOptions[plugin.id]) === null || _q === void 0 ? void 0 : _q.moduleAliases) !== null && _r !== void 0 ? _r : {});
|
||||
const aliasText = (aliases === null || aliases === void 0 ? void 0 : aliases.length)
|
||||
? `, moduleAliases: [${aliases
|
||||
.map(([target, replacement]) => {
|
||||
return `"${target}": "${replacement}"`;
|
||||
})
|
||||
.join(', ')}]`
|
||||
: '';
|
||||
let pluginText = `,\n .product(name: "${(_s = plugin.ios) === null || _s === void 0 ? void 0 : _s.name}", package: "${(_t = plugin.ios) === null || _t === void 0 ? void 0 : _t.name}"${aliasText})`;
|
||||
if ((0, plugin_1.getPluginType)(plugin, config.ios.name) === 1 /* PluginType.Cordova */) {
|
||||
const platformTag = (0, plugin_1.getPluginPlatform)(plugin, config.ios.name);
|
||||
if ((_u = platformTag.$) === null || _u === void 0 ? void 0 : _u.package) {
|
||||
pluginText = `,\n .product(name: "${plugin.id}", package: "${plugin.id}")`;
|
||||
}
|
||||
else {
|
||||
const sourceFiles = (0, plugin_1.getPlatformElement)(plugin, config.ios.name, 'source-file');
|
||||
const headerFiles = (0, plugin_1.getPlatformElement)(plugin, config.ios.name, 'header-file');
|
||||
if (sourceFiles.length === 0 && headerFiles.length === 0) {
|
||||
pluginText = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
packageSwiftText += pluginText;
|
||||
packageSwiftText += `,\n .product(name: "${(_k = plugin.ios) === null || _k === void 0 ? void 0 : _k.name}", package: "${(_l = plugin.ios) === null || _l === void 0 ? void 0 : _l.name}")`;
|
||||
}
|
||||
packageSwiftText += `
|
||||
]
|
||||
|
|
|
|||
2
electron/node_modules/@capacitor/cli/node_modules/fs-extra/README.md
generated
vendored
2
electron/node_modules/@capacitor/cli/node_modules/fs-extra/README.md
generated
vendored
|
|
@ -61,7 +61,7 @@ const fse = require('fs-extra')
|
|||
|
||||
### ESM
|
||||
|
||||
There is also an `fs-extra/esm` import, that supports both default and named exports. However, note that `fs` methods are not included in `fs-extra/esm`; you still need to import `fs` and/or `fs/promises` separately:
|
||||
There is also an `fs-extra/esm` import, that supports both default and named exports. However, note that `fs` methods are not included in `fs-extra/esm`; you still need to import `fs` and/or `fs/promises` seperately:
|
||||
|
||||
```js
|
||||
import { readFileSync } from 'fs'
|
||||
|
|
|
|||
8
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/ensure/link.js
generated
vendored
8
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/ensure/link.js
generated
vendored
|
|
@ -10,14 +10,14 @@ const { areIdentical } = require('../util/stat')
|
|||
async function createLink (srcpath, dstpath) {
|
||||
let dstStat
|
||||
try {
|
||||
dstStat = await fs.lstat(dstpath, { bigint: true })
|
||||
dstStat = await fs.lstat(dstpath)
|
||||
} catch {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
let srcStat
|
||||
try {
|
||||
srcStat = await fs.lstat(srcpath, { bigint: true })
|
||||
srcStat = await fs.lstat(srcpath)
|
||||
} catch (err) {
|
||||
err.message = err.message.replace('lstat', 'ensureLink')
|
||||
throw err
|
||||
|
|
@ -39,11 +39,11 @@ async function createLink (srcpath, dstpath) {
|
|||
function createLinkSync (srcpath, dstpath) {
|
||||
let dstStat
|
||||
try {
|
||||
dstStat = fs.lstatSync(dstpath, { bigint: true })
|
||||
dstStat = fs.lstatSync(dstpath)
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
const srcStat = fs.lstatSync(srcpath, { bigint: true })
|
||||
const srcStat = fs.lstatSync(srcpath)
|
||||
if (dstStat && areIdentical(srcStat, dstStat)) return
|
||||
} catch (err) {
|
||||
err.message = err.message.replace('lstat', 'ensureLink')
|
||||
|
|
|
|||
16
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/ensure/symlink.js
generated
vendored
16
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/ensure/symlink.js
generated
vendored
|
|
@ -24,18 +24,18 @@ async function createSymlink (srcpath, dstpath, type) {
|
|||
// (standard symlink behavior) or fall back to cwd if that doesn't exist
|
||||
let srcStat
|
||||
if (path.isAbsolute(srcpath)) {
|
||||
srcStat = await fs.stat(srcpath, { bigint: true })
|
||||
srcStat = await fs.stat(srcpath)
|
||||
} else {
|
||||
const dstdir = path.dirname(dstpath)
|
||||
const relativeToDst = path.join(dstdir, srcpath)
|
||||
try {
|
||||
srcStat = await fs.stat(relativeToDst, { bigint: true })
|
||||
srcStat = await fs.stat(relativeToDst)
|
||||
} catch {
|
||||
srcStat = await fs.stat(srcpath, { bigint: true })
|
||||
srcStat = await fs.stat(srcpath)
|
||||
}
|
||||
}
|
||||
|
||||
const dstStat = await fs.stat(dstpath, { bigint: true })
|
||||
const dstStat = await fs.stat(dstpath)
|
||||
if (areIdentical(srcStat, dstStat)) return
|
||||
}
|
||||
|
||||
|
|
@ -61,18 +61,18 @@ function createSymlinkSync (srcpath, dstpath, type) {
|
|||
// (standard symlink behavior) or fall back to cwd if that doesn't exist
|
||||
let srcStat
|
||||
if (path.isAbsolute(srcpath)) {
|
||||
srcStat = fs.statSync(srcpath, { bigint: true })
|
||||
srcStat = fs.statSync(srcpath)
|
||||
} else {
|
||||
const dstdir = path.dirname(dstpath)
|
||||
const relativeToDst = path.join(dstdir, srcpath)
|
||||
try {
|
||||
srcStat = fs.statSync(relativeToDst, { bigint: true })
|
||||
srcStat = fs.statSync(relativeToDst)
|
||||
} catch {
|
||||
srcStat = fs.statSync(srcpath, { bigint: true })
|
||||
srcStat = fs.statSync(srcpath)
|
||||
}
|
||||
}
|
||||
|
||||
const dstStat = fs.statSync(dstpath, { bigint: true })
|
||||
const dstStat = fs.statSync(dstpath)
|
||||
if (areIdentical(srcStat, dstStat)) return
|
||||
}
|
||||
|
||||
|
|
|
|||
10
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/util/stat.js
generated
vendored
10
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/util/stat.js
generated
vendored
|
|
@ -101,10 +101,7 @@ async function checkParentPaths (src, srcStat, dest, funcName) {
|
|||
try {
|
||||
destStat = await fs.stat(destParent, { bigint: true })
|
||||
} catch (err) {
|
||||
// The destination parent does not exist yet, but a deeper ancestor might
|
||||
// (e.g. when it is a symlink into the source tree). Keep walking up so the
|
||||
// self-subdirectory check is not bypassed.
|
||||
if (err.code === 'ENOENT') return checkParentPaths(src, srcStat, destParent, funcName)
|
||||
if (err.code === 'ENOENT') return
|
||||
throw err
|
||||
}
|
||||
|
||||
|
|
@ -123,10 +120,7 @@ function checkParentPathsSync (src, srcStat, dest, funcName) {
|
|||
try {
|
||||
destStat = fs.statSync(destParent, { bigint: true })
|
||||
} catch (err) {
|
||||
// The destination parent does not exist yet, but a deeper ancestor might
|
||||
// (e.g. when it is a symlink into the source tree). Keep walking up so the
|
||||
// self-subdirectory check is not bypassed.
|
||||
if (err.code === 'ENOENT') return checkParentPathsSync(src, srcStat, destParent, funcName)
|
||||
if (err.code === 'ENOENT') return
|
||||
throw err
|
||||
}
|
||||
if (areIdentical(srcStat, destStat)) {
|
||||
|
|
|
|||
33
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/util/utimes.js
generated
vendored
33
electron/node_modules/@capacitor/cli/node_modules/fs-extra/lib/util/utimes.js
generated
vendored
|
|
@ -4,47 +4,30 @@ const fs = require('../fs')
|
|||
const u = require('universalify').fromPromise
|
||||
|
||||
async function utimesMillis (path, atime, mtime) {
|
||||
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
|
||||
const fd = await fs.open(path, 'r+')
|
||||
|
||||
let error = null
|
||||
let closeErr = null
|
||||
|
||||
try {
|
||||
await fs.futimes(fd, atime, mtime)
|
||||
} catch (futimesErr) {
|
||||
error = futimesErr
|
||||
} finally {
|
||||
try {
|
||||
await fs.close(fd)
|
||||
} catch (closeErr) {
|
||||
if (!error) error = closeErr
|
||||
} catch (e) {
|
||||
closeErr = e
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
if (closeErr) {
|
||||
throw closeErr
|
||||
}
|
||||
}
|
||||
|
||||
function utimesMillisSync (path, atime, mtime) {
|
||||
const fd = fs.openSync(path, 'r+')
|
||||
|
||||
let error = null
|
||||
|
||||
try {
|
||||
fs.futimesSync(fd, atime, mtime)
|
||||
} catch (futimesErr) {
|
||||
error = futimesErr
|
||||
} finally {
|
||||
try {
|
||||
fs.closeSync(fd)
|
||||
} catch (closeErr) {
|
||||
if (!error) error = closeErr
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
fs.futimesSync(fd, atime, mtime)
|
||||
return fs.closeSync(fd)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
2
electron/node_modules/@capacitor/cli/node_modules/fs-extra/package.json
generated
vendored
2
electron/node_modules/@capacitor/cli/node_modules/fs-extra/package.json
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fs-extra",
|
||||
"version": "11.3.6",
|
||||
"version": "11.3.4",
|
||||
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as recursive mkdir, copy, and remove.",
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
|
|
|
|||
2
electron/node_modules/@capacitor/cli/package.json
generated
vendored
2
electron/node_modules/@capacitor/cli/package.json
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@capacitor/cli",
|
||||
"version": "8.4.1",
|
||||
"version": "8.3.1",
|
||||
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
|
||||
"homepage": "https://capacitorjs.com",
|
||||
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
|
||||
|
|
|
|||
2
electron/node_modules/@capacitor/core/package.json
generated
vendored
2
electron/node_modules/@capacitor/core/package.json
generated
vendored
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@capacitor/core",
|
||||
"version": "8.4.1",
|
||||
"version": "8.3.1",
|
||||
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
|
||||
"homepage": "https://capacitorjs.com",
|
||||
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
|
||||
|
|
|
|||
259
electron/node_modules/@develar/schema-utils/CHANGELOG.md
generated
vendored
Normal file
259
electron/node_modules/@develar/schema-utils/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [2.6.5](https://github.com/webpack/schema-utils/compare/v2.6.4...v2.6.5) (2020-03-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typo ([#88](https://github.com/webpack/schema-utils/issues/88)) ([52fc92b](https://github.com/webpack/schema-utils/commit/52fc92b531b7503b7bbe1bf9b21bfa26dffbc8f5))
|
||||
|
||||
### [2.6.4](https://github.com/webpack/schema-utils/compare/v2.6.3...v2.6.4) (2020-01-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* change `initialised` to `initialized` ([#87](https://github.com/webpack/schema-utils/issues/87)) ([70f12d3](https://github.com/webpack/schema-utils/commit/70f12d33a8eaa27249bc9c1a27f886724cf91ea7))
|
||||
|
||||
### [2.6.3](https://github.com/webpack/schema-utils/compare/v2.6.2...v2.6.3) (2020-01-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prefer the `baseDataPath` option from arguments ([#86](https://github.com/webpack/schema-utils/issues/86)) ([e236859](https://github.com/webpack/schema-utils/commit/e236859e85b28e35e1294f86fc1ff596a5031cea))
|
||||
|
||||
### [2.6.2](https://github.com/webpack/schema-utils/compare/v2.6.1...v2.6.2) (2020-01-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* better handle Windows absolute paths ([#85](https://github.com/webpack/schema-utils/issues/85)) ([1fa2930](https://github.com/webpack/schema-utils/commit/1fa2930a161e907b9fc53a7233d605910afdb883))
|
||||
|
||||
### [2.6.1](https://github.com/webpack/schema-utils/compare/v2.6.0...v2.6.1) (2019-11-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typescript declarations ([#84](https://github.com/webpack/schema-utils/issues/84)) ([89d55a9](https://github.com/webpack/schema-utils/commit/89d55a9a8edfa6a8ac8b112f226bb3154e260319))
|
||||
|
||||
## [2.6.0](https://github.com/webpack/schema-utils/compare/v2.5.0...v2.6.0) (2019-11-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support configuration via title ([#81](https://github.com/webpack/schema-utils/issues/81)) ([afddc10](https://github.com/webpack/schema-utils/commit/afddc109f6891cd37a9f1835d50862d119a072bf))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typescript definitions ([#70](https://github.com/webpack/schema-utils/issues/70)) ([f38158d](https://github.com/webpack/schema-utils/commit/f38158d6d040e2c701622778ae8122fb26a4f990))
|
||||
|
||||
## [2.5.0](https://github.com/webpack/schema-utils/compare/v2.4.1...v2.5.0) (2019-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rework format for maxLength, minLength ([#67](https://github.com/webpack/schema-utils/issues/67)) ([0d12259](https://github.com/webpack/schema-utils/commit/0d12259))
|
||||
* support all cases with one number in range ([#64](https://github.com/webpack/schema-utils/issues/64)) ([7fc8069](https://github.com/webpack/schema-utils/commit/7fc8069))
|
||||
* typescript definition and export naming ([#69](https://github.com/webpack/schema-utils/issues/69)) ([a435b79](https://github.com/webpack/schema-utils/commit/a435b79))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* "smart" numbers range ([62fb107](https://github.com/webpack/schema-utils/commit/62fb107))
|
||||
|
||||
### [2.4.1](https://github.com/webpack/schema-utils/compare/v2.4.0...v2.4.1) (2019-09-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* publish definitions ([#58](https://github.com/webpack/schema-utils/issues/58)) ([1885faa](https://github.com/webpack/schema-utils/commit/1885faa))
|
||||
|
||||
## [2.4.0](https://github.com/webpack/schema-utils/compare/v2.3.0...v2.4.0) (2019-09-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* better errors when the `type` keyword doesn't exist ([0988be2](https://github.com/webpack/schema-utils/commit/0988be2))
|
||||
* support $data reference ([#56](https://github.com/webpack/schema-utils/issues/56)) ([d2f11d6](https://github.com/webpack/schema-utils/commit/d2f11d6))
|
||||
* types definitions ([#52](https://github.com/webpack/schema-utils/issues/52)) ([facb431](https://github.com/webpack/schema-utils/commit/facb431))
|
||||
|
||||
## [2.3.0](https://github.com/webpack/schema-utils/compare/v2.2.0...v2.3.0) (2019-09-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support `not` keyword ([#53](https://github.com/webpack/schema-utils/issues/53)) ([765f458](https://github.com/webpack/schema-utils/commit/765f458))
|
||||
|
||||
## [2.2.0](https://github.com/webpack/schema-utils/compare/v2.1.0...v2.2.0) (2019-09-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* better error output for `oneOf` and `anyOf` ([#48](https://github.com/webpack/schema-utils/issues/48)) ([#50](https://github.com/webpack/schema-utils/issues/50)) ([332242f](https://github.com/webpack/schema-utils/commit/332242f))
|
||||
|
||||
## [2.1.0](https://github.com/webpack-contrib/schema-utils/compare/v2.0.1...v2.1.0) (2019-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* throw error on sparse arrays ([#47](https://github.com/webpack-contrib/schema-utils/issues/47)) ([b85ac38](https://github.com/webpack-contrib/schema-utils/commit/b85ac38))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* export `ValidateError` ([#46](https://github.com/webpack-contrib/schema-utils/issues/46)) ([ff781d7](https://github.com/webpack-contrib/schema-utils/commit/ff781d7))
|
||||
|
||||
|
||||
|
||||
### [2.0.1](https://github.com/webpack-contrib/schema-utils/compare/v2.0.0...v2.0.1) (2019-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* error message for empty object ([#44](https://github.com/webpack-contrib/schema-utils/issues/44)) ([0b4b4a2](https://github.com/webpack-contrib/schema-utils/commit/0b4b4a2))
|
||||
|
||||
|
||||
|
||||
### [2.0.0](https://github.com/webpack-contrib/schema-utils/compare/v1.0.0...v2.0.0) (2019-07-17)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* drop support for Node.js < 8.9.0
|
||||
* drop support `errorMessage`, please use `description` for links.
|
||||
* api was changed, please look documentation.
|
||||
* error messages was fully rewritten.
|
||||
|
||||
|
||||
<a name="1.0.0"></a>
|
||||
# [1.0.0](https://github.com/webpack-contrib/schema-utils/compare/v0.4.7...v1.0.0) (2018-08-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **src:** add support for custom error messages ([#33](https://github.com/webpack-contrib/schema-utils/issues/33)) ([1cbe4ef](https://github.com/webpack-contrib/schema-utils/commit/1cbe4ef))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.7"></a>
|
||||
## [0.4.7](https://github.com/webpack-contrib/schema-utils/compare/v0.4.6...v0.4.7) (2018-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **src:** `node >= v4.0.0` support ([#32](https://github.com/webpack-contrib/schema-utils/issues/32)) ([cb13dd4](https://github.com/webpack-contrib/schema-utils/commit/cb13dd4))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.6"></a>
|
||||
## [0.4.6](https://github.com/webpack-contrib/schema-utils/compare/v0.4.5...v0.4.6) (2018-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **package:** remove lockfile ([#28](https://github.com/webpack-contrib/schema-utils/issues/28)) ([69f1a81](https://github.com/webpack-contrib/schema-utils/commit/69f1a81))
|
||||
* **package:** remove unnecessary `webpack` dependency ([#26](https://github.com/webpack-contrib/schema-utils/issues/26)) ([532eaa5](https://github.com/webpack-contrib/schema-utils/commit/532eaa5))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.5"></a>
|
||||
## [0.4.5](https://github.com/webpack-contrib/schema-utils/compare/v0.4.4...v0.4.5) (2018-02-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **CHANGELOG:** update broken links ([4483b9f](https://github.com/webpack-contrib/schema-utils/commit/4483b9f))
|
||||
* **package:** update broken links ([f2494ba](https://github.com/webpack-contrib/schema-utils/commit/f2494ba))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.4"></a>
|
||||
## [0.4.4](https://github.com/webpack-contrib/schema-utils/compare/v0.4.3...v0.4.4) (2018-02-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **package:** update `dependencies` ([#22](https://github.com/webpack-contrib/schema-utils/issues/22)) ([3aecac6](https://github.com/webpack-contrib/schema-utils/commit/3aecac6))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.3"></a>
|
||||
## [0.4.3](https://github.com/webpack-contrib/schema-utils/compare/v0.4.2...v0.4.3) (2017-12-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **validateOptions:** throw `err` instead of `process.exit(1)` ([#17](https://github.com/webpack-contrib/schema-utils/issues/17)) ([c595eda](https://github.com/webpack-contrib/schema-utils/commit/c595eda))
|
||||
* **ValidationError:** never return `this` in the ctor ([#16](https://github.com/webpack-contrib/schema-utils/issues/16)) ([c723791](https://github.com/webpack-contrib/schema-utils/commit/c723791))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.2"></a>
|
||||
## [0.4.2](https://github.com/webpack-contrib/schema-utils/compare/v0.4.1...v0.4.2) (2017-11-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **validateOptions:** catch `ValidationError` and handle it internally ([#15](https://github.com/webpack-contrib/schema-utils/issues/15)) ([9c5ef5e](https://github.com/webpack-contrib/schema-utils/commit/9c5ef5e))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.1"></a>
|
||||
## [0.4.1](https://github.com/webpack-contrib/schema-utils/compare/v0.4.0...v0.4.1) (2017-11-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ValidationError:** use `Error.captureStackTrace` for `err.stack` handling ([#14](https://github.com/webpack-contrib/schema-utils/issues/14)) ([a6fb974](https://github.com/webpack-contrib/schema-utils/commit/a6fb974))
|
||||
|
||||
|
||||
|
||||
<a name="0.4.0"></a>
|
||||
# [0.4.0](https://github.com/webpack-contrib/schema-utils/compare/v0.3.0...v0.4.0) (2017-10-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add support for `typeof`, `instanceof` (`{Function\|RegExp}`) ([#10](https://github.com/webpack-contrib/schema-utils/issues/10)) ([9f01816](https://github.com/webpack-contrib/schema-utils/commit/9f01816))
|
||||
|
||||
|
||||
|
||||
<a name="0.3.0"></a>
|
||||
# [0.3.0](https://github.com/webpack-contrib/schema-utils/compare/v0.2.1...v0.3.0) (2017-04-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add ValidationError ([#8](https://github.com/webpack-contrib/schema-utils/issues/8)) ([d48f0fb](https://github.com/webpack-contrib/schema-utils/commit/d48f0fb))
|
||||
|
||||
|
||||
|
||||
<a name="0.2.1"></a>
|
||||
## [0.2.1](https://github.com/webpack-contrib/schema-utils/compare/v0.2.0...v0.2.1) (2017-03-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Include .babelrc to `files` ([28f0363](https://github.com/webpack-contrib/schema-utils/commit/28f0363))
|
||||
* Include source to `files` ([43b0f2f](https://github.com/webpack-contrib/schema-utils/commit/43b0f2f))
|
||||
|
||||
|
||||
|
||||
<a name="0.2.0"></a>
|
||||
# [0.2.0](https://github.com/webpack-contrib/schema-utils/compare/v0.1.0...v0.2.0) (2017-03-12)
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
# 0.1.0 (2017-03-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **validations:** add validateOptions module ([ae9b47b](https://github.com/webpack-contrib/schema-utils/commit/ae9b47b))
|
||||
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
20
electron/node_modules/@develar/schema-utils/LICENSE
generated
vendored
Normal file
20
electron/node_modules/@develar/schema-utils/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Copyright JS Foundation and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
276
electron/node_modules/@develar/schema-utils/README.md
generated
vendored
Normal file
276
electron/node_modules/@develar/schema-utils/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
<div align="center">
|
||||
<a href="http://json-schema.org">
|
||||
<img width="160" height="160"
|
||||
src="https://raw.githubusercontent.com/webpack-contrib/schema-utils/master/.github/assets/logo.png">
|
||||
</a>
|
||||
<a href="https://github.com/webpack/webpack">
|
||||
<img width="200" height="200"
|
||||
src="https://webpack.js.org/assets/icon-square-big.svg">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
[![npm][npm]][npm-url]
|
||||
[![node][node]][node-url]
|
||||
[![deps][deps]][deps-url]
|
||||
[![tests][tests]][tests-url]
|
||||
[![coverage][cover]][cover-url]
|
||||
[![chat][chat]][chat-url]
|
||||
[![size][size]][size-url]
|
||||
|
||||
# schema-utils
|
||||
|
||||
Package for validate options in loaders and plugins.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To begin, you'll need to install `schema-utils`:
|
||||
|
||||
```console
|
||||
npm install schema-utils
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
**schema.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"option": {
|
||||
"type": ["boolean"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
import schema from './path/to/schema.json';
|
||||
import validate from 'schema-utils';
|
||||
|
||||
const options = { option: true };
|
||||
const configuration = { name: 'Loader Name/Plugin Name/Name' };
|
||||
|
||||
validate(schema, options, configuration);
|
||||
```
|
||||
|
||||
### `schema`
|
||||
|
||||
Type: `String`
|
||||
|
||||
JSON schema.
|
||||
|
||||
Simple example of schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "This is description of option.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
### `options`
|
||||
|
||||
Type: `Object`
|
||||
|
||||
Object with options.
|
||||
|
||||
```js
|
||||
validate(
|
||||
schema,
|
||||
{
|
||||
name: 123,
|
||||
},
|
||||
{ name: 'MyPlugin' }
|
||||
);
|
||||
```
|
||||
|
||||
### `configuration`
|
||||
|
||||
Allow to configure validator.
|
||||
|
||||
There is an alternative method to configure the `name` and`baseDataPath` options via the `title` property in the schema.
|
||||
For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "My Loader options",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "This is description of option.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
The last word used for the `baseDataPath` option, other words used for the `name` option.
|
||||
Based on the example above the `name` option equals `My Loader`, the `baseDataPath` option equals `options`.
|
||||
|
||||
#### `name`
|
||||
|
||||
Type: `Object`
|
||||
Default: `"Object"`
|
||||
|
||||
Allow to setup name in validation errors.
|
||||
|
||||
```js
|
||||
validate(schema, options, { name: 'MyPlugin' });
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid configuration object. MyPlugin has been initialised using a configuration object that does not match the API schema.
|
||||
- configuration.optionName should be a integer.
|
||||
```
|
||||
|
||||
#### `baseDataPath`
|
||||
|
||||
Type: `String`
|
||||
Default: `"configuration"`
|
||||
|
||||
Allow to setup base data path in validation errors.
|
||||
|
||||
```js
|
||||
validate(schema, options, { name: 'MyPlugin', baseDataPath: 'options' });
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid options object. MyPlugin has been initialised using an options object that does not match the API schema.
|
||||
- options.optionName should be a integer.
|
||||
```
|
||||
|
||||
#### `postFormatter`
|
||||
|
||||
Type: `Function`
|
||||
Default: `undefined`
|
||||
|
||||
Allow to reformat errors.
|
||||
|
||||
```js
|
||||
validate(schema, options, {
|
||||
name: 'MyPlugin',
|
||||
postFormatter: (formattedError, error) => {
|
||||
if (error.keyword === 'type') {
|
||||
return `${formattedError}\nAdditional Information.`;
|
||||
}
|
||||
|
||||
return formattedError;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
```shell
|
||||
Invalid options object. MyPlugin has been initialized using an options object that does not match the API schema.
|
||||
- options.optionName should be a integer.
|
||||
Additional Information.
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
**schema.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"test": {
|
||||
"anyOf": [
|
||||
{ "type": "array" },
|
||||
{ "type": "string" },
|
||||
{ "instanceof": "RegExp" }
|
||||
]
|
||||
},
|
||||
"transform": {
|
||||
"instanceof": "Function"
|
||||
},
|
||||
"sourceMap": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
### `Loader`
|
||||
|
||||
```js
|
||||
import { getOptions } from 'loader-utils';
|
||||
import validateOptions from 'schema-utils';
|
||||
|
||||
import schema from 'path/to/schema.json';
|
||||
|
||||
function loader(src, map) {
|
||||
const options = getOptions(this) || {};
|
||||
|
||||
validateOptions(schema, options, {
|
||||
name: 'Loader Name',
|
||||
baseDataPath: 'options',
|
||||
});
|
||||
|
||||
// Code...
|
||||
}
|
||||
|
||||
export default loader;
|
||||
```
|
||||
|
||||
### `Plugin`
|
||||
|
||||
```js
|
||||
import validateOptions from 'schema-utils';
|
||||
|
||||
import schema from 'path/to/schema.json';
|
||||
|
||||
class Plugin {
|
||||
constructor(options) {
|
||||
validateOptions(schema, options, {
|
||||
name: 'Plugin Name',
|
||||
baseDataPath: 'options',
|
||||
});
|
||||
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
apply(compiler) {
|
||||
// Code...
|
||||
}
|
||||
}
|
||||
|
||||
export default Plugin;
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Please take a moment to read our contributing guidelines if you haven't yet done so.
|
||||
|
||||
[CONTRIBUTING](./.github/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
[npm]: https://img.shields.io/npm/v/schema-utils.svg
|
||||
[npm-url]: https://npmjs.com/package/schema-utils
|
||||
[node]: https://img.shields.io/node/v/schema-utils.svg
|
||||
[node-url]: https://nodejs.org
|
||||
[deps]: https://david-dm.org/webpack/schema-utils.svg
|
||||
[deps-url]: https://david-dm.org/webpack/schema-utils
|
||||
[tests]: https://dev.azure.com/webpack/schema-utils/_apis/build/status/webpack.schema-utils?branchName=master
|
||||
[tests-url]: https://dev.azure.com/webpack/schema-utils/_build/latest?definitionId=9&branchName=master
|
||||
[cover]: https://codecov.io/gh/webpack/schema-utils/branch/master/graph/badge.svg
|
||||
[cover-url]: https://codecov.io/gh/webpack/schema-utils
|
||||
[chat]: https://badges.gitter.im/webpack/webpack.svg
|
||||
[chat-url]: https://gitter.im/webpack/webpack
|
||||
[size]: https://packagephobia.now.sh/badge?p=schema-utils
|
||||
[size-url]: https://packagephobia.now.sh/result?p=schema-utils
|
||||
115
electron/node_modules/@develar/schema-utils/declarations/ValidationError.d.ts
generated
vendored
Normal file
115
electron/node_modules/@develar/schema-utils/declarations/ValidationError.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
export default ValidationError;
|
||||
export type JSONSchema6 = import('json-schema').JSONSchema6;
|
||||
export type JSONSchema7 = import('json-schema').JSONSchema7;
|
||||
export type Schema =
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7;
|
||||
export type ValidationErrorConfiguration = {
|
||||
name?: string | undefined;
|
||||
baseDataPath?: string | undefined;
|
||||
postFormatter?: import('./validate').PostFormatter | undefined;
|
||||
};
|
||||
export type PostFormatter = (
|
||||
formattedError: string,
|
||||
error: import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
}
|
||||
) => string;
|
||||
export type SchemaUtilErrorObject = import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
};
|
||||
export type SPECIFICITY = number;
|
||||
declare class ValidationError extends Error {
|
||||
/**
|
||||
* @param {Array<SchemaUtilErrorObject>} errors
|
||||
* @param {Schema} schema
|
||||
* @param {ValidationErrorConfiguration} configuration
|
||||
*/
|
||||
constructor(
|
||||
errors: (import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
})[],
|
||||
schema:
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7,
|
||||
configuration?: import('./validate').ValidationErrorConfiguration
|
||||
);
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
errors: Array<SchemaUtilErrorObject>;
|
||||
/** @type {Schema} */
|
||||
schema: Schema;
|
||||
/** @type {string} */
|
||||
headerName: string;
|
||||
/** @type {string} */
|
||||
baseDataPath: string;
|
||||
/** @type {PostFormatter | null} */
|
||||
postFormatter: PostFormatter | null;
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Schema}
|
||||
*/
|
||||
getSchemaPart(
|
||||
path: string
|
||||
):
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7;
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<Object>} prevSchemas
|
||||
* @returns {string}
|
||||
*/
|
||||
formatSchema(
|
||||
schema:
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7,
|
||||
prevSchemas?: Object[]
|
||||
): string;
|
||||
/**
|
||||
* @param {Schema=} schemaPart
|
||||
* @param {(boolean | Array<string>)=} additionalPath
|
||||
* @param {boolean=} needDot
|
||||
* @returns {string}
|
||||
*/
|
||||
getSchemaPartText(
|
||||
schemaPart?:
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7
|
||||
| undefined,
|
||||
additionalPath?: boolean | string[] | undefined,
|
||||
needDot?: boolean | undefined
|
||||
): string;
|
||||
/**
|
||||
* @param {Schema=} schemaPart
|
||||
* @returns {string}
|
||||
*/
|
||||
getSchemaPartDescription(
|
||||
schemaPart?:
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7
|
||||
| undefined
|
||||
): string;
|
||||
/**
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
* @returns {string}
|
||||
*/
|
||||
formatValidationError(
|
||||
error: import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
}
|
||||
): string;
|
||||
/**
|
||||
* @param {Array<SchemaUtilErrorObject>} errors
|
||||
* @returns {string}
|
||||
*/
|
||||
formatValidationErrors(
|
||||
errors: (import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
})[]
|
||||
): string;
|
||||
}
|
||||
2
electron/node_modules/@develar/schema-utils/declarations/index.d.ts
generated
vendored
Normal file
2
electron/node_modules/@develar/schema-utils/declarations/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
declare const _exports: typeof import('./validate').default;
|
||||
export = _exports;
|
||||
13
electron/node_modules/@develar/schema-utils/declarations/keywords/absolutePath.d.ts
generated
vendored
Normal file
13
electron/node_modules/@develar/schema-utils/declarations/keywords/absolutePath.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export default addAbsolutePathKeyword;
|
||||
export type Ajv = import('ajv').Ajv;
|
||||
export type SchemaUtilErrorObject = import('ajv').ErrorObject & {
|
||||
children?: import('ajv').ErrorObject[] | undefined;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {Ajv} ajv
|
||||
* @returns {Ajv}
|
||||
*/
|
||||
declare function addAbsolutePathKeyword(
|
||||
ajv: import('ajv').Ajv
|
||||
): import('ajv').Ajv;
|
||||
43
electron/node_modules/@develar/schema-utils/declarations/util/Range.d.ts
generated
vendored
Normal file
43
electron/node_modules/@develar/schema-utils/declarations/util/Range.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
export = Range;
|
||||
/**
|
||||
* @typedef {[number, boolean]} RangeValue
|
||||
*/
|
||||
/**
|
||||
* @callback RangeValueCallback
|
||||
* @param {RangeValue} rangeValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
declare class Range {
|
||||
/** @type {Array<RangeValue>} */
|
||||
_left: Array<RangeValue>;
|
||||
/** @type {Array<RangeValue>} */
|
||||
_right: Array<RangeValue>;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
left(value: number, exclusive?: boolean | undefined): void;
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
right(value: number, exclusive?: boolean | undefined): void;
|
||||
/**
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {string} "smart" range string representation
|
||||
*/
|
||||
format(logic?: boolean): string;
|
||||
}
|
||||
declare namespace Range {
|
||||
export {
|
||||
getOperator,
|
||||
formatRight,
|
||||
formatLeft,
|
||||
formatRange,
|
||||
getRangeValue,
|
||||
RangeValue,
|
||||
RangeValueCallback,
|
||||
};
|
||||
}
|
||||
type RangeValue = [number, boolean];
|
||||
type RangeValueCallback = (rangeValue: [number, boolean]) => boolean;
|
||||
43
electron/node_modules/@develar/schema-utils/declarations/validate.d.ts
generated
vendored
Normal file
43
electron/node_modules/@develar/schema-utils/declarations/validate.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
export default validate;
|
||||
export type JSONSchema4 = import('json-schema').JSONSchema4;
|
||||
export type JSONSchema6 = import('json-schema').JSONSchema6;
|
||||
export type JSONSchema7 = import('json-schema').JSONSchema7;
|
||||
export type ErrorObject = Ajv.ErrorObject;
|
||||
export type Schema =
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7;
|
||||
export type SchemaUtilErrorObject = Ajv.ErrorObject & {
|
||||
children?: Ajv.ErrorObject[] | undefined;
|
||||
};
|
||||
export type PostFormatter = (
|
||||
formattedError: string,
|
||||
error: Ajv.ErrorObject & {
|
||||
children?: Ajv.ErrorObject[] | undefined;
|
||||
}
|
||||
) => string;
|
||||
export type ValidationErrorConfiguration = {
|
||||
name?: string | undefined;
|
||||
baseDataPath?: string | undefined;
|
||||
postFormatter?: PostFormatter | undefined;
|
||||
};
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @param {ValidationErrorConfiguration=} configuration
|
||||
* @returns {void}
|
||||
*/
|
||||
declare function validate(
|
||||
schema:
|
||||
| import('json-schema').JSONSchema4
|
||||
| import('json-schema').JSONSchema6
|
||||
| import('json-schema').JSONSchema7,
|
||||
options: any,
|
||||
configuration?: ValidationErrorConfiguration | undefined
|
||||
): void;
|
||||
declare namespace validate {
|
||||
export { ValidationError };
|
||||
export { ValidationError as ValidateError };
|
||||
}
|
||||
import Ajv from 'ajv';
|
||||
import ValidationError from './ValidationError';
|
||||
1286
electron/node_modules/@develar/schema-utils/dist/ValidationError.js
generated
vendored
Normal file
1286
electron/node_modules/@develar/schema-utils/dist/ValidationError.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
5
electron/node_modules/@develar/schema-utils/dist/index.js
generated
vendored
Normal file
5
electron/node_modules/@develar/schema-utils/dist/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
const validate = require('./validate');
|
||||
|
||||
module.exports = validate.default;
|
||||
91
electron/node_modules/@develar/schema-utils/dist/keywords/absolutePath.js
generated
vendored
Normal file
91
electron/node_modules/@develar/schema-utils/dist/keywords/absolutePath.js
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
/** @typedef {import("ajv").Ajv} Ajv */
|
||||
|
||||
/** @typedef {import("../validate").SchemaUtilErrorObject} SchemaUtilErrorObject */
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} data
|
||||
* @param {object} schema
|
||||
* @param {string} message
|
||||
* @returns {object} // Todo `returns` should be `SchemaUtilErrorObject`
|
||||
*/
|
||||
function errorMessage(message, schema, data) {
|
||||
return {
|
||||
keyword: 'absolutePath',
|
||||
params: {
|
||||
absolutePath: data
|
||||
},
|
||||
message,
|
||||
parentSchema: schema
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @param {boolean} shouldBeAbsolute
|
||||
* @param {object} schema
|
||||
* @param {string} data
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
|
||||
function getErrorFor(shouldBeAbsolute, schema, data) {
|
||||
const message = shouldBeAbsolute ? `The provided value ${JSON.stringify(data)} is not an absolute path!` : `A relative path is expected. However, the provided value ${JSON.stringify(data)} is an absolute path!`;
|
||||
return errorMessage(message, schema, data);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {Ajv} ajv
|
||||
* @returns {Ajv}
|
||||
*/
|
||||
|
||||
|
||||
function addAbsolutePathKeyword(ajv) {
|
||||
ajv.addKeyword('absolutePath', {
|
||||
errors: true,
|
||||
type: 'string',
|
||||
|
||||
compile(schema, parentSchema) {
|
||||
/**
|
||||
* @param {any} data
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function callback(data) {
|
||||
let passes = true;
|
||||
const isExclamationMarkPresent = data.includes('!');
|
||||
|
||||
if (isExclamationMarkPresent) {
|
||||
callback.errors = [errorMessage(`The provided value ${JSON.stringify(data)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`, parentSchema, data)];
|
||||
passes = false;
|
||||
} // ?:[A-Za-z]:\\ - Windows absolute path
|
||||
// \\\\ - Windows network absolute path
|
||||
// \/ - Unix-like OS absolute path
|
||||
|
||||
|
||||
const isCorrectAbsolutePath = schema === /^(?:[A-Za-z]:(\\|\/)|\\\\|\/)/.test(data);
|
||||
|
||||
if (!isCorrectAbsolutePath) {
|
||||
callback.errors = [getErrorFor(schema, parentSchema, data)];
|
||||
passes = false;
|
||||
}
|
||||
|
||||
return passes;
|
||||
}
|
||||
/** @type {null | Array<SchemaUtilErrorObject>}*/
|
||||
|
||||
|
||||
callback.errors = [];
|
||||
return callback;
|
||||
}
|
||||
|
||||
});
|
||||
return ajv;
|
||||
}
|
||||
|
||||
var _default = addAbsolutePathKeyword;
|
||||
exports.default = _default;
|
||||
163
electron/node_modules/@develar/schema-utils/dist/util/Range.js
generated
vendored
Normal file
163
electron/node_modules/@develar/schema-utils/dist/util/Range.js
generated
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* @typedef {[number, boolean]} RangeValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback RangeValueCallback
|
||||
* @param {RangeValue} rangeValue
|
||||
* @returns {boolean}
|
||||
*/
|
||||
class Range {
|
||||
/**
|
||||
* @param {"left" | "right"} side
|
||||
* @param {boolean} exclusive
|
||||
* @returns {">" | ">=" | "<" | "<="}
|
||||
*/
|
||||
static getOperator(side, exclusive) {
|
||||
if (side === 'left') {
|
||||
return exclusive ? '>' : '>=';
|
||||
}
|
||||
|
||||
return exclusive ? '<' : '<=';
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatRight(value, logic, exclusive) {
|
||||
if (logic === false) {
|
||||
return Range.formatLeft(value, !logic, !exclusive);
|
||||
}
|
||||
|
||||
return `should be ${Range.getOperator('right', exclusive)} ${value}`;
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @param {boolean} exclusive is range exclusive
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatLeft(value, logic, exclusive) {
|
||||
if (logic === false) {
|
||||
return Range.formatRight(value, !logic, !exclusive);
|
||||
}
|
||||
|
||||
return `should be ${Range.getOperator('left', exclusive)} ${value}`;
|
||||
}
|
||||
/**
|
||||
* @param {number} start left side value
|
||||
* @param {number} end right side value
|
||||
* @param {boolean} startExclusive is range exclusive from left side
|
||||
* @param {boolean} endExclusive is range exclusive from right side
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
|
||||
static formatRange(start, end, startExclusive, endExclusive, logic) {
|
||||
let result = 'should be';
|
||||
result += ` ${Range.getOperator(logic ? 'left' : 'right', logic ? startExclusive : !startExclusive)} ${start} `;
|
||||
result += logic ? 'and' : 'or';
|
||||
result += ` ${Range.getOperator(logic ? 'right' : 'left', logic ? endExclusive : !endExclusive)} ${end}`;
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @param {Array<RangeValue>} values
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {RangeValue} computed value and it's exclusive flag
|
||||
*/
|
||||
|
||||
|
||||
static getRangeValue(values, logic) {
|
||||
let minMax = logic ? Infinity : -Infinity;
|
||||
let j = -1;
|
||||
const predicate = logic ?
|
||||
/** @type {RangeValueCallback} */
|
||||
([value]) => value <= minMax :
|
||||
/** @type {RangeValueCallback} */
|
||||
([value]) => value >= minMax;
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (predicate(values[i])) {
|
||||
[minMax] = values[i];
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (j > -1) {
|
||||
return values[j];
|
||||
}
|
||||
|
||||
return [Infinity, true];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
/** @type {Array<RangeValue>} */
|
||||
this._left = [];
|
||||
/** @type {Array<RangeValue>} */
|
||||
|
||||
this._right = [];
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
|
||||
|
||||
left(value, exclusive = false) {
|
||||
this._left.push([value, exclusive]);
|
||||
}
|
||||
/**
|
||||
* @param {number} value
|
||||
* @param {boolean=} exclusive
|
||||
*/
|
||||
|
||||
|
||||
right(value, exclusive = false) {
|
||||
this._right.push([value, exclusive]);
|
||||
}
|
||||
/**
|
||||
* @param {boolean} logic is not logic applied
|
||||
* @return {string} "smart" range string representation
|
||||
*/
|
||||
|
||||
|
||||
format(logic = true) {
|
||||
const [start, leftExclusive] = Range.getRangeValue(this._left, logic);
|
||||
const [end, rightExclusive] = Range.getRangeValue(this._right, !logic);
|
||||
|
||||
if (!Number.isFinite(start) && !Number.isFinite(end)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const realStart = leftExclusive ? start + 1 : start;
|
||||
const realEnd = rightExclusive ? end - 1 : end; // e.g. 5 < x < 7, 5 < x <= 6, 6 <= x <= 6
|
||||
|
||||
if (realStart === realEnd) {
|
||||
return `should be ${logic ? '' : '!'}= ${realStart}`;
|
||||
} // e.g. 4 < x < ∞
|
||||
|
||||
|
||||
if (Number.isFinite(start) && !Number.isFinite(end)) {
|
||||
return Range.formatLeft(start, logic, leftExclusive);
|
||||
} // e.g. ∞ < x < 4
|
||||
|
||||
|
||||
if (!Number.isFinite(start) && Number.isFinite(end)) {
|
||||
return Range.formatRight(end, logic, rightExclusive);
|
||||
}
|
||||
|
||||
return Range.formatRange(start, end, leftExclusive, rightExclusive, logic);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Range;
|
||||
154
electron/node_modules/@develar/schema-utils/dist/validate.js
generated
vendored
Normal file
154
electron/node_modules/@develar/schema-utils/dist/validate.js
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _ajv = _interopRequireDefault(require("ajv"));
|
||||
|
||||
var _ajvKeywords = _interopRequireDefault(require("ajv-keywords"));
|
||||
|
||||
var _absolutePath = _interopRequireDefault(require("./keywords/absolutePath"));
|
||||
|
||||
var _ValidationError = _interopRequireDefault(require("./ValidationError"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/** @typedef {import("json-schema").JSONSchema4} JSONSchema4 */
|
||||
|
||||
/** @typedef {import("json-schema").JSONSchema6} JSONSchema6 */
|
||||
|
||||
/** @typedef {import("json-schema").JSONSchema7} JSONSchema7 */
|
||||
|
||||
/** @typedef {import("ajv").ErrorObject} ErrorObject */
|
||||
|
||||
/** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7)} Schema */
|
||||
|
||||
/** @typedef {ErrorObject & { children?: Array<ErrorObject>}} SchemaUtilErrorObject */
|
||||
|
||||
/**
|
||||
* @callback PostFormatter
|
||||
* @param {string} formattedError
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ValidationErrorConfiguration
|
||||
* @property {string=} name
|
||||
* @property {string=} baseDataPath
|
||||
* @property {PostFormatter=} postFormatter
|
||||
*/
|
||||
const ajv = new _ajv.default({
|
||||
allErrors: true,
|
||||
verbose: true,
|
||||
$data: true,
|
||||
coerceTypes: true
|
||||
});
|
||||
(0, _ajvKeywords.default)(ajv, ['instanceof', 'formatMinimum', 'formatMaximum', 'patternRequired']); // Custom keywords
|
||||
|
||||
(0, _absolutePath.default)(ajv);
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @param {ValidationErrorConfiguration=} configuration
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function validate(schema, options, configuration) {
|
||||
let errors = [];
|
||||
|
||||
if (Array.isArray(options)) {
|
||||
errors = Array.from(options).map(nestedOptions => validateObject(schema, nestedOptions));
|
||||
errors.forEach((list, idx) => {
|
||||
const applyPrefix =
|
||||
/**
|
||||
* @param {SchemaUtilErrorObject} error
|
||||
*/
|
||||
error => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
error.dataPath = `[${idx}]${error.dataPath}`;
|
||||
|
||||
if (error.children) {
|
||||
error.children.forEach(applyPrefix);
|
||||
}
|
||||
};
|
||||
|
||||
list.forEach(applyPrefix);
|
||||
});
|
||||
errors = errors.reduce((arr, items) => arr.concat(items), []);
|
||||
} else {
|
||||
errors = validateObject(schema, options);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new _ValidationError.default(errors, schema, configuration);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {Schema} schema
|
||||
* @param {Array<object> | object} options
|
||||
* @returns {Array<SchemaUtilErrorObject>}
|
||||
*/
|
||||
|
||||
|
||||
function validateObject(schema, options) {
|
||||
const compiledSchema = ajv.compile(schema);
|
||||
const valid = compiledSchema(options);
|
||||
|
||||
if (!compiledSchema.errors) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return valid ? [] : filterErrors(compiledSchema.errors);
|
||||
}
|
||||
/**
|
||||
* @param {Array<ErrorObject>} errors
|
||||
* @returns {Array<SchemaUtilErrorObject>}
|
||||
*/
|
||||
|
||||
|
||||
function filterErrors(errors) {
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
let newErrors = [];
|
||||
|
||||
for (const error of
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
errors) {
|
||||
const {
|
||||
dataPath
|
||||
} = error;
|
||||
/** @type {Array<SchemaUtilErrorObject>} */
|
||||
|
||||
let children = [];
|
||||
newErrors = newErrors.filter(oldError => {
|
||||
if (oldError.dataPath.includes(dataPath)) {
|
||||
if (oldError.children) {
|
||||
children = children.concat(oldError.children.slice(0));
|
||||
} // eslint-disable-next-line no-undefined, no-param-reassign
|
||||
|
||||
|
||||
oldError.children = undefined;
|
||||
children.push(oldError);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (children.length) {
|
||||
error.children = children;
|
||||
}
|
||||
|
||||
newErrors.push(error);
|
||||
}
|
||||
|
||||
return newErrors;
|
||||
} // TODO change after resolve https://github.com/microsoft/TypeScript/issues/34994
|
||||
|
||||
|
||||
validate.ValidationError = _ValidationError.default;
|
||||
validate.ValidateError = _ValidationError.default;
|
||||
var _default = validate;
|
||||
exports.default = _default;
|
||||
78
electron/node_modules/@develar/schema-utils/package.json
generated
vendored
Normal file
78
electron/node_modules/@develar/schema-utils/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name": "@develar/schema-utils",
|
||||
"version": "2.6.5",
|
||||
"description": "webpack Validation Utils",
|
||||
"license": "MIT",
|
||||
"repository": "webpack/schema-utils",
|
||||
"author": "webpack Contrib (https://github.com/webpack-contrib)",
|
||||
"homepage": "https://github.com/webpack/schema-utils",
|
||||
"bugs": "https://github.com/webpack/schema-utils/issues",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "declarations/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">= 8.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run build -- -w",
|
||||
"clean": "del-cli dist declarations",
|
||||
"prebuild": "npm run clean",
|
||||
"build:types": "tsc --declaration --emitDeclarationOnly --outDir declarations && prettier \"declarations/**/*.ts\" --write",
|
||||
"build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files",
|
||||
"build": "npm-run-all -p \"build:**\"",
|
||||
"commitlint": "commitlint --from=master",
|
||||
"security": "npm audit",
|
||||
"lint:prettier": "prettier \"{**/*,*}.{js,json,md,yml,css,ts}\" --list-different",
|
||||
"lint:js": "eslint --cache .",
|
||||
"lint:types": "tsc --pretty --noEmit",
|
||||
"lint": "npm-run-all -l -p \"lint:**\"",
|
||||
"test:only": "cross-env NODE_ENV=test jest",
|
||||
"test:watch": "npm run test:only -- --watch",
|
||||
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run test:coverage",
|
||||
"prepare": "npm run build",
|
||||
"release": "standard-version",
|
||||
"defaults": "webpack-defaults"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"declarations"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.0",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.3",
|
||||
"@babel/core": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.3",
|
||||
"@commitlint/cli": "^8.3.5",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"@webpack-contrib/defaults": "^6.3.0",
|
||||
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
|
||||
"babel-jest": "^24.9.0",
|
||||
"commitlint-azure-pipelines-cli": "^1.0.3",
|
||||
"cross-env": "^6.0.3",
|
||||
"del": "^5.1.0",
|
||||
"del-cli": "^3.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.9.0",
|
||||
"eslint-plugin-import": "^2.20.0",
|
||||
"husky": "^4.0.10",
|
||||
"jest": "^24.9.0",
|
||||
"jest-junit": "^10.0.0",
|
||||
"lint-staged": "^9.5.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^1.19.1",
|
||||
"standard-version": "^7.0.1",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"keywords": [
|
||||
"webpack"
|
||||
]
|
||||
}
|
||||
96
electron/node_modules/@electron-internal/extract-zip/README.md
generated
vendored
96
electron/node_modules/@electron-internal/extract-zip/README.md
generated
vendored
|
|
@ -1,96 +0,0 @@
|
|||
# @electron-internal/extract-zip
|
||||
|
||||
> [!WARNING]
|
||||
> **Internal to the Electron project.** This package exists to serve Electron's
|
||||
> own tooling. Use from non-Electron packages is not supported: the API may
|
||||
> change to suit Electron's needs, and bug reports or feature requests from
|
||||
> outside use cases may be closed without action. If you need a general-purpose
|
||||
> extractor, use [`extract-zip`](https://github.com/max-mapper/extract-zip).
|
||||
|
||||
Fast, safe, native zip extraction for Node.js. Drop-in replacement for [`extract-zip`](https://github.com/max-mapper/extract-zip).
|
||||
|
||||
- **Native**: Rust core via N-API, decompression runs off the event loop.
|
||||
- **Fast**: ~2x faster on entry-heavy archives, never slower. See [benchmarks](#benchmarks).
|
||||
- **Safe**: hardened against Zip Slip, symlink escapes, absolute paths, NUL injection, Windows reserved names, and zip bombs.
|
||||
- **Zero runtime deps**: no `debug`/`yauzl`/`get-stream` in your tree.
|
||||
- **Cross-platform**: prebuilt binaries for macOS, Linux (glibc/musl), and Windows (x64/arm64).
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
yarn add @electron-internal/extract-zip
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
ESM only:
|
||||
|
||||
```js
|
||||
import extract from '@electron-internal/extract-zip';
|
||||
|
||||
await extract('archive.zip', { dir: '/absolute/output/path' });
|
||||
```
|
||||
|
||||
`dir` (required, absolute) is the only option. Everything else is fixed: existing
|
||||
files are overwritten, archive mode bits (masked to `0o777`) and mtimes are
|
||||
preserved, symlinks are created (skipped on Windows without symlink privilege),
|
||||
and writes are parallelised across `min(cpus, 8)` workers. The original's
|
||||
`onEntry`, `defaultDirMode`, and `defaultFileMode` are not supported, since no
|
||||
consumer in the `electron` org uses them.
|
||||
|
||||
## Security
|
||||
|
||||
Every entry path is verified to land inside `dir`:
|
||||
|
||||
- `..` traversal is rejected and absolute paths are stripped, via the `zip` crate's audited `enclosed_name()`.
|
||||
- Directories are created one component at a time without following symlinks; an entry whose path crosses a symlink is rejected.
|
||||
- Symlinks are created after all files. Each target is walked against the on-disk tree and the archive's own symlink set, with relative-only hops bounded by `dir` and a hop cap, so a chain resolving outside `dir` is rejected before any link is created.
|
||||
- NUL bytes and Windows reserved device names (`CON`, `AUX`, `COM1`, trailing space/dot) are rejected on every platform.
|
||||
- Symlink targets are capped at 4 KiB; per-file output is capped at `max(2 x declared size, 1 MB)` to catch entries that lie about their size.
|
||||
|
||||
`test/security.test.js` exercises these escapes end-to-end with hand-crafted archives.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
`yarn bench` (Apple M-series, Node 24, median of 5):
|
||||
|
||||
| Corpus | Zip size | `extract-zip` (JS) | this | Speedup |
|
||||
|---|--:|--:|--:|--:|
|
||||
| electron-v42.2.0-darwin-arm64 | 112 MB | 817 ms | 441 ms | 1.9x |
|
||||
| 8 x 4 MB compressible | 0.1 MB | 24 ms | 3 ms | 9.4x |
|
||||
| 2000 small text files | 0.4 MB | 372 ms | 199 ms | 1.9x |
|
||||
| 200 incompressible files | 6.2 MB | 40 ms | 22 ms | 1.8x |
|
||||
| `node_modules` | 2.9 MB | 68 ms | 37 ms | 1.9x |
|
||||
|
||||
Extraction runs in phases: validate paths and create directories, inflate and
|
||||
write files in parallel with zlib-ng, then apply symlinks and directory
|
||||
metadata. The Electron number is gated by its single 182 MB framework binary,
|
||||
which can't be split further.
|
||||
|
||||
## Distribution
|
||||
|
||||
One package ships all prebuilt binaries (~2 MB gzipped): macOS
|
||||
`darwin-universal`, Windows `x64`/`arm64`, and Linux `x64`/`arm64` for both glibc
|
||||
and musl. `binding.js` picks the right one at load time. No
|
||||
`optionalDependencies`, no postinstall, no network at install.
|
||||
|
||||
## Building from source
|
||||
|
||||
Requires a Rust toolchain (and `cmake` for zlib-ng).
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
yarn build # builds index.<your-platform>.node
|
||||
yarn test
|
||||
```
|
||||
|
||||
## Releasing
|
||||
|
||||
Releases are driven by [semantic-release](https://semantic-release.gitbook.io/)
|
||||
on every push to `main`: conventional commit messages decide the version bump,
|
||||
CI builds all targets, and the fat package is published to npm via trusted
|
||||
publishing. No manual version bumps or tags.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue