Por que colocamos interfaces específicas de cada plataforma sobre um único núcleo em Rust
Para lançar o mesmo editor em três sistemas operacionais, há um caminho em que a interface é criada apenas uma vez. Nós seguimos esse caminho uma vez, voltamos atrás e colocamos um conjunto de shells finos específicos de cada plataforma sobre um único núcleo.

Para lançar o mesmo editor no Linux, no macOS e no Windows, é preciso criar a interface três vezes.
No início, queríamos evitar esse custo. Desenhamos a mesma tela nos três sistemas operacionais com um único toolkit multiplataforma. Com essa decisão, passamos pelo primeiro gate de desempenho e também pelo primeiro marco de entrada de texto em coreano.
Então as prioridades mudaram.
O desempenho é mais importante do que o design, e o design é mais importante do que o custo. Tempo e custo ficam fora das restrições.
Ao reavaliarmos com esses critérios, a resposta também mudou.
Abrimos mão de uma única interface
Um toolkit multiplataforma tem vantagens claras. Uma funcionalidade criada uma vez pode ser usada em vários sistemas operacionais.
Mas também havia um preço a pagar. Ele usava de 40 a 60 MB a mais de memória, e a inicialização era de 150 a 200 ms mais lenta. A representação dos caracteres deixava a desejar em comparação com o mecanismo de texto nativo do sistema operacional, e também não era possível usar plenamente os efeitos visuais próprios do sistema.
Quando o custo de desenvolvimento era importante, essa era uma escolha razoável. Mas, quando retiramos o custo das restrições, não havia mais motivo para aceitar essas perdas.
Por isso, decidimos criar em Rust o núcleo responsável pelo processamento dos documentos e criar separadamente, para cada sistema operacional, o shell responsável pela interface.
Não foi porque a tecnologia anterior era ruim. Quando as prioridades mudam, as mesmas opções podem levar a respostas diferentes. Decidimos pagar o custo de criar as funcionalidades três vezes para recuperar o desempenho, a qualidade dos caracteres e uma experiência condizente com o sistema.
No entanto, não descartamos tudo.
Mantivemos intacto o conjunto de disciplinas de medição. Deixar o ponteiro fora da janela, localizar a janela exatamente pelo nome e contar os frames de forma síncrona. Também levamos conosco os princípios de design da entrada de texto em coreano. Mostramos ao sistema operacional apenas a linha em que está o cursor e não estilizamos os caracteres durante a composição. Se os estados ficarem dessincronizados, não fazemos suposições: nós os sincronizamos novamente.
Mesmo que o código seja descartado, os princípios validados permanecem.
Mantivemos pequeno o papel do shell
Se os shells específicos de cada plataforma começarem a assumir muitas tarefas, os três produtos rapidamente se tornarão diferentes. Por isso, definimos claramente o que o shell deve e não deve fazer.
O shell cria a janela e desenha os caracteres. Ele encaminha ao núcleo as entradas do teclado e do ponteiro e transforma a entrada de texto em coreano em comandos de edição. Também aplica os efeitos visuais do sistema operacional.
Por outro lado, ele não interpreta Markdown. Com exceção das quebras de linha, também não determina o layout, nem salva arquivos ou toma decisões sobre licenças.
Não deixamos esse princípio apenas registrado na documentação. Se um código de interpretação de Markdown entrar no shell, a verificação automática interrompe a compilação.
Se apenas um shell começar a processar a sintaxe, os resultados poderão variar ligeiramente entre os sistemas operacionais. Esses problemas também são difíceis de encontrar. Se uma tabela parecer diferente em apenas um shell, a causa real pode nem estar no código que desenha a tabela.
Fizemos com que apenas o núcleo permaneça por muito tempo
O núcleo não depende de nenhuma tecnologia de interface. Mesmo sem uma janela, ele pode ser testado de forma independente no terminal.
O núcleo é responsável pela edição de documentos, pela interpretação de Markdown, pelo desfazer, pela pesquisa, pelo outline, pelo salvamento de arquivos, pela exportação e pela validação de licenças.

