Новый лендинг

This commit is contained in:
2026-07-31 06:06:07 -07:00
parent 8b2af4db62
commit 871c84cedd
8 changed files with 1873 additions and 18 deletions

View File

@@ -16,6 +16,22 @@
border: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-5));
}
.protoSubheading {
margin: 30px 0 12px;
color: light-dark(var(--mantine-color-dark-8), var(--mantine-color-dark-0));
font-size: 17px;
font-weight: 650;
}
.layerExamples {
margin: 18px 0 24px;
}
.layerExamples .protoCode {
height: calc(100% - 30px);
margin-bottom: 0;
}
.docsLink {
display: inline-block;
margin-top: 44px;

View File

@@ -374,13 +374,147 @@ field_id:uint8 | wire_type:uint8 | [length:uint16] | value
</LegalList>
</LegalSection>
<LegalSection title="Layers and compatibility">
<LegalSection title="Protocol versioning">
<LegalText>
Protocol layers are generated from RCC YAML schemas. Every layer defines
packet IDs, field IDs, primitive types, reusable types, enumerations, and
optional values. Keeping field IDs stable allows compatible decoders to
read known fields while the schema evolves.
Each <Code>layer.N.yml</Code> file is a complete protocol snapshot. Its
top-level <Code>version</Code> is the layer number. RCC sorts all layers by
that number, rejects duplicate versions, and builds a timeline for every
packet, field, type, and enum value. A newer layer repeats the definitions
that still exist and adds, removes, or makes fields optional as needed.
</LegalText>
<LegalText>
Packet IDs should remain stable, and RCC explicitly validates field-ID
reuse inside every packet and type. An existing field ID cannot be assigned
to a different field name or type in a later layer. A new field receives a
new ID; an optional field may be omitted by older peers. The server
generator keeps version-aware codecs for the supported range, while a
client generator produces the current layer.
</LegalText>
<LegalText>
RCC does not negotiate a layer over the network by itself. The connection
or handshake code selects a version supported by both peers, then passes
that number to the generated server codec. The registry exposes the
minimum and maximum compiled versions, and the codec includes only fields
whose lifetime contains the selected version. TLV fields that are not used
by that layer do not become properties of the decoded packet.
</LegalText>
<Title order={3} className={classes.protoSubheading}>
Two simple layers
</Title>
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md" className={classes.layerExamples}>
<Box>
<Text fw={650} size="sm">Layer 1</Text>
<Code block className={classes.protoCode}>
{`version: 1
packets:
Profile:
id: 10
fields:
- id: 1
name: username
type: string`}
</Code>
</Box>
<Box>
<Text fw={650} size="sm">Layer 2</Text>
<Code block className={classes.protoCode}>
{`version: 2
packets:
Profile:
id: 10
fields:
- id: 1
name: username
type: string
- id: 2
name: displayName?
type: string`}
</Code>
</Box>
</SimpleGrid>
<LegalText>
Layer 2 preserves packet ID <Code>10</Code> and field ID
<Code> 1</Code>, then introduces optional field ID <Code>2</Code>. A layer 1
peer sends and reads only <Code>username</Code>. A layer 2 peer can include
<Code> displayName</Code>, but does not require it.
</LegalText>
<Title order={3} className={classes.protoSubheading}>
Simplified server output
</Title>
<LegalText>
In server mode RCC combines both snapshots into one model and a codec that
receives the negotiated version. The generated source is more verbose; the
following excerpt keeps only the versioning decisions.
</LegalText>
<Code block className={classes.protoCode}>
{`public class PacketProfile extends Packet {
public String username;
public String displayName; // introduced in layer 2
}
public class PacketProfileCodec {
private static final int F_USERNAME = 1;
private static final int F_DISPLAY_NAME = 2;
public PacketProfile decode(ByteBuf data, int version) {
TlvReader reader = new TlvReader(data);
PacketProfile packet = new PacketProfile();
packet.username = requireField(reader.getString(F_USERNAME));
if (version >= 2) {
packet.displayName = reader.getString(F_DISPLAY_NAME);
}
return packet;
}
public ByteBuf encode(PacketProfile packet, int version) {
TlvWriter writer = new TlvWriter();
writer.writeString(F_USERNAME, packet.username);
if (version >= 2 && packet.displayName != null) {
writer.writeString(F_DISPLAY_NAME, packet.displayName);
}
return writer.getBuffer();
}
}`}
</Code>
<LegalText>
RCC also writes the supported interval to the generated registry. A packet
codec can reject a layer older than the packet&apos;s introduction. Removed
fields remain available to the server model and codec only for the layers
in which they existed; enum values receive equivalent
<Code> isSupportedInVersion</Code> checks.
</LegalText>
<Code block className={classes.protoCode}>
{`public final class RccGeneratedPacketRegistry {
public static final int MIN_VERSION = 1;
public static final int MAX_VERSION = 2;
public static final int PACKET_PROFILE_ID = 10;
}`}
</Code>
<Title order={3} className={classes.protoSubheading}>
Latest client output
</Title>
<LegalText>
A TypeScript client is generated from the latest layer, so application code
sees the current shape and the optional marker directly.
</LegalText>
<Code block className={classes.protoCode}>
{`export class PacketProfile extends Packet {
username!: string;
displayName?: string;
}
export class RccGeneratedPacketRegistry {
static readonly VERSION = 2;
static readonly PACKET_PROFILE_ID = 10;
}`}
</Code>
</LegalSection>
<LegalSection title="How an RCC YAML layer is structured">
@@ -707,10 +841,11 @@ export function ProtoDocsPage() {
<Box className={classes.documentation}>
<Box className={classes.filters}>
<TextInput
label="Search"
value={search}
onChange={(event) => setSearch(event.currentTarget.value)}
leftSection={<IconSearch size={17} />}
placeholder="Search packets, types, fields, or enums"
placeholder="Packets, types, fields, or enums"
aria-label="Search Proto documentation"
/>
<Text c="dimmed" size="xs">

View File

@@ -40,12 +40,12 @@
}
.searchInput input {
height: 64px;
padding-inline: 58px;
border-color: transparent;
height: 56px;
padding-inline: 50px;
border-color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4));
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
box-shadow: 0 12px 38px rgba(25, 113, 194, 0.16);
font-size: 17px;
box-shadow: none;
font-size: 16px;
}
.searchInput input:focus {

View File

@@ -108,9 +108,6 @@ export function SupportPage() {
</ThemeIcon>
<Stack align="center" gap="sm" className={classes.heroContent}>
<Text fw={700} size="sm" c="blue.8">
Rosetta Support
</Text>
<Title order={1}>How can we help?</Title>
<Text c="blue.9" ta="center" className={classes.heroDescription}>
Search account access, messaging, media, privacy, and security help.
@@ -135,8 +132,8 @@ export function SupportPage() {
}
placeholder="Describe your problem"
aria-label="Search Rosetta support"
size="xl"
radius="xl"
size="lg"
radius="sm"
className={classes.searchInput}
/>