(['linux', 'win32', 'android']);
-
- useEffect(() => {
- fetchUpdates().then(setKernels);
- }, []);
-
- const switchTarget = (value: string) => {
- let targetPlatforms: string[] = [];
- switch(value){
- case 'All':
- targetPlatforms = ['linux', 'win32', 'android'];
- break;
- case 'Windows':
- targetPlatforms = ['win32'];
- break;
- case 'Android':
- targetPlatforms = ['android'];
- break;
- case 'Linux':
- targetPlatforms = ['linux'];
- break;
- }
- setTargetPlatforms(targetPlatforms);
- }
-
- const translatePlatformToOsName = (platform: string) => {
- switch(platform){
- case 'linux':
- return 'Linux';
- case 'win32':
- return 'Windows';
- case 'android':
- return 'Android';
- default:
- return platform;
- }
- }
-
- const translatePlatformAndArchToCpuName = (platform : string, arch: string) => {
- switch(platform){
- case 'linux':
- return arch === 'arm64' ? 'ARM64' : 'x64';
- case 'win32':
- return arch === 'arm64' ? 'ARM64' : 'x64';
- case 'android':
- return 'universal';
- default:
- return arch;
- }
- }
-
-
- const features = kernels.filter((v) => targetPlatforms.includes(v.platform)).map((feature) => (
-
-
- {feature.platform === 'linux' && }
- {feature.platform === 'win32' && }
- {feature.platform === 'android' && }
-
- {translatePlatformToOsName(feature.platform)} - {translatePlatformAndArchToCpuName(feature.platform, feature.arch)}
-
-
- {feature.platform == 'linux' && <>This version is for Linux and comes as an AppImage for the GUI versions.>}
- {feature.platform == 'win32' && <>Download this version of you have computer on Windows 10 or later.>}
- {feature.platform == 'android' && <>This version is for Android devices and all architectures.>}
-
-
- }>Get {feature.version} now
-
-
- ));
-
- return (
-
-
-
-
- Download
-
-
-
-
-
- Choose your platform and get started with Rosetta today. Enjoy secure and fast messaging across all your devices.
-
-
-
-
-
-
-
- {features}
-
-
-
- );
-}
diff --git a/src/components/FeaturesGrid/FeaturesGrid.module.css b/src/components/FeaturesGrid/FeaturesGrid.module.css
deleted file mode 100644
index b6a681d..0000000
--- a/src/components/FeaturesGrid/FeaturesGrid.module.css
+++ /dev/null
@@ -1,29 +0,0 @@
-.wrapper {
- display: flex;
- justify-content: flex-start;
- flex-direction: column;
- background-color: #0066ff05;
- min-height: 100vh;
- padding-top: var(--mantine-spacing-xl);
- padding-bottom: var(--mantine-spacing-xl);
-}
-
-.title {
- font-family: Outfit, var(--mantine-font-family);
- font-weight: 500;
- margin-bottom: var(--mantine-spacing-md);
- text-align: center;
-
- @media (max-width: $mantine-breakpoint-sm) {
- font-size: 28px;
- text-align: left;
- }
-}
-
-.description {
- text-align: center;
-
- @media (max-width: $mantine-breakpoint-sm) {
- text-align: left;
- }
-}
\ No newline at end of file
diff --git a/src/components/FeaturesGrid/FeaturesGrid.tsx b/src/components/FeaturesGrid/FeaturesGrid.tsx
deleted file mode 100644
index df27858..0000000
--- a/src/components/FeaturesGrid/FeaturesGrid.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { IconGauge, IconLock, IconMessage2, IconServer, IconUser, IconUsersGroup, type TablerIcon } from '@tabler/icons-react';
-import { Container, Flex, SimpleGrid, Text, ThemeIcon, Title } from '@mantine/core';
-import type { ReactNode } from 'react';
-import classes from './FeaturesGrid.module.css';
-import { RosettaLogo } from '../RosettaLogo/RosettaLogo';
-
-const MOCKDATA = [
- {
- icon: IconGauge,
- title: 'Performance',
- description:
- 'Despite encryption, the messenger remains fast and convenient.',
- },
- {
- icon: IconUser,
- title: 'Privacy focused',
- description:
- 'We encrypt everything, even media files, so that no one can access your files.',
- },
- {
- icon: IconServer,
- title: 'Server does not save messages',
- description:
- 'The server does not store messages. All messages are stored only on your devices.',
- },
- {
- icon: IconLock,
- title: 'Secure by default',
- description:
- 'The messenger uses asymmetric encryption to hide message keys. ChaCha20 is used for message encryption, and AES-256 is used for encrypting your media files.',
- },
- {
- icon: IconMessage2,
- title: 'Regular Updates',
- description:
- 'We strive to update the messenger as often as possible to fix bugs and add new features. This is not an abandoned project.',
- },
- {
- icon: IconUsersGroup,
- title: 'No trackers or ads',
- description:
- 'Registration without phone number, email, or other personal data. Only public or private keys. We do not use trackers and do not show ads.',
- }
-];
-
-interface FeatureProps {
- icon: TablerIcon;
- title: ReactNode;
- description: ReactNode;
-}
-
-export function Feature({ icon: Icon, title, description }: FeatureProps) {
- return (
-
-
-
-
-
- {title}
-
-
- {description}
-
-
- );
-}
-
-export function FeaturesGrid() {
- const features = MOCKDATA.map((feature, index) => );
-
- return (
-
-
-
-
-
-
- Here’s what we offer
-
-
-
-
- Rosetta offers a wide range of features that ensure security, privacy, and ease of use. Explore the key features that make Rosetta an excellent choice for messaging.
-
-
-
-
- {features}
-
-
-
- );
-}
diff --git a/src/components/Footer/Footer.module.css b/src/components/Footer/Footer.module.css
deleted file mode 100644
index 69bf702..0000000
--- a/src/components/Footer/Footer.module.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.footer {
- padding: var(--mantine-spacing-xl) var(--mantine-spacing-md);
- border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
-}
-
-.legal {
- max-width: 720px;
- margin: 0 auto;
-}
-
-.links {
- display: flex;
- justify-content: center;
- gap: var(--mantine-spacing-md);
- flex-wrap: wrap;
- margin-top: var(--mantine-spacing-xs);
-}
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
deleted file mode 100644
index 1021c34..0000000
--- a/src/components/Footer/Footer.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Anchor, Text } from '@mantine/core';
-import { Link } from 'react-router-dom';
-import classes from './Footer.module.css';
-
-const LEGAL_NAME = 'ROSETTA CLOUD SERVICES LIMITED';
-
-export function Footer() {
- const currentYear = new Date().getFullYear();
-
- return (
-
-
- © {currentYear} {LEGAL_NAME}. All rights reserved.
-
-
-
- Support
-
-
- Privacy Policy
-
-
- Terms of Use
-
-
-
- );
-}
diff --git a/src/components/HeroTitle/HeroTitle.module.css b/src/components/HeroTitle/HeroTitle.module.css
deleted file mode 100644
index 96cad8e..0000000
--- a/src/components/HeroTitle/HeroTitle.module.css
+++ /dev/null
@@ -1,59 +0,0 @@
-.wrapper {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- box-sizing: border-box;
- background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
- height: 100vh;
-}
-
-.inner {
- position: relative;
-}
-
-.title {
- font-family: Outfit, var(--mantine-font-family);
- font-size: 62px;
- font-weight: 500;
- line-height: 1.1;
- margin: 0;
- word-wrap: normal;
- padding: 0;
- color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
-
- @media (max-width: $mantine-breakpoint-sm) {
- font-size: 42px;
- line-height: 1.2;
- }
-}
-
-.description {
- margin-top: var(--mantine-spacing-xl);
- font-size: 24px;
-
- @media (max-width: $mantine-breakpoint-sm) {
- font-size: 18px;
- }
-}
-
-.controls {
- margin-top: calc(var(--mantine-spacing-xl) * 2);
-
- @media (max-width: $mantine-breakpoint-sm) {
- margin-top: var(--mantine-spacing-xl);
- }
-}
-
-.control {
- height: 54px;
- padding-left: 38px;
- padding-right: 38px;
-
- @media (max-width: $mantine-breakpoint-sm) {
- height: 54px;
- padding-left: 18px;
- padding-right: 18px;
- flex: 1;
- }
-}
\ No newline at end of file
diff --git a/src/components/HeroTitle/HeroTitle.tsx b/src/components/HeroTitle/HeroTitle.tsx
deleted file mode 100644
index f587111..0000000
--- a/src/components/HeroTitle/HeroTitle.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { Container, Group, Text } from '@mantine/core';
-import classes from './HeroTitle.module.css';
-import { Download } from '../Download/Download';
-
-export function HeroTitle() {
- return (
-
-
-
- Rosetta is{' '}
-
- secure
- {' '}
- messaging for everyone
-
-
-
- Rosetta is designed to ensure secure messaging. We use comprehensive encryption, which keeps the app fast and convenient, yet secure.
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/LegalPage/LegalPage.module.css b/src/components/LegalPage/LegalPage.module.css
index 40d93ec..e864263 100644
--- a/src/components/LegalPage/LegalPage.module.css
+++ b/src/components/LegalPage/LegalPage.module.css
@@ -1,34 +1,17 @@
.page {
+ display: flex;
+ flex-direction: column;
min-height: 100vh;
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
}
.shell {
- padding-top: var(--mantine-spacing-md);
- padding-bottom: calc(var(--mantine-spacing-xl) * 2);
-}
-
-.header {
- min-height: 58px;
- padding-bottom: var(--mantine-spacing-md);
- border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5));
-}
-
-.brand {
- display: inline-flex;
- align-items: center;
- gap: var(--mantine-spacing-xs);
- color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white));
- text-decoration: none;
-}
-
-.brand img {
- border-radius: var(--mantine-radius-sm);
+ flex: 1;
}
.main {
width: 100%;
- padding-top: 96px;
+ padding: 96px 0 112px;
}
.intro {
@@ -46,10 +29,6 @@
line-height: 1.08;
}
-.document {
- padding-bottom: 72px;
-}
-
.lead {
max-width: 780px;
margin: 0;
@@ -124,14 +103,9 @@
white-space: pre-line;
}
-.footer {
- padding-top: var(--mantine-spacing-lg);
- border-top: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5));
-}
-
@media (max-width: 48em) {
.main {
- padding-top: 72px;
+ padding: 72px 0 88px;
}
.intro {
@@ -147,17 +121,7 @@
}
@media (max-width: 36em) {
- .shell {
- padding-inline: var(--mantine-spacing-md);
- }
-
- .header {
- align-items: flex-start;
- flex-direction: column;
- }
-
- .footer {
- align-items: flex-start;
- flex-direction: column;
+ .main {
+ padding-top: 56px;
}
}
diff --git a/src/components/LegalPage/LegalPage.tsx b/src/components/LegalPage/LegalPage.tsx
index 599b0ef..2b7da17 100644
--- a/src/components/LegalPage/LegalPage.tsx
+++ b/src/components/LegalPage/LegalPage.tsx
@@ -1,22 +1,17 @@
import type { ReactNode } from 'react';
import {
- Anchor,
Box,
Container,
- Group,
List,
Paper,
Text,
Title,
} from '@mantine/core';
-import { Link } from 'react-router-dom';
-import { RosettaLogo } from '../RosettaLogo/RosettaLogo';
+import { SiteFooter } from '../SiteFooter/SiteFooter';
+import { SiteHeader } from '../SiteHeader/SiteHeader';
import classes from './LegalPage.module.css';
-type ContentRoute = 'support' | 'privacy' | 'terms';
-
interface LegalPageProps {
- active: ContentRoute;
title: string;
updated?: string;
eyebrow?: string;
@@ -28,54 +23,16 @@ interface LegalSectionProps {
children: ReactNode;
}
-const NAV_LINKS = [
- { route: 'support', label: 'Support' },
- { route: 'privacy', label: 'Privacy' },
- { route: 'terms', label: 'Terms' },
-] as const;
-
export function LegalPage({
- active,
title,
updated,
eyebrow = 'Rosetta Legal',
children,
}: LegalPageProps) {
- const currentYear = new Date().getFullYear();
-
return (
+
-
-
-
-
- Rosetta
-
-
-
-
- {NAV_LINKS.map(({ route, label }) => (
-
- {label}
-
- ))}
-
-
-
@@ -95,27 +52,8 @@ export function LegalPage({
{children}
-
-
-
- © {currentYear} ROSETTA CLOUD SERVICES LIMITED
-
-
-
- Rosetta
-
-
- Support
-
-
- Privacy Policy
-
-
- Terms of Use
-
-
-
+
);
}
diff --git a/src/components/MessageSteps/MessageSteps.module.css b/src/components/MessageSteps/MessageSteps.module.css
deleted file mode 100644
index 27e46fe..0000000
--- a/src/components/MessageSteps/MessageSteps.module.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.wrapper {
- display: flex;
- justify-content: flex-start;
- flex-direction: column;
- min-height: 100vh;
- padding-top: var(--mantine-spacing-xl);
- padding-bottom: var(--mantine-spacing-xl);
-}
-
-.title {
- font-family: Outfit, var(--mantine-font-family);
- font-weight: 500;
- margin-bottom: var(--mantine-spacing-md);
- text-align: center;
-
- @media (max-width: $mantine-breakpoint-sm) {
- font-size: 28px;
- text-align: left;
- }
-}
-
-.description {
- text-align: center;
-
- @media (max-width: $mantine-breakpoint-sm) {
- text-align: left;
- }
-}
\ No newline at end of file
diff --git a/src/components/MessageSteps/MessageSteps.tsx b/src/components/MessageSteps/MessageSteps.tsx
deleted file mode 100644
index 6382af5..0000000
--- a/src/components/MessageSteps/MessageSteps.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Container, Flex, Text, Title } from '@mantine/core';
-import classes from './MessageSteps.module.css';
-import { RosettaLogo } from '../RosettaLogo/RosettaLogo';
-import { Switch } from '../Switch/Switch';
-import { useState } from 'react';
-import { Sender } from '../Sender/Sender';
-import { Recipient } from '../Recipient/Recipient';
-
-
-export function MessageSteps() {
- const [show, setShow] = useState('Sender');
-
- return (
-
-
-
-
-
-
- How is my message sent?
-
-
-
-
- See why Rosetta is truly secure. Notice how your message is sent and received from you to the recipient. The steps highlighted in blue are what you see, while the steps not highlighted are what happens behind the scenes.
-
-
-
-
- setShow(v)}>
-
-
-
-
- {show == 'Sender' && }
- {show == 'Recipient' && }
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/Recipient/Recipient.tsx b/src/components/Recipient/Recipient.tsx
deleted file mode 100644
index 659100c..0000000
--- a/src/components/Recipient/Recipient.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { Text, Timeline } from "@mantine/core";
-import { IconDeviceDesktop, IconKey, IconLockAccess, IconMessage, IconMessage2, IconServer } from "@tabler/icons-react";
-
-export function Recipient() {
- return (
-
- } title="Retranslate">
-
- Server retranslate message to recipient.
-
- After retranslate server deletes message from memory.
-
- } title="Receive message">
-
- Recipient device receives encrypted message and encrypted ChaCha20 key from server.
-
- Message now encrypted.
-
- } title="Decrypting ChaCha20 key">
-
- Decrypt ChaCha20 key with recipient private key using RSA-4096 decryption.
-
- Message still encrypted.
-
- } title="Decrypting message using ChaCha20">
-
- Decrypting text of message or attachments using decrypted ChaCha20 key from previous step.
-
- Message text is available.
-
- } title="Save message on your device">
-
- Encrypt decrypted text with AES-256 for save in your device.
-
- Need for future access, because message not save on server.
-
- } title="Show message">
-
- Message available for reading in your chat.
-
- Final step.
-
-
- )
-}
\ No newline at end of file
diff --git a/src/components/SEO/SEO.tsx b/src/components/SEO/SEO.tsx
index 52c1c65..d9a12e0 100644
--- a/src/components/SEO/SEO.tsx
+++ b/src/components/SEO/SEO.tsx
@@ -8,9 +8,9 @@ interface SEOProps {
}
export function SEO({
- title = 'Rosetta - Secure Messaging for Everyone',
- description = 'End-to-end encrypted messaging app with military-grade security. Download for Windows, Linux, and Android.',
- keywords = 'secure messaging, encrypted chat, private messenger',
+ title = 'Rosetta — Private Messaging',
+ description = 'Private messaging built around strong encryption, local message history, and user-controlled identity.',
+ keywords = 'Rosetta, encrypted messaging, private messenger, secure chat',
canonical = 'https://rosetta.im/'
}: SEOProps) {
useEffect(() => {
diff --git a/src/components/ScrollToTop/ScrollToTop.tsx b/src/components/ScrollToTop/ScrollToTop.tsx
index e895b96..8192001 100644
--- a/src/components/ScrollToTop/ScrollToTop.tsx
+++ b/src/components/ScrollToTop/ScrollToTop.tsx
@@ -2,11 +2,23 @@ import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export function ScrollToTop() {
- const { pathname } = useLocation();
+ const { pathname, hash } = useLocation();
useEffect(() => {
- window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
- }, [pathname]);
+ const frame = window.requestAnimationFrame(() => {
+ if (hash) {
+ const target = document.getElementById(decodeURIComponent(hash.slice(1)));
+ if (target) {
+ target.scrollIntoView();
+ return;
+ }
+ }
+
+ window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
+ });
+
+ return () => window.cancelAnimationFrame(frame);
+ }, [pathname, hash]);
return null;
}
diff --git a/src/components/Sender/Sender.tsx b/src/components/Sender/Sender.tsx
deleted file mode 100644
index f7b864d..0000000
--- a/src/components/Sender/Sender.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Text, Timeline } from "@mantine/core";
-import { IconDeviceDesktop, IconKering, IconKey, IconMessage, IconServer } from "@tabler/icons-react";
-
-export function Sender() {
- return (
-
- } title="Enter message">
-
- You enter message text in chat.
-
-
- } title="Encrypt for you">
-
- Message encrypted for your device using AES-256 encryption.
-
- Need for safety save message on your device.
-
- } title="Encrypt for opponent">
-
- Encrypt message for recipient using ChaCha20 encryption.
-
- Encrypt message with chacha20 with key.
-
- } title="Encrypt ChaCha20 key to send">
-
- Encrypt key of previous step with recipient public key using RSA-4096 encryption.
-
- Needs for send encrypted key to opponent.
-
- } title="Send encrypted data to server">
-
- Send encrypted message and encrypted ChaCha20 key to server for delivery to recipient.
-
- See next steps in recipient timeline.
-
-
- )
-}
\ No newline at end of file
diff --git a/src/components/SiteFooter/SiteFooter.module.css b/src/components/SiteFooter/SiteFooter.module.css
new file mode 100644
index 0000000..4391ba9
--- /dev/null
+++ b/src/components/SiteFooter/SiteFooter.module.css
@@ -0,0 +1,16 @@
+.footer {
+ padding: 72px 0 32px;
+ border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6));
+ background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9));
+}
+
+.footer a {
+ width: fit-content;
+ text-decoration: none;
+}
+
+.copyright {
+ margin-top: 56px;
+ padding-top: var(--mantine-spacing-lg);
+ border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6));
+}
diff --git a/src/components/SiteFooter/SiteFooter.tsx b/src/components/SiteFooter/SiteFooter.tsx
new file mode 100644
index 0000000..b5f4ebe
--- /dev/null
+++ b/src/components/SiteFooter/SiteFooter.tsx
@@ -0,0 +1,66 @@
+import { Anchor, Container, Group, SimpleGrid, Stack, Text } from '@mantine/core';
+import { Link } from 'react-router-dom';
+import { RosettaLogo } from '../RosettaLogo/RosettaLogo';
+import classes from './SiteFooter.module.css';
+
+export function SiteFooter() {
+ const year = new Date().getFullYear();
+
+ return (
+
+
+
+
+
+
+
+ Rosetta
+
+
+
+ Private messaging built around strong encryption, local history, and
+ user-controlled identity.
+
+
+
+
+
+ Product
+
+
+ Features
+
+
+ Apps
+
+
+ Proto
+
+
+ Support
+
+
+
+
+
+ Legal
+
+
+ Privacy Policy
+
+
+ Terms of Use
+
+
+ Admin@rosetta.im
+
+
+
+
+
+ © {year} ROSETTA CLOUD SERVICES LIMITED
+
+
+
+ );
+}
diff --git a/src/components/SiteHeader/SiteHeader.module.css b/src/components/SiteHeader/SiteHeader.module.css
new file mode 100644
index 0000000..47a7e03
--- /dev/null
+++ b/src/components/SiteHeader/SiteHeader.module.css
@@ -0,0 +1,58 @@
+.header {
+ position: sticky;
+ z-index: 50;
+ top: 0;
+ border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6));
+ background: light-dark(rgba(255, 255, 255, 0.92), rgba(26, 27, 30, 0.92));
+ backdrop-filter: blur(16px);
+}
+
+.inner {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 72px;
+}
+
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white));
+ text-decoration: none;
+}
+
+.brand img {
+ border-radius: 9px;
+}
+
+.link {
+ color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-1));
+ font-size: var(--mantine-font-size-sm);
+ font-weight: 500;
+ text-decoration: none;
+ transition: color 120ms ease;
+}
+
+.link:hover,
+.link[data-active] {
+ color: var(--mantine-color-blue-6);
+}
+
+.burger {
+ display: none;
+}
+
+.drawerTitle {
+ font-weight: 700;
+}
+
+@media (max-width: 48em) {
+ .navigation {
+ display: none;
+ }
+
+ .burger {
+ display: block;
+ }
+}
diff --git a/src/components/SiteHeader/SiteHeader.tsx b/src/components/SiteHeader/SiteHeader.tsx
new file mode 100644
index 0000000..e7a0c52
--- /dev/null
+++ b/src/components/SiteHeader/SiteHeader.tsx
@@ -0,0 +1,80 @@
+import {
+ Anchor,
+ Burger,
+ Container,
+ Drawer,
+ Group,
+ Stack,
+ Text,
+} from '@mantine/core';
+import { useDisclosure } from '@mantine/hooks';
+import { Link, useLocation } from 'react-router-dom';
+import { RosettaLogo } from '../RosettaLogo/RosettaLogo';
+import classes from './SiteHeader.module.css';
+
+const NAVIGATION = [
+ { label: 'Features', to: '/#features' },
+ { label: 'Apps', to: '/#apps' },
+ { label: 'Proto', to: '/proto' },
+ { label: 'Support', to: '/support' },
+];
+
+export function SiteHeader() {
+ const [opened, { close, toggle }] = useDisclosure(false);
+ const { pathname } = useLocation();
+
+ const links = NAVIGATION.map(({ label, to }) => {
+ const active =
+ (to === '/proto' && pathname === '/proto') ||
+ (to === '/support' && pathname === '/support');
+
+ return (
+
+ {label}
+
+ );
+ });
+
+ return (
+
+
+
+
+
+ Rosetta
+
+
+
+
+ {links}
+
+
+
+
+
+
+ {links}
+
+
+ );
+}
diff --git a/src/components/Switch/Switch.module.css b/src/components/Switch/Switch.module.css
deleted file mode 100644
index 12abfca..0000000
--- a/src/components/Switch/Switch.module.css
+++ /dev/null
@@ -1,37 +0,0 @@
-.root {
- position: relative;
- background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
- width: fit-content;
- border-radius: var(--mantine-radius-md);
- padding: 5px;
- border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4));
-}
-
-.control {
- padding: 7px 12px;
- line-height: 1;
- color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-2));
- border-radius: var(--mantine-radius-md);
- font-size: var(--mantine-font-size-sm);
- transition: color 100ms ease;
- font-weight: 500;
-
- @mixin hover {
- color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
- background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-7));
- }
-
- &[data-active] {
- color: var(--mantine-color-white);
- }
-}
-
-.controlLabel {
- position: relative;
- z-index: 1;
-}
-
-.indicator {
- background-color: var(--mantine-primary-color-filled);
- border-radius: var(--mantine-radius-md);
-}
\ No newline at end of file
diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx
deleted file mode 100644
index 8b2c909..0000000
--- a/src/components/Switch/Switch.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useState } from 'react';
-import { FloatingIndicator, UnstyledButton } from '@mantine/core';
-import classes from './Switch.module.css';
-
-interface SwitchProps {
- data: string[];
- onChange?: (value: string) => void;
-}
-
-export function Switch(props: SwitchProps) {
- const [rootRef, setRootRef] = useState(null);
- const [controlsRefs, setControlsRefs] = useState>({});
- const [active, setActive] = useState(0);
-
- const setControlRef = (index: number) => (node: HTMLButtonElement) => {
- controlsRefs[index] = node;
- setControlsRefs(controlsRefs);
- };
-
- const changeActive = (index: number) => {
- setActive(index);
- if(!props.onChange){
- return;
- }
- props.onChange(props.data[index]);
- }
-
- const controls = props.data.map((item, index) => (
- changeActive(index)}
- mod={{ active: active === index }}
- >
- {item}
-
- ));
-
- return (
-
- {controls}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/generated/protoDocs.ts b/src/generated/protoDocs.ts
new file mode 100644
index 0000000..3991dc3
--- /dev/null
+++ b/src/generated/protoDocs.ts
@@ -0,0 +1,3707 @@
+/* This file is generated by scripts/generate-proto-docs.mjs. */
+import type { ProtoLayer } from '../types/proto';
+
+export const protoLayers = [
+ {
+ "version": 1,
+ "description": "",
+ "source": "layer.1.yml",
+ "enums": [
+ {
+ "name": "AttachmentType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "IMAGE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "MESSAGES",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "FILE",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "AVATAR",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "VOICE",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "VIDEO_CIRCLE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceSolution",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "DECLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "HandshakeStage",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "COMPLETED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "NEED_DEVICE_VERIFICATION",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "JOINED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "INVALID",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "NOT_JOINED",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "BANNED",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NotificationAction",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUBSCRIBE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "UNSUBSCRIBE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "KEY_EXCHANGE",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ACTIVE_CALL",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "END_CALL",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "ACTIVE",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "END_CALL_BECAUSE_PEER_DISCONNECTED",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "END_CALL_BECAUSE_BUSY",
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "RINGING_TIMEOUT",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NetworkStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ONLINE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "OFFLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SyncStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "NOT_NEEDED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "BATCH_START",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "BATCH_END",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTCType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "OFFER",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ANSWER",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ICE_CANDIDATE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "ResultCode",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUCCESS",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ERROR",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "INVALID",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "USERNAME_TAKEN",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "TokenType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "FCM",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "VoIPApns",
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "types": [
+ {
+ "name": "Attachment",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "id",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "blob",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "type",
+ "type": "AttachmentType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "preview",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "transport",
+ "type": "AttachmentTransport",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "AttachmentTransport",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "transportTag",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "transportServer",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NetworkDevice",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceSolution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "PKNetworkStatus",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SearchInfo",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "verified",
+ "type": "int32",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RTCIceServer",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "url",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "credential",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "transport",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "packets": [
+ {
+ "name": "Handshake",
+ "id": 0,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "handshakeStage",
+ "type": "HandshakeStage",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "UserInfo",
+ "id": 1,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Result",
+ "id": 2,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "resultCode",
+ "type": "ResultCode",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Search",
+ "id": 3,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "search",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "searchInfo",
+ "type": "SearchInfo[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineSubscribe",
+ "id": 4,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKeys",
+ "type": "string[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineState",
+ "id": 5,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "pkNetworkStatuses",
+ "type": "PKNetworkStatus[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Message",
+ "id": 6,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "content",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "chachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "attachments",
+ "type": "Attachment[]",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 9,
+ "name": "aesChachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Read",
+ "id": 7,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Delivery",
+ "id": 8,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceNew",
+ "id": 9,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "ipAddress",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestUpdate",
+ "id": 10,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Typeing",
+ "id": 11,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestTransport",
+ "id": 15,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "PushNotification",
+ "id": 16,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "notificationToken",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "action",
+ "type": "NotificationAction",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "tokenType",
+ "type": "TokenType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupCreate",
+ "id": 17,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInfo",
+ "id": 18,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersPKs",
+ "type": "string[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInviteInfo",
+ "id": 19,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersCount",
+ "type": "int32",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupJoin",
+ "id": 20,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "groupString",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupLeave",
+ "id": 21,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupBan",
+ "id": 22,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceList",
+ "id": 23,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "devices",
+ "type": "NetworkDevice[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceResolve",
+ "id": 24,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "solution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Sync",
+ "id": 25,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "syncStatus",
+ "type": "SyncStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalPeer",
+ "id": 26,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "src",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "dst",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "sharedPublic",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "signalType",
+ "type": "SignalType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "callId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "joinToken",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTC",
+ "id": 27,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "sdpOrCandidate",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "type",
+ "type": "WebRTCType",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "IceServers",
+ "id": 28,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "iceServers",
+ "type": "RTCIceServer[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "version": 2,
+ "description": "Remove avatars as attachments, add video",
+ "source": "layer.2.yml",
+ "enums": [
+ {
+ "name": "AttachmentType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "IMAGE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "MESSAGES",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "FILE",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "VOICE",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "VIDEO_CIRCLE",
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "VIDEO",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceSolution",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "DECLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "HandshakeStage",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "COMPLETED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "NEED_DEVICE_VERIFICATION",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "JOINED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "INVALID",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "NOT_JOINED",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "BANNED",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NotificationAction",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUBSCRIBE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "UNSUBSCRIBE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "KEY_EXCHANGE",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ACTIVE_CALL",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "END_CALL",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "ACTIVE",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "END_CALL_BECAUSE_PEER_DISCONNECTED",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "END_CALL_BECAUSE_BUSY",
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "RINGING_TIMEOUT",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NetworkStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ONLINE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "OFFLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SyncStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "NOT_NEEDED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "BATCH_START",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "BATCH_END",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTCType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "OFFER",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ANSWER",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ICE_CANDIDATE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "ResultCode",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUCCESS",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ERROR",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "INVALID",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "USERNAME_TAKEN",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "TokenType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "FCM",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "VoIPApns",
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "types": [
+ {
+ "name": "Attachment",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "id",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "blob",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "type",
+ "type": "AttachmentType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "preview",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "transport",
+ "type": "AttachmentTransport",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "AttachmentTransport",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "transportTag",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "transportServer",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NetworkDevice",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceSolution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "PKNetworkStatus",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SearchInfo",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "verified",
+ "type": "int32",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RTCIceServer",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "url",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "credential",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "transport",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "packets": [
+ {
+ "name": "Handshake",
+ "id": 0,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "handshakeStage",
+ "type": "HandshakeStage",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "UserInfo",
+ "id": 1,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Result",
+ "id": 2,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "resultCode",
+ "type": "ResultCode",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Search",
+ "id": 3,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "search",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "searchInfo",
+ "type": "SearchInfo[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineSubscribe",
+ "id": 4,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKeys",
+ "type": "string[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineState",
+ "id": 5,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "pkNetworkStatuses",
+ "type": "PKNetworkStatus[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Message",
+ "id": 6,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "content",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "chachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "attachments",
+ "type": "Attachment[]",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 9,
+ "name": "aesChachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Read",
+ "id": 7,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Delivery",
+ "id": 8,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceNew",
+ "id": 9,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "ipAddress",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestUpdate",
+ "id": 10,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Typeing",
+ "id": 11,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestTransport",
+ "id": 15,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "PushNotification",
+ "id": 16,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "notificationToken",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "action",
+ "type": "NotificationAction",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "tokenType",
+ "type": "TokenType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupCreate",
+ "id": 17,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInfo",
+ "id": 18,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersPKs",
+ "type": "string[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInviteInfo",
+ "id": 19,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersCount",
+ "type": "int32",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupJoin",
+ "id": 20,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "groupString",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupLeave",
+ "id": 21,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupBan",
+ "id": 22,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceList",
+ "id": 23,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "devices",
+ "type": "NetworkDevice[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceResolve",
+ "id": 24,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "solution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Sync",
+ "id": 25,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "syncStatus",
+ "type": "SyncStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalPeer",
+ "id": 26,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "src",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "dst",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "sharedPublic",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "signalType",
+ "type": "SignalType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "callId",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "joinToken",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTC",
+ "id": 27,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "sdpOrCandidate",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "type",
+ "type": "WebRTCType",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "IceServers",
+ "id": 28,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "iceServers",
+ "type": "RTCIceServer[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "version": 3,
+ "description": "Add avatars and more CDN safety, remove transport server",
+ "source": "layer.3.yml",
+ "enums": [
+ {
+ "name": "AttachmentType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "IMAGE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "MESSAGES",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "FILE",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "VOICE",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "VIDEO_CIRCLE",
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "VIDEO",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceSolution",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "DECLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "HandshakeStage",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "COMPLETED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "NEED_DEVICE_VERIFICATION",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "JOINED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "INVALID",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "NOT_JOINED",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "BANNED",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NotificationAction",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUBSCRIBE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "UNSUBSCRIBE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "CALL",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "KEY_EXCHANGE",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ACTIVE_CALL",
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "END_CALL",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "ACTIVE",
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "END_CALL_BECAUSE_PEER_DISCONNECTED",
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "END_CALL_BECAUSE_BUSY",
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "ACCEPT",
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "RINGING_TIMEOUT",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "NetworkStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "ONLINE",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "OFFLINE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SyncStatus",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "NOT_NEEDED",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "BATCH_START",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "BATCH_END",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTCType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "OFFER",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ANSWER",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "ICE_CANDIDATE",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "ResultCode",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "SUCCESS",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "ERROR",
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "INVALID",
+ "description": "любая ошибка смысл которой зависит от контекста, например в случае с файловой системой - нет файла"
+ },
+ {
+ "id": 3,
+ "name": "USERNAME_TAKEN",
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "OVERSIZE",
+ "description": "слишком большой размер файла"
+ },
+ {
+ "id": 5,
+ "name": "OVERLIFE",
+ "description": "слишком большой TTL"
+ }
+ ]
+ },
+ {
+ "name": "TokenType",
+ "type": "int8",
+ "description": "",
+ "values": [
+ {
+ "id": 0,
+ "name": "FCM",
+ "description": ""
+ },
+ {
+ "id": 1,
+ "name": "VoIPApns",
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "ProfileMediaType",
+ "type": "uint8",
+ "description": "Тип медиа файла профиля (пока только фото)",
+ "values": [
+ {
+ "id": 0,
+ "name": "PROFILE_PHOTO",
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "types": [
+ {
+ "name": "Attachment",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "id",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "blob",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "type",
+ "type": "AttachmentType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "preview",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "objectId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "ProfileMedia",
+ "description": "Медиа-файлы профиля такие как аватарки (фото) и будущие истории",
+ "fields": [
+ {
+ "id": 1,
+ "name": "preview",
+ "type": "string",
+ "optional": false,
+ "description": "превью (для фото blurhash)"
+ },
+ {
+ "id": 2,
+ "name": "objectId",
+ "type": "string",
+ "optional": false,
+ "description": "обьект на cdn"
+ },
+ {
+ "id": 3,
+ "name": "type",
+ "type": "ProfileMediaType",
+ "optional": false,
+ "description": "тип медиа"
+ },
+ {
+ "id": 4,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": "дата публикации"
+ },
+ {
+ "id": 5,
+ "name": "mediaKey",
+ "type": "string",
+ "optional": false,
+ "description": "ключ шифрования от медиа"
+ }
+ ]
+ },
+ {
+ "name": "NetworkDevice",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceSolution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "PKNetworkStatus",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SearchInfo",
+ "description": "Информация при поиске пользователя (приходит в ответ на пакет PacketSearch)",
+ "fields": [
+ {
+ "id": 1,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": "имя для поиска"
+ },
+ {
+ "id": 2,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": "отображаемое имя пользователя"
+ },
+ {
+ "id": 3,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": "публичный ключ"
+ },
+ {
+ "id": 4,
+ "name": "verified",
+ "type": "int32",
+ "optional": false,
+ "description": "верификация"
+ },
+ {
+ "id": 5,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": "онлайн пользователь или нет"
+ },
+ {
+ "id": 6,
+ "name": "avatar",
+ "type": "ProfileMedia",
+ "optional": true,
+ "description": "аватар пользователя если есть"
+ }
+ ]
+ },
+ {
+ "name": "ProfileInfo",
+ "description": "Информация которая используется только при входе в профиль пользователя",
+ "fields": [
+ {
+ "id": 1,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": "имя для поиска"
+ },
+ {
+ "id": 2,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": "отображаемое имя пользователя"
+ },
+ {
+ "id": 3,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": "публичный ключ"
+ },
+ {
+ "id": 4,
+ "name": "verified",
+ "type": "int32",
+ "optional": false,
+ "description": "верификация"
+ },
+ {
+ "id": 5,
+ "name": "networkStatus",
+ "type": "NetworkStatus",
+ "optional": false,
+ "description": "онлайн пользователь или нет"
+ },
+ {
+ "id": 6,
+ "name": "media",
+ "type": "ProfileMedia[]",
+ "optional": true,
+ "description": "все активное медиа (включая будущие истории)"
+ }
+ ]
+ },
+ {
+ "name": "RTCIceServer",
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "url",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "credential",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "transport",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ }
+ ],
+ "packets": [
+ {
+ "name": "Handshake",
+ "id": 0,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "handshakeStage",
+ "type": "HandshakeStage",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "UserInfo",
+ "id": 1,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "username",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "title",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Result",
+ "id": 2,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "resultCode",
+ "type": "ResultCode",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Search",
+ "id": 3,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "search",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "searchInfo",
+ "type": "SearchInfo[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineSubscribe",
+ "id": 4,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKeys",
+ "type": "string[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "OnlineState",
+ "id": 5,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "pkNetworkStatuses",
+ "type": "PKNetworkStatus[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Message",
+ "id": 6,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "content",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "chachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 7,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 8,
+ "name": "attachments",
+ "type": "Attachment[]",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 9,
+ "name": "aesChachaKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Read",
+ "id": 7,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Delivery",
+ "id": 8,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "messageId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceNew",
+ "id": 9,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "ipAddress",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "deviceName",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceOs",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestUpdate",
+ "id": 10,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Typeing",
+ "id": 11,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "fromPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "toPublicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "privateKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "RequestTransport",
+ "id": 15,
+ "description": "Запрос транспортного сервера",
+ "fields": [
+ {
+ "id": 1,
+ "name": "server",
+ "type": "string",
+ "optional": true,
+ "description": "выбранный транспортный сервер"
+ }
+ ]
+ },
+ {
+ "name": "PushNotification",
+ "id": 16,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "notificationToken",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "action",
+ "type": "NotificationAction",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "tokenType",
+ "type": "TokenType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupCreate",
+ "id": 17,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInfo",
+ "id": 18,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersPKs",
+ "type": "string[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupInviteInfo",
+ "id": 19,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "membersCount",
+ "type": "int32",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupJoin",
+ "id": 20,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "status",
+ "type": "GroupStatus",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "groupString",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupLeave",
+ "id": 21,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "GroupBan",
+ "id": 22,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "groupId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceList",
+ "id": 23,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "devices",
+ "type": "NetworkDevice[]",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "DeviceResolve",
+ "id": 24,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "deviceId",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "solution",
+ "type": "DeviceSolution",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "Sync",
+ "id": 25,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "syncStatus",
+ "type": "SyncStatus",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "timestamp",
+ "type": "int64",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "SignalPeer",
+ "id": 26,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "src",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "dst",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 3,
+ "name": "sharedPublic",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 4,
+ "name": "signalType",
+ "type": "SignalType",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 5,
+ "name": "callId",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ },
+ {
+ "id": 6,
+ "name": "joinToken",
+ "type": "string",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "WebRTC",
+ "id": 27,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "sdpOrCandidate",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "type",
+ "type": "WebRTCType",
+ "optional": false,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "IceServers",
+ "id": 28,
+ "description": "",
+ "fields": [
+ {
+ "id": 1,
+ "name": "iceServers",
+ "type": "RTCIceServer[]",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ },
+ {
+ "name": "UploadCandidate",
+ "id": 29,
+ "description": "Запрос \"посадочного места\" у сервера при загрузке файла",
+ "fields": [
+ {
+ "id": 1,
+ "name": "size",
+ "type": "uint32",
+ "optional": false,
+ "description": "размер загружаемого файла (декларируемый)"
+ },
+ {
+ "id": 2,
+ "name": "objectId",
+ "type": "string",
+ "optional": true,
+ "description": "выделенный сервером objectId"
+ },
+ {
+ "id": 3,
+ "name": "chunkLength",
+ "type": "uint32",
+ "optional": true,
+ "description": "размер чанка"
+ },
+ {
+ "id": 4,
+ "name": "chunks",
+ "type": "uint32",
+ "optional": true,
+ "description": "количество чанков"
+ },
+ {
+ "id": 5,
+ "name": "host",
+ "type": "string",
+ "optional": true,
+ "description": "хост, где сохранен файл"
+ },
+ {
+ "id": 6,
+ "name": "ttl",
+ "type": "uint32",
+ "optional": true,
+ "description": "ttl в секундах, после которого файл будет удален с CDN и с таблицы, по умолчанию 7 дней, не допускается использование слишком больших значений"
+ }
+ ]
+ },
+ {
+ "name": "DownloadCandidate",
+ "id": 30,
+ "description": "Запрос скачивания файла с сервера, нужно передать objectId, а дальнейшую информацию вернет сервер",
+ "fields": [
+ {
+ "id": 1,
+ "name": "size",
+ "type": "uint32",
+ "optional": true,
+ "description": "размер файла"
+ },
+ {
+ "id": 2,
+ "name": "objectId",
+ "type": "string",
+ "optional": false,
+ "description": "выделенный сервером objectId"
+ },
+ {
+ "id": 3,
+ "name": "chunkLength",
+ "type": "uint32",
+ "optional": true,
+ "description": "размер чанка"
+ },
+ {
+ "id": 4,
+ "name": "chunks",
+ "type": "uint32",
+ "optional": true,
+ "description": "количество чанков"
+ },
+ {
+ "id": 5,
+ "name": "host",
+ "type": "string",
+ "optional": true,
+ "description": "сервер с которого скачивать файл"
+ }
+ ]
+ },
+ {
+ "name": "SetAvatar",
+ "id": 31,
+ "description": "Установить аватар профиля",
+ "fields": [
+ {
+ "id": 1,
+ "name": "objectId",
+ "type": "string",
+ "optional": false,
+ "description": "выделенный сервером objectId при отправке пакета UploadCandidate"
+ },
+ {
+ "id": 2,
+ "name": "blurhash",
+ "type": "string",
+ "optional": false,
+ "description": "превью аватара, строка blurhash"
+ },
+ {
+ "id": 3,
+ "name": "mediaKey",
+ "type": "string",
+ "optional": false,
+ "description": "ключ шифрования"
+ }
+ ]
+ },
+ {
+ "name": "RemoveAvatar",
+ "id": 32,
+ "description": "Удаляет аватар или фотографию из профиля",
+ "fields": [
+ {
+ "id": 1,
+ "name": "objectId",
+ "type": "string",
+ "optional": false,
+ "description": "objectId аватара, аватар или фотография профиля должна при этом стоять у профиля отправителя пакета"
+ }
+ ]
+ },
+ {
+ "name": "Profile",
+ "id": 33,
+ "description": "Запрос профиля",
+ "fields": [
+ {
+ "id": 1,
+ "name": "publicKey",
+ "type": "string",
+ "optional": false,
+ "description": ""
+ },
+ {
+ "id": 2,
+ "name": "info",
+ "type": "ProfileInfo",
+ "optional": true,
+ "description": ""
+ }
+ ]
+ }
+ ]
+ }
+] satisfies ProtoLayer[];
diff --git a/src/pages/LandingPage.module.css b/src/pages/LandingPage.module.css
new file mode 100644
index 0000000..05cadc5
--- /dev/null
+++ b/src/pages/LandingPage.module.css
@@ -0,0 +1,241 @@
+.hero {
+ overflow: hidden;
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
+}
+
+.heroGrid {
+ display: grid;
+ grid-template-columns: minmax(0, 1.08fr) minmax(340px, 0.92fr);
+ gap: 72px;
+ min-height: calc(100vh - 72px);
+ padding-top: 72px;
+ padding-bottom: 72px;
+}
+
+.heroTitle {
+ max-width: 720px;
+ color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
+ font-size: clamp(52px, 7vw, 76px);
+ font-weight: 600;
+ letter-spacing: -0.045em;
+ line-height: 1.02;
+}
+
+.heroDescription {
+ max-width: 680px;
+ margin-top: 28px;
+ font-size: 20px;
+ line-height: 1.65;
+}
+
+.preview {
+ position: relative;
+ display: grid;
+ place-items: center;
+ min-height: 640px;
+}
+
+.preview::before {
+ position: absolute;
+ width: 520px;
+ height: 520px;
+ border-radius: 50%;
+ background: light-dark(rgba(34, 139, 230, 0.11), rgba(34, 139, 230, 0.16));
+ content: '';
+ filter: blur(2px);
+}
+
+.phone {
+ position: relative;
+ z-index: 1;
+ width: min(100%, 380px);
+ padding: 20px;
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
+}
+
+.phoneHeader {
+ padding-bottom: var(--mantine-spacing-md);
+ border-bottom: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+}
+
+.messages {
+ padding: 32px 0 24px;
+}
+
+.messageIncoming,
+.messageOutgoing {
+ max-width: 84%;
+}
+
+.messageIncoming {
+ background: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-6));
+}
+
+.messageOutgoing {
+ margin-left: auto;
+ background: light-dark(var(--mantine-color-blue-0), rgba(34, 139, 230, 0.18));
+}
+
+.encryptionNote {
+ color: var(--mantine-color-blue-6);
+ padding-top: var(--mantine-spacing-md);
+ border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+}
+
+.floatingCard {
+ position: absolute;
+ z-index: 2;
+ right: -24px;
+ bottom: 84px;
+ display: flex;
+ align-items: center;
+ gap: var(--mantine-spacing-sm);
+ padding: var(--mantine-spacing-md);
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
+}
+
+.featuresSection,
+.appsSection,
+.protocolSection {
+ padding: 112px 0;
+}
+
+.featuresSection {
+ background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-9));
+}
+
+.appsSection {
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
+}
+
+.sectionHeading {
+ max-width: 720px;
+}
+
+.sectionHeading h2 {
+ margin-top: var(--mantine-spacing-xs);
+ color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
+ font-size: clamp(36px, 5vw, 52px);
+ font-weight: 600;
+ letter-spacing: -0.035em;
+ line-height: 1.08;
+}
+
+.sectionHeading > p:last-child {
+ margin-top: var(--mantine-spacing-md);
+ font-size: 18px;
+ line-height: 1.65;
+}
+
+.featureTitle,
+.appTitle {
+ color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white));
+ font-size: 19px;
+ font-weight: 600;
+}
+
+.appCard {
+ min-height: 320px;
+ border-color: light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
+}
+
+.protocolSection {
+ padding-top: 0;
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
+}
+
+.protocolCard {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 48px;
+ padding: 56px;
+ background: linear-gradient(135deg, var(--mantine-color-blue-8), var(--mantine-color-blue-5));
+}
+
+.protocolCard h2 {
+ font-size: clamp(32px, 4vw, 46px);
+ font-weight: 600;
+ letter-spacing: -0.03em;
+ line-height: 1.1;
+}
+
+.protocolButton {
+ flex: 0 0 auto;
+}
+
+@media (max-width: 62em) {
+ .heroGrid {
+ grid-template-columns: 1fr;
+ gap: 28px;
+ padding-top: 88px;
+ }
+
+ .preview {
+ min-height: 560px;
+ }
+
+ .floatingCard {
+ right: 8%;
+ }
+}
+
+@media (max-width: 48em) {
+ .heroGrid {
+ min-height: auto;
+ padding-top: 64px;
+ padding-bottom: 64px;
+ }
+
+ .heroTitle {
+ font-size: clamp(44px, 13vw, 62px);
+ }
+
+ .heroDescription {
+ font-size: 18px;
+ }
+
+ .preview {
+ min-height: 520px;
+ }
+
+ .floatingCard {
+ right: 0;
+ bottom: 40px;
+ }
+
+ .featuresSection,
+ .appsSection,
+ .protocolSection {
+ padding: 80px 0;
+ }
+
+ .protocolSection {
+ padding-top: 0;
+ }
+
+ .protocolCard {
+ align-items: flex-start;
+ flex-direction: column;
+ padding: 36px 28px;
+ }
+}
+
+@media (max-width: 36em) {
+ .heroCopy :global(.mantine-Group-root) {
+ align-items: stretch;
+ }
+
+ .heroCopy :global(.mantine-Button-root) {
+ width: 100%;
+ }
+
+ .preview {
+ min-height: 480px;
+ }
+
+ .floatingCard {
+ display: none;
+ }
+}
diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx
new file mode 100644
index 0000000..0b6b5c7
--- /dev/null
+++ b/src/pages/LandingPage.tsx
@@ -0,0 +1,422 @@
+import {
+ Badge,
+ Box,
+ Button,
+ Card,
+ Container,
+ Group,
+ Paper,
+ SimpleGrid,
+ Stack,
+ Text,
+ ThemeIcon,
+ Title,
+} from '@mantine/core';
+import {
+ IconArrowDown,
+ IconBolt,
+ IconBrandAndroid,
+ IconBrandWindows,
+ IconCheck,
+ IconCode,
+ IconDatabaseOff,
+ IconDeviceDesktop,
+ IconFileTextShield,
+ IconKey,
+ IconLock,
+ IconMessageCircle,
+ IconPhoneOff,
+ type TablerIcon,
+} from '@tabler/icons-react';
+import { useEffect, useMemo, useState } from 'react';
+import { Link } from 'react-router-dom';
+import { RosettaLogo } from '../components/RosettaLogo/RosettaLogo';
+import { SEO } from '../components/SEO/SEO';
+import { SiteFooter } from '../components/SiteFooter/SiteFooter';
+import { SiteHeader } from '../components/SiteHeader/SiteHeader';
+import { fetchUpdates, type DownloadRelease } from '../services/updates';
+import classes from './LandingPage.module.css';
+
+const FEATURES: Array<{ icon: TablerIcon; title: string; description: string }> = [
+ {
+ icon: IconLock,
+ title: 'Private by design',
+ description:
+ 'Messages and media are encrypted before they leave your device, with keys kept under your control.',
+ },
+ {
+ icon: IconPhoneOff,
+ title: 'No phone number',
+ description:
+ 'Create an identity without sharing a phone number, email address, or advertising profile.',
+ },
+ {
+ icon: IconDatabaseOff,
+ title: 'Local message history',
+ description:
+ 'Readable message history lives on your devices instead of becoming a permanent server archive.',
+ },
+ {
+ icon: IconFileTextShield,
+ title: 'Encrypted media',
+ description:
+ 'Photos, videos, voice messages, and files use the same privacy-first transport model.',
+ },
+ {
+ icon: IconBolt,
+ title: 'Fast across platforms',
+ description:
+ 'A focused protocol and native clients keep everyday conversations responsive on desktop and mobile.',
+ },
+ {
+ icon: IconCode,
+ title: 'Documented protocol',
+ description:
+ 'Every packet, object, enum, and field is published from the protocol source schemas.',
+ },
+];
+
+const PLATFORMS: Array<{
+ key: string;
+ title: string;
+ description: string;
+ icon: TablerIcon;
+}> = [
+ {
+ key: 'android',
+ title: 'Rosetta for Android',
+ description: 'Private messaging for Android phones and tablets.',
+ icon: IconBrandAndroid,
+ },
+ {
+ key: 'win32',
+ title: 'Rosetta for Windows',
+ description: 'A native desktop client for Windows 10 and later.',
+ icon: IconBrandWindows,
+ },
+ {
+ key: 'linux',
+ title: 'Rosetta for Linux',
+ description: 'Desktop builds for x64 and ARM64 Linux systems.',
+ icon: IconDeviceDesktop,
+ },
+];
+
+function architectureName(arch: string) {
+ if (arch === 'arm64') {
+ return 'ARM64';
+ }
+ if (arch === 'universal') {
+ return 'Universal';
+ }
+ return arch;
+}
+
+function DownloadApps() {
+ const [releases, setReleases] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [failed, setFailed] = useState(false);
+
+ useEffect(() => {
+ const controller = new AbortController();
+ let active = true;
+
+ fetchUpdates(controller.signal)
+ .then((items) => {
+ if (active) {
+ setReleases(items);
+ }
+ })
+ .catch((error: unknown) => {
+ if (active && error instanceof Error && error.name !== 'AbortError') {
+ setFailed(true);
+ }
+ })
+ .finally(() => {
+ if (active) {
+ setLoading(false);
+ }
+ });
+
+ return () => {
+ active = false;
+ controller.abort();
+ };
+ }, []);
+
+ const groupedReleases = useMemo(
+ () =>
+ releases.reduce>((groups, release) => {
+ groups[release.platform] ||= [];
+ groups[release.platform].push(release);
+ return groups;
+ }, {}),
+ [releases],
+ );
+
+ return (
+
+
+
+
+ Rosetta Apps
+
+ One conversation, every device.
+
+ Select your platform. Available versions are loaded directly from the
+ Rosetta update service.
+
+
+
+
+ {PLATFORMS.map(({ key, title, description, icon: Icon }) => {
+ const platformReleases = groupedReleases[key] || [];
+
+ return (
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+
+ {platformReleases.map((release) => (
+ }
+ >
+ {architectureName(release.arch)} · {release.version}
+
+ ))}
+
+ {loading && (
+
+ Checking the latest release…
+
+ )}
+ {!loading && platformReleases.length === 0 && (
+
+ {failed
+ ? 'Release service is temporarily unavailable.'
+ : 'No active release for this platform.'}
+
+ )}
+
+
+ );
+ })}
+
+
+
+ );
+}
+
+export function LandingPage() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ Private messaging
+
+
+
+ A new era of{' '}
+
+ private
+ {' '}
+ messaging.
+
+
+ Rosetta keeps identity, conversations, and media under your control
+ — without requiring a phone number or building a readable message
+ archive on the server.
+
+
+
+ Download Rosetta
+
+
+ Explore Proto
+
+
+
+
+ {[
+ ['No phone', 'Identity without personal contact data'],
+ ['Encrypted', 'Messages and media protected by default'],
+ ['Local', 'Readable history stays on your devices'],
+ ].map(([value, label]) => (
+
+ {value}
+
+ {label}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Rosetta
+
+
+ Encrypted
+
+
+
+
+
+
+
+
+
+
+ Your recovery phrase never leaves your device.
+
+ 10:42
+
+
+
+ And the server cannot read this conversation.
+
+
+ 10:43
+
+
+
+
+
+ Exactly. Privacy is the default.
+
+ 10:43
+
+
+
+
+
+
+ End-to-end encrypted session
+
+
+
+
+
+
+
+
+
+ Delivered privately
+
+
+ No readable server history
+
+
+
+
+
+
+
+
+
+
+
+ Why Rosetta
+
+ Simple to use. Difficult to compromise.
+
+ Privacy is built into the account model, transport, and storage
+ architecture — not added as an optional mode.
+
+
+
+
+ {FEATURES.map(({ icon: Icon, title, description }) => (
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ Rosetta Proto
+
+
+ The protocol is documented from its source.
+
+
+ Browse every packet, object, enum, field ID, type reference, and
+ layer change generated directly from Rosetta YAML schemas.
+
+
+
+ Open Proto Documentation
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/PrivacyPage.tsx b/src/pages/PrivacyPage.tsx
index b8570d9..8018edb 100644
--- a/src/pages/PrivacyPage.tsx
+++ b/src/pages/PrivacyPage.tsx
@@ -18,7 +18,7 @@ export function PrivacyPage() {
canonical="https://rosetta.im/privacy/"
keywords="Rosetta privacy policy, encrypted messenger privacy, data protection"
/>
-
+
Rosetta IM is operated by ROSETTA CLOUD SERVICES LIMITED ("Rosetta",
"we", "us", or "our"). This Privacy Policy
diff --git a/src/pages/ProtoPage.module.css b/src/pages/ProtoPage.module.css
new file mode 100644
index 0000000..1bf6836
--- /dev/null
+++ b/src/pages/ProtoPage.module.css
@@ -0,0 +1,159 @@
+.page {
+ padding: 48px 0 112px;
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
+}
+
+.hero {
+ max-width: 780px;
+ padding: 64px 0 48px;
+}
+
+.hero h1 {
+ margin-top: var(--mantine-spacing-lg);
+ color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
+ font-size: clamp(52px, 7vw, 76px);
+ font-weight: 600;
+ letter-spacing: -0.045em;
+ line-height: 1.02;
+}
+
+.heroDescription {
+ margin-top: var(--mantine-spacing-lg);
+ font-size: 19px;
+ line-height: 1.65;
+}
+
+.stats {
+ margin-bottom: 56px;
+}
+
+.docsGrid {
+ display: grid;
+ grid-template-columns: 200px minmax(0, 1fr);
+ gap: 48px;
+ align-items: start;
+}
+
+.sidebar {
+ position: sticky;
+ top: 104px;
+}
+
+.sidebar a {
+ width: fit-content;
+ text-decoration: none;
+}
+
+.documentation {
+ min-width: 0;
+}
+
+.filters {
+ display: grid;
+ gap: var(--mantine-spacing-md);
+ margin-bottom: 56px;
+ background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
+}
+
+.schemaSection {
+ scroll-margin-top: 104px;
+ margin-top: 72px;
+}
+
+.schemaSection:first-of-type {
+ margin-top: 0;
+}
+
+.schemaSection > h2 {
+ color: light-dark(var(--mantine-color-black), var(--mantine-color-white));
+ font-size: 36px;
+ font-weight: 600;
+ letter-spacing: -0.03em;
+}
+
+.definition {
+ scroll-margin-top: 104px;
+ margin-top: 44px;
+ padding-top: 36px;
+ border-top: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+}
+
+.definition h3 {
+ margin-top: 5px;
+ color: light-dark(var(--mantine-color-dark-9), var(--mantine-color-white));
+ font-size: 27px;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+}
+
+.signature {
+ margin: var(--mantine-spacing-lg) 0;
+ padding: 12px 12px 12px 18px;
+ border: 1px solid light-dark(var(--mantine-color-blue-1), var(--mantine-color-blue-9));
+ border-radius: var(--mantine-radius-md);
+ background: light-dark(var(--mantine-color-blue-0), rgba(34, 139, 230, 0.1));
+}
+
+.signature code {
+ flex: 1;
+ padding: 0;
+ background: transparent;
+ color: light-dark(var(--mantine-color-blue-9), var(--mantine-color-blue-1));
+ overflow-wrap: anywhere;
+ white-space: normal;
+}
+
+.table {
+ border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+ border-radius: var(--mantine-radius-md);
+}
+
+.table th {
+ background: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-7));
+}
+
+.typeLink {
+ font-family: var(--mantine-font-family-monospace);
+ font-size: var(--mantine-font-size-sm);
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.primitive {
+ color: light-dark(var(--mantine-color-violet-8), var(--mantine-color-violet-3));
+}
+
+@media (max-width: 62em) {
+ .docsGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .sidebar {
+ position: static;
+ padding: var(--mantine-spacing-lg);
+ border: 1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));
+ border-radius: var(--mantine-radius-lg);
+ }
+}
+
+@media (max-width: 48em) {
+ .page {
+ padding-top: 32px;
+ }
+
+ .hero {
+ padding-top: 48px;
+ }
+
+ .docsGrid {
+ gap: 32px;
+ }
+
+ .schemaSection {
+ margin-top: 56px;
+ }
+
+ .definition {
+ margin-top: 36px;
+ }
+}
diff --git a/src/pages/ProtoPage.tsx b/src/pages/ProtoPage.tsx
new file mode 100644
index 0000000..265946a
--- /dev/null
+++ b/src/pages/ProtoPage.tsx
@@ -0,0 +1,483 @@
+import {
+ ActionIcon,
+ Anchor,
+ Badge,
+ Box,
+ Code,
+ Container,
+ CopyButton,
+ Group,
+ Paper,
+ SegmentedControl,
+ Select,
+ SimpleGrid,
+ Stack,
+ Table,
+ Text,
+ TextInput,
+ ThemeIcon,
+ Title,
+ Tooltip,
+} from '@mantine/core';
+import {
+ IconBraces,
+ IconCheck,
+ IconCopy,
+ IconFileCode,
+ IconList,
+ IconPackage,
+ IconSearch,
+} from '@tabler/icons-react';
+import { useState } from 'react';
+import { Link } from 'react-router-dom';
+import { SEO } from '../components/SEO/SEO';
+import { SiteFooter } from '../components/SiteFooter/SiteFooter';
+import { SiteHeader } from '../components/SiteHeader/SiteHeader';
+import { protoLayers } from '../generated/protoDocs';
+import type {
+ ProtoEnum,
+ ProtoField,
+ ProtoLayer,
+ ProtoPacket,
+ ProtoType,
+} from '../types/proto';
+import classes from './ProtoPage.module.css';
+
+type SchemaCategory = 'all' | 'packets' | 'types' | 'enums';
+
+const PRIMITIVE_TYPES = new Set([
+ 'bool',
+ 'bytes',
+ 'double',
+ 'float',
+ 'int8',
+ 'int16',
+ 'int32',
+ 'int64',
+ 'string',
+ 'uint8',
+ 'uint16',
+ 'uint32',
+ 'uint64',
+]);
+
+function slug(value: string) {
+ return value.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/_/g, '-').toLowerCase();
+}
+
+function entityMatches(
+ entity: ProtoPacket | ProtoType | ProtoEnum,
+ query: string,
+) {
+ if (!query) {
+ return true;
+ }
+
+ const searchable = [
+ entity.name,
+ entity.description,
+ 'fields' in entity
+ ? entity.fields.map((field) => `${field.name} ${field.type} ${field.description}`).join(' ')
+ : entity.values.map((value) => `${value.name} ${value.description}`).join(' '),
+ ]
+ .join(' ')
+ .toLowerCase();
+
+ return searchable.includes(query);
+}
+
+function TypeReference({ type, layer }: { type: string; layer: ProtoLayer }) {
+ const array = type.endsWith('[]');
+ const baseType = array ? type.slice(0, -2) : type;
+ const targetType = layer.types.find((item) => item.name === baseType);
+ const targetEnum = layer.enums.find((item) => item.name === baseType);
+
+ if (targetType || targetEnum) {
+ return (
+
+ {baseType}
+ {array && '[]'}
+
+ );
+ }
+
+ if (PRIMITIVE_TYPES.has(baseType)) {
+ return {type};
+ }
+
+ return {type};
+}
+
+function FieldTable({ fields, layer }: { fields: ProtoField[]; layer: ProtoLayer }) {
+ if (fields.length === 0) {
+ return (
+
+ This schema has no fields.
+
+ );
+ }
+
+ return (
+
+
+
+
+ ID
+ Field
+ Type
+ Description
+
+
+
+ {fields.map((field) => (
+
+
+ {field.id}
+
+
+
+ {field.name}
+
+ {field.optional && (
+
+ Optional
+
+ )}
+
+
+
+
+
+
+ {field.description || 'No field description in the schema.'}
+
+
+
+ ))}
+
+
+
+ );
+}
+
+function packetSignature(packet: ProtoPacket) {
+ const constructor = packet.id.toString(16).padStart(2, '0');
+ const fields = packet.fields
+ .map((field) => `${field.name}${field.optional ? '?' : ''}:${field.type}`)
+ .join(' ');
+ return `${packet.name}#${constructor}${fields ? ` ${fields}` : ''}`;
+}
+
+function typeSignature(type: ProtoType) {
+ const fields = type.fields
+ .map((field) => `${field.name}${field.optional ? '?' : ''}:${field.type}`)
+ .join(' ');
+ return `${type.name}${fields ? ` ${fields}` : ''} = ${type.name}`;
+}
+
+function Signature({ value }: { value: string }) {
+ return (
+
+ {value}
+
+ {({ copied, copy }) => (
+
+
+ {copied ? : }
+
+
+ )}
+
+
+ );
+}
+
+function PacketDefinition({ packet, layer }: { packet: ProtoPacket; layer: ProtoLayer }) {
+ return (
+
+
+
+
+ Packet · ID {packet.id}
+
+
{packet.name}
+
+ 0x{packet.id.toString(16).padStart(2, '0')}
+
+
+ {packet.description || `Packet ${packet.name} in protocol layer ${layer.version}.`}
+
+
+
+
+ );
+}
+
+function TypeDefinition({ type, layer }: { type: ProtoType; layer: ProtoLayer }) {
+ return (
+
+
+ Object
+
+ {type.name}
+
+ {type.description || `Object ${type.name} in protocol layer ${layer.version}.`}
+
+
+
+
+ );
+}
+
+function EnumDefinition({ item, layer }: { item: ProtoEnum; layer: ProtoLayer }) {
+ return (
+
+
+ Enum · {item.type}
+
+ {item.name}
+
+ {item.description || `Enumeration ${item.name} in protocol layer ${layer.version}.`}
+
+
+
+
+
+
+ Value
+ Name
+ Description
+
+
+
+ {item.values.map((value) => (
+
+
+ {value.id}
+
+
+
+ {value.name}
+
+
+
+
+ {value.description || 'No value description in the schema.'}
+
+
+
+ ))}
+
+
+
+
+ );
+}
+
+export function ProtoPage() {
+ const latestLayer = protoLayers.at(-1)!;
+ const [selectedVersion, setSelectedVersion] = useState(String(latestLayer.version));
+ const [category, setCategory] = useState('all');
+ const [search, setSearch] = useState('');
+
+ const layer =
+ protoLayers.find((item) => String(item.version) === selectedVersion) || latestLayer;
+ const query = search.trim().toLowerCase();
+ const packets = layer.packets.filter((packet) => entityMatches(packet, query));
+ const types = layer.types.filter((type) => entityMatches(type, query));
+ const enums = layer.enums.filter((item) => entityMatches(item, query));
+ const resultCount =
+ (category === 'all' || category === 'packets' ? packets.length : 0) +
+ (category === 'all' || category === 'types' ? types.length : 0) +
+ (category === 'all' || category === 'enums' ? enums.length : 0);
+
+ return (
+ <>
+
+
+
+
+
+ Rosetta
+
+
+ {' '}
+ / Proto
+
+
+
+
+
+
+
+ Generated from YAML
+
+ Rosetta Proto
+
+ Complete protocol reference generated from the RCC layer schemas. Browse
+ packet constructors, objects, enumerations, field IDs, optional values,
+ comments, and linked types.
+
+
+
+
+ {[
+ [IconPackage, layer.packets.length, 'Packets'],
+ [IconBraces, layer.types.length, 'Objects'],
+ [IconList, layer.enums.length, 'Enums'],
+ ].map(([Icon, value, label]) => {
+ const StatIcon = Icon as typeof IconPackage;
+ return (
+
+
+
+
+
+
+
+ {String(value)}
+
+
+ {String(label)}
+
+
+
+
+ );
+ })}
+
+
+
+
+
+ value && setSelectedVersion(value)}
+ data={protoLayers.map((item) => ({
+ value: String(item.version),
+ label: `Layer ${item.version}${item === latestLayer ? ' · latest' : ''}`,
+ }))}
+ />
+
+
+
+ Contents
+
+
+ Packets ({layer.packets.length})
+
+
+ Objects ({layer.types.length})
+
+
+ Enums ({layer.enums.length})
+
+
+
+
+
+ Source
+
+ {layer.source}
+
+ {layer.description || 'Initial protocol schema.'}
+
+
+
+
+
+
+
+ setSearch(event.currentTarget.value)}
+ leftSection={ }
+ placeholder="Search packets, objects, fields, or types"
+ aria-label="Search Proto documentation"
+ />
+ setCategory(value as SchemaCategory)}
+ data={[
+ { label: 'All', value: 'all' },
+ { label: 'Packets', value: 'packets' },
+ { label: 'Objects', value: 'types' },
+ { label: 'Enums', value: 'enums' },
+ ]}
+ fullWidth
+ />
+
+ {resultCount} definitions in layer {layer.version}
+
+
+
+ {(category === 'all' || category === 'packets') && packets.length > 0 && (
+
+ Packets
+
+ Network packet constructors and their serialized fields.
+
+ {packets.map((packet) => (
+
+ ))}
+
+ )}
+
+ {(category === 'all' || category === 'types') && types.length > 0 && (
+
+ Objects
+
+ Reusable structured values referenced by packet fields.
+
+ {types.map((type) => (
+
+ ))}
+
+ )}
+
+ {(category === 'all' || category === 'enums') && enums.length > 0 && (
+
+ Enums
+
+ Named protocol constants and their serialized numeric values.
+
+ {enums.map((item) => (
+
+ ))}
+
+ )}
+
+ {resultCount === 0 && (
+
+ No schema definitions found.
+
+ Try another search term or category.
+
+
+ )}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/SupportPage.tsx b/src/pages/SupportPage.tsx
index 4cd9c0b..41862c5 100644
--- a/src/pages/SupportPage.tsx
+++ b/src/pages/SupportPage.tsx
@@ -161,7 +161,6 @@ export function SupportPage() {
keywords="Rosetta support, account recovery, messaging help, privacy help"
/>
diff --git a/src/pages/TermsPage.tsx b/src/pages/TermsPage.tsx
index a6fec9e..75d7c0e 100644
--- a/src/pages/TermsPage.tsx
+++ b/src/pages/TermsPage.tsx
@@ -18,7 +18,7 @@ export function TermsPage() {
canonical="https://rosetta.im/terms/"
keywords="Rosetta terms of use, end user license agreement, encrypted messenger terms"
/>
-
+
These Terms of Use and End User License Agreement ("Terms") govern
your use of Rosetta, an end-to-end encrypted messaging application operated by
diff --git a/src/services/updates.ts b/src/services/updates.ts
new file mode 100644
index 0000000..780ac84
--- /dev/null
+++ b/src/services/updates.ts
@@ -0,0 +1,42 @@
+export interface DownloadRelease {
+ platform: string;
+ arch: string;
+ version: string;
+ link: string;
+}
+
+interface UpdateItem {
+ platform: string;
+ arch: string;
+ version: string;
+ downloadUrl: string;
+}
+
+const PLATFORM_ORDER = ['linux', 'win32', 'android'];
+const ARCH_ORDER = ['arm64', 'x64', 'universal'];
+
+export async function fetchUpdates(signal?: AbortSignal): Promise {
+ const response = await fetch('https://sdu.rosetta.im/updates/all', { signal });
+
+ if (!response.ok) {
+ throw new Error(`Update server returned ${response.status}`);
+ }
+
+ const data = (await response.json()) as { items?: UpdateItem[] };
+
+ return (data.items || [])
+ .map((item) => ({
+ platform: item.platform,
+ arch: item.arch,
+ version: item.version,
+ link: new URL(item.downloadUrl, 'https://sdu.rosetta.im').toString(),
+ }))
+ .sort((a, b) => {
+ const platformComparison =
+ PLATFORM_ORDER.indexOf(a.platform) - PLATFORM_ORDER.indexOf(b.platform);
+ if (platformComparison !== 0) {
+ return platformComparison;
+ }
+ return ARCH_ORDER.indexOf(a.arch) - ARCH_ORDER.indexOf(b.arch);
+ });
+}
diff --git a/src/style.css b/src/style.css
index 4500664..00fce46 100644
--- a/src/style.css
+++ b/src/style.css
@@ -1,3 +1,22 @@
-*{
- scroll-behavior: smooth;
-}
\ No newline at end of file
+* {
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ min-width: 320px;
+ margin: 0;
+ background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-8));
+}
+
+a {
+ text-underline-offset: 3px;
+}
+
+::selection {
+ background: var(--mantine-color-blue-1);
+ color: var(--mantine-color-blue-9);
+}
diff --git a/src/types/proto.ts b/src/types/proto.ts
new file mode 100644
index 0000000..4644001
--- /dev/null
+++ b/src/types/proto.ts
@@ -0,0 +1,42 @@
+export interface ProtoField {
+ id: number;
+ name: string;
+ type: string;
+ optional: boolean;
+ description: string;
+}
+
+export interface ProtoPacket {
+ name: string;
+ id: number;
+ description: string;
+ fields: ProtoField[];
+}
+
+export interface ProtoType {
+ name: string;
+ description: string;
+ fields: ProtoField[];
+}
+
+export interface ProtoEnumValue {
+ id: number;
+ name: string;
+ description: string;
+}
+
+export interface ProtoEnum {
+ name: string;
+ type: string;
+ description: string;
+ values: ProtoEnumValue[];
+}
+
+export interface ProtoLayer {
+ version: number;
+ description: string;
+ source: string;
+ enums: ProtoEnum[];
+ types: ProtoType[];
+ packets: ProtoPacket[];
+}