Quando o usuário pressiona uma tecla, o shell encaminha essa entrada ao núcleo. O núcleo modifica o documento e reinterpreta apenas a região ao redor da parte alterada. Depois de calcular somente o conteúdo visível na tela, ele devolve ao shell os resultados modificados. O shell redesenha apenas as linhas afetadas e conclui um frame.
Se, nesse processo, o documento inteiro for lido novamente, isso não é um simples problema de desempenho. Significa que violamos o orçamento definido no início.
A correção também é validada apenas uma vez, no núcleo. Todos os 652 exemplos oficiais do CommonMark precisam ser aprovados e, se o número de aprovações diminuir, a compilação falha. Também realizamos testes de fuzzing que introduzem continuamente entradas inesperadas.
Também verificamos se o arquivo salvo é idêntico ao original byte por byte. Se a taxa de round-trip for inferior a 1,0, não significa que está lento, mas que perdemos o arquivo.
Mesmo que o shell mude, o núcleo permanece igual. No Linux e no Windows, conectamos o núcleo diretamente; no macOS, usamos código de integração em Swift. O shell do macOS está sendo criado com AppKit e TextKit 2, e o shell do Windows, com DirectWrite.
O objetivo é manter o documento igual e fazer com que a experiência de uso seja própria de cada sistema operacional.
O documento é igual, mas a experiência de uso é diferente
Ao definirmos os limites, também ficou claro o que precisava ser unificado.
O que precisa ser unificado é o documento. Independentemente do sistema operacional em que seja salvo, o conteúdo do arquivo deve ser idêntico byte por byte. O alinhamento das tabelas, as terminações de linha e a quebra de linha final também não podem mudar.
A correção do Markdown já é validada no núcleo. Não é necessário comprová-la novamente toda vez que criamos um novo shell. O que deve ser verificado em uma nova plataforma é a experiência de uso e o desempenho.
Por outro lado, não unificamos a experiência diretamente percebida pelo usuário.
Os caracteres são desenhados com o mecanismo de texto de cada sistema operacional. O método de entrada de texto em coreano também usa diretamente a abordagem do sistema operacional. Para os efeitos translúcidos, usamos igualmente os recursos do sistema no macOS e no Windows; no Linux, que não oferece os mesmos recursos, desenhamos diretamente apenas as partes necessárias.
Também não forçamos a padronização da posição dos botões da janela, do formato dos menus ou das teclas modificadoras dos atalhos. Mostrar os mesmos pixels em sistemas operacionais diferentes não é o objetivo. É mais importante que a experiência pareça natural em cada sistema operacional.
Também definimos os critérios de lançamento separadamente para cada sistema operacional. Para que uma versão possa ser lançada, ela precisa atender aos critérios de desempenho e concluir testes reais com o método de entrada de coreano padrão.
Uma linha para essa plataforma só é adicionada à tabela de benchmarks depois que uma compilação realmente existe. Se incluirmos antecipadamente uma linha que não foi medida, até as demais linhas da tabela se tornam difíceis de confiar.
Essa escolha tem um preço claro
Precisamos criar a mesma funcionalidade três vezes. Também precisamos implementar três vezes a entrada de texto em coreano e manter três ambientes de compilação.
No momento, não podemos compilar nem depurar o Windows e o macOS nas máquinas que usamos. Precisamos de hardware separado e de ambientes de automação. Por isso, não definimos datas para essas duas plataformas.
Também registramos as condições para reconsiderar essa decisão. A premissa da decisão é que o custo foi retirado das restrições. Se não conseguirmos obter o hardware necessário, apenas o cronograma da plataforma correspondente será adiado. O lançamento para Linux continuará como planejado.
Uma decisão precisa não apenas de motivos, mas também de condições para ser reconsiderada. Assim, quando a situação mudar, não será necessário repetir toda a discussão desde o início.
O que precisa permanecer por muito tempo é o núcleo. Os shells devem poder ser descartados a qualquer momento.
Deixamos a cargo da plataforma apenas aquilo que pode ser descartado.