Skip to content

Upgrading pt BR

Edgar Mesquita edited this page Sep 24, 2026 · 15 revisions

Atualizar de versão

🌐 Esta página em: English · Português

O que cada release MUDOU debaixo de você, versão a versão — os erros de compilação que vai encontrar e a resposta de uma linha para cada um.

Cada entrada aqui foi encontrada por uma app real ao atualizar, não derivada de um changelog. Uma quebra que custou uma hora a alguém e nunca foi escrita custa a mesma hora à próxima pessoa.

Cada entrada abre com o que ficou mais simples, porque uma página que só lista quebras nunca diz se valeu a pena atualizar.


Para 0.2.0-preview.58

O que ficou mais simples. O C# que uma página roda no browser responde o que o .NET responde: o texto de um número nos dois sentidos, uma divisão por zero, os operadores de um nullable, a precisão de um float, o default de um valor, uma chave ausente num dicionário. O editor de código ganha um motor próprio e recebe a entrada do jeito que cada plataforma a entrega, e um app publicado deixa de levar o C# dentro dos source maps.

Os tipos do editor de código foram para eQuantic.UI.Code

using eQuantic.UI.Primitives;   // CodeDocument, CodeEditorController, CSharpLanguage… não estão mais aqui
using eQuantic.UI.Code;         // acrescente este

Trinta e cinco tipos: o documento, o controlador, posições e intervalos, o realçador, o histórico e o keymap, as linguagens e as interfaces de provedor. A assembly chega junto com eQuantic.UI.Components, então o csproj não muda. CodeSurface recebe um ICodeSurfaceModel onde recebia um CodeEditorController, o que só afeta um host que monta a superfície ele mesmo.

ListDetail recebe builders

ListDetail(list: Inbox(), detail: Message())               // não compila mais
ListDetail(list: () => Inbox(), detail: () => Message())

Cada forma constrói a sua própria cópia dos painéis, então nenhum é montado duas vezes.

StyleClass e o resto de eQuantic.UI.Web.Styling não existem mais

StyleClass, Spacing, Breakpoint e [CompileTimeEvaluate], junto com StyleClass e StyleClasses em HtmlElement e IComponent. Um StyleClass acrescentava um nome de classe que nenhuma folha de estilo definia, então nada do que ele fazia se perde: estilize um elemento da saída de escape com HtmlStyle ou ClassBuilder.

Treze ícones adotaram o nome do upstream

era é
LucideIcons.Trash2 LucideIcons.Trash
LucideIcons.Building2 LucideIcons.BuildingComplex
LucideIcons.History LucideIcons.RotateCcwClock
TablerIcons.BrandKakoTalk TablerIcons.BrandKakaoTalk
TablerIcons.CurrencyRubel TablerIcons.CurrencyRuble
TablerIcons.Foodsteps TablerIcons.Footsteps
TablerIcons.GenderTrasvesti TablerIcons.GenderTravesti
TablerIcons.Ikosaedr TablerIcons.Icosahedron
TablerIcons.MoodConfuzed, MoodConfuzedFilled TablerIcons.MoodConfused, MoodConfusedFilled
TablerIcons.SportBillard TablerIcons.SportBilliard
TablerIcons.BrandAdobeAfterEffect TablerIcons.BrandAdobeAfterEffects
TablerIcons.Physotherapist TablerIcons.Physiotherapist

Cada um é o mesmo desenho. O próprio LucideIcons.Trash agora desenha o que o Trash2 desenhava, o cesto com duas linhas, e nenhum erro de compilação avisa.

Um app publicado não escreve source maps

Não é erro de compilação. Um build Release não escreve nenhum e um publish nunca leva um. Um build Debug os escreve com o C#, para o depurador do browser. Um relator de erros que envia os mapas ele mesmo define <EQuanticSourceMaps>external</EQuanticSourceMaps>, que os escreve sem o C#. Até aqui todo app publicado levava o C# dos seus componentes nos mapas, corpos de [ServerAction] incluídos.

equantic.css não é mais escrito

Um app que ligava /_equantic/equantic.css pelo ConfigureHtmlShell remove o link: a folha gerada já vai inline.

O browser responde como o .NET, exceções incluídas

Não é erro de compilação, e é o que mais provavelmente aparece numa página rodando: código que se apoiava na resposta do JavaScript recebe a do .NET.

  • int.Parse("12abc") lança FormatException onde respondia 12, e um TryParse que falha deixa 0, não NaN.
  • Uma divisão inteira ou um resto por zero lança DivideByZeroException onde a página mostrava Infinity ou NaN.
  • x += 1 num int? nulo continua nulo onde virava 1.
  • Uma chave ausente num SortedDictionary, num SortedList ou num Dictionary com chave de valor lança KeyNotFoundException, e um TryGetValue que não acha escreve default.
  • true.ToString() é True.
  • int x = default é 0 e new long[3] guarda longs, onde eram undefined e números comuns, e um filtro escrito o.Status != default(OrderStatus) compara com o zero do enum, onde todo item passava.

Para 0.2.0-preview.57

O que ficou mais simples. Um componente pode ser escrito sobre uma base sua, e um componente que a página compõe pode carregar os próprios dados de servidor. As duas coisas eram prometidas e nenhuma valia: a subclasse de uma base do app só levava o Build e morria no browser, e um IServerPrefetch abaixo da página nunca era chamado. O runtime também passa a ter uma cópia committed de cada módulo transpilado, em vez de duas.

As declarações nativas foram para eQuantic.UI.Native.Hosting

using eQuantic.UI.Primitives;       // PhotonEntitlements, AppCategory… não estão mais aqui
using eQuantic.UI.Native.Hosting;   // acrescente este

PhotonCapabilityAttribute, PhotonEntitlementAttribute, PhotonEntitlements, PhotonBundleKeyAttribute, PhotonBundleValueKind e AppCategory. Um app Photon já referencia a assembly de hosting — é de lá que vem o CreateApp —, então é um using só. Um app web nunca conseguiria usá-los de verdade e não é afetado.

RangeValue carrega só números

Progress(child, value: new RangeValue(0.45f, 0, 1) { Text = "Estimando" })   // não compila mais
Progress(child, value: new RangeValue(0.45f, 0, 1), valueText: "Estimando")

As palavras foram para o nó — Progress.ValueText e Adjustable.ValueText —, porque uma barra indeterminada não tem número e perdia as palavras junto com ele. Vindo da .55, é a segunda mudança no tipo: a .56 renomeou AdjustableValue para RangeValue, e a .57 tira o Text dele.

SemanticNode se desconstrói em treze partes

var (role, path, bounds, label, value, disabled, isChecked, expanded,
     current, selected, heading, live) = node;          // não compila mais
var (role, path, bounds, label, value, disabled, isChecked, expanded,
     current, selected, heading, live, range) = node;

Construir não muda: Range é um parâmetro com valor padrão.

ImageOptimizationState não existe mais

Ele era escrito pelo UseImageOptimization e lido por nada. Leia ImageOptimizationOptions do DI em vez disso, que é o que o endpoint /_equantic/image faz a cada requisição.

Quatro overloads viraram um cada, e a maioria das chamadas continua compilando

  • UI.Glyph(glyph) vira UI.Icon(glyph). UI.Icon e new Icon recebem um IconGlyph, com uma conversão implícita a partir de Icons, então UI.Icon(Icons.Check) fica igual.
  • EmailRenderer.Render(component, theme) liga na forma de VisualNode, que um componente já é, com os mesmos parâmetros.
  • WebRealizer.Lower(node, theme, scale, styles) é um método só, com StyleSink? styles = null.
  • CodeWriter.BeginScope(line, opener, closer) saiu; a forma de abertura e fechamento fica.

EQ2012: um [ServerAction] num componente abstrato

public abstract class CardBase : StatelessComponent
{
    [ServerAction] public Task<string> Load() => …;   // EQ2012
}

O servidor registra uma ação sob o nome do componente CONCRETO, então um stub na base invocava um id que nada servia. Declare a ação no componente concreto.

Um IServerPrefetch composto agora roda

Não é erro de compilação, mas muda o que acontece: um componente que implementa IServerPrefetch abaixo da página era pulado, e agora o PrefetchAsync dele roda e os campos chegam ao payload de hidratação. Um componente que implementava esperando que nada acontecesse vai descobrir que algo acontece.


Para 0.2.0-preview.56

O que ficou mais simples. Nenhum membro sobrevive para manter viva uma forma antiga. O TypeStyle e o SemanticNode carregavam um construtor e um Deconstruct acrescentados para que um assembly compilado contra um pacote mais velho ainda ligasse, e os dois saíram junto com o parágrafo que os explicava. Uma extensão sobre o vocabulário agora é transpilada como qualquer outra, então node.Centered() vira VisualNodeExtensions.centered(node), e os dois espelhos mais a costura que aquela exceção exigia foram junto. Isso também fecha um buraco que nenhum autor em C# conseguia ver: um parâmetro de componente com o nome de um membro do runtime sobrescrevia esse membro e derrubava a página no browser com centered is not a function.

O AdjustableValue virou RangeValue

Value = new AdjustableValue(now, min, max)   // não compila mais
Value = new RangeValue(now, min, max)

Os mesmos três números e o mesmo Text opcional. O nome mudou porque um segundo nó passou a carregar o trio: o Progress.Value também é um RangeValue, e uma barra de progresso não é ajustável.

O WebFrame recebe o conteúdo como um valor só

new WebFrame { Source = "https://example.com", Title = "Docs" }   // não compila mais
new WebFrame { Document = "<p>hello</p>" }                        // não compila mais
WebFrame(WebContent.Url("https://example.com"), "Docs")
WebFrame(WebContent.Document("<p>hello</p>"), "Note")

Um frame mostra uma URL ou marcação embutida, nunca as duas, e duas propriedades init não conseguiam dizer isso. O título é argumento do construtor pela razão de ele existir: um frame que ninguém consegue nomear é um que o leitor de tela não consegue anunciar.

O Navigable recebe o manipulador primeiro

new Navigable(rows, onMove)   // não compila mais
new Navigable(onMove, rows)

Os filhos vão por último, como em todo contêiner do vocabulário.

As factories de gráficos espelham os seus records

ChartSeries(name: "2026", values: [15, 21])   // não compila mais
ChartSeries(Name: "2026", Values: [15, 21])

Os parâmetros de um record posicional SÃO as propriedades dele, então new ChartSeries(Name: …) e a factory passam a ter uma grafia só entre as duas. Só quebra quem usa argumento NOMEADO, e o CategoryAxis e o ValueAxis mudaram do mesmo jeito.

O TypeStyle e o SemanticNode se desmontam na forma real

var (size, line, weight, tracking, maxScale, mono, italic) = style;            // não compila mais
var (size, line, weight, tracking, maxScale, mono, italic, family) = style;

Os dois carregavam um Deconstruct mais curto (e o TypeStyle um construtor a condizer) para que código compilado contra um pacote mais velho ainda ligasse. Construir não muda, porque os parâmetros novos têm valor padrão. Só a desmontagem muda, e o compilador diz a aridade que ele quer.

O LinkRegion e o RealizeResult carregam mais

var (bounds, destination) = region;          // não compila mais
var (bounds, destination, path) = region;

Uma região de link agora sabe o caminho de layout de onde veio, e é isso que permite chegar nela pelo Tab e ativá-la no Photon. Só afeta um host que leia as regiões realizadas, e o construtor do RealizeResult cresceu pela mesma passada.

Para 0.2.0-preview.55

O que ficou mais simples. Todo despacho sobre o vocabulário agora é um visitor: o motor de layout, a passada de pintura do Photon, o realizador web, o DOM do servidor, a passada de semântica e as duas alternativas do email. Um nó que um realizador esqueceu vira erro de compilação em vez de cair num braço default, e foi assim que Navigable e WebFrame apareceram, medidos em silêncio como uma caixa de tamanho zero. Os vinte wrappers que envolvem um único filho compartilham uma base, SingleChildNode, onde antes quatro listas mantidas à mão discordavam em oito lugares. Isso aparece numa tela nativa: um Text dentro de um Pressable, um Link ou um Hoverable, numa Row estreita demais para ele, agora trunca como um Text solto em vez de passar 128dp da borda da linha. E um slider diz o valor que tem, na web e para o VoiceOver e o TalkBack.

ButtonStyles saiu

Era só uma visão sobre o Sizing, e a escada agora é a única declaração das métricas de um controle. Cada campo de Metrics(size, density) já era produzido pelo degrau que ele agora nomeia, então nenhum número muda:

ButtonStyles.MinWidth                          // → Sizing.ButtonMinWidth
ButtonStyles.Metrics(size, density).Height     // → Sizing.Height(size, density)
ButtonStyles.Metrics(size, density).PadX       // → Sizing.PaddingX(size, density)
ButtonStyles.Metrics(size, density).Gap        // → Sizing.Gap(size)
ButtonStyles.Metrics(size, density).LabelSize  // → Sizing.LabelSize(size, density)
ButtonStyles.Metrics(size, density).IconSize   // → Sizing.Icon(size)
ButtonStyles.Metrics(size, density).Radius     // → Sizing.Radius(size)
ButtonStyles.Metrics(size, density).Hit        // → Sizing.HitTarget(size, density)

FlexNode é só do host

FlexNode container = row;      // EQ2010 num componente

O runtime nunca exportou um gêmeo do FlexNode, então um componente cliente que o nomeava compilava e depois morria na hidratação com "does not provide an export named". Agora o eqc avisa no ponto da chamada. Nomeie o nó concreto, Row ou Column. Row(...).With(child) continua compilando, porque a cerca pergunta por onde o membro foi alcançado. O código do servidor e a track nativa não mudam.

EQ2111 quando uma busca de serviço não tem por onde chavear

Uma chamada GetService sem argumento de tipo e sem argumento emitia getService(), que o runtime não consegue responder, porque o registro dele é indexado pelo NOME da interface. Nada avisava. Agora é EQ2111, o mesmo erro que o outro caminho até essa situação já dava. Se o seu build parar aqui, essa chamada já não devolvia nada no browser na .54: passe o tipo.

Um Adjustable sem valor se anuncia como group

O ARIA exige que role="slider" traga aria-valuenow, e todo slider que o SDK entregou trazia o papel sem o valor. Agora o papel é derivado do valor. Um Adjustable no papel padrão é um slider quando tem um Value e um group quando não tem, que é o que um contêiner focável ajustado pelas setas é. Um valor em Tablist ou Radiogroup, papéis que não carregam valor, não é emitido.

Adjustable(track, onAdjust, value: new AdjustableValue(now, 0, 1000) { Text = "R$ 400" });
Slider(budget, onBudget, valueText: "R$ 400");

O Slider do SDK já entrega o valor a partir do qual o thumb é desenhado, e valueText troca o número por palavras, porque só o app sabe o que os números dele significam.

Para 0.2.0-preview.54

O que ficou mais simples. A geometria tem uma casa só. Rect, Point e Size eram declarados no motor nativo e copiados quatro vezes para chegar aos sítios que precisavam deles; agora vivem em eQuantic.UI.Primitives, ao lado do resto do vocabulário, portanto um componente, um gráfico e um realizador nomeiam o mesmo tipo. Um pintor recebe essa caixa em vez de quatro floats soltos. E a hierarquia de nós é fechada, o que significa que um realizador a quem falta um caso é um erro de compilação em vez de um nó que silenciosamente não desenha.

Primeiro uma correção à .53, porque saiu errada. A entrada da .53 dizia que um rótulo nativo truncado "agora acaba em …". A marca estava certa e o SÍTIO não: passámos ao CoreText a constante 2 a acreditar que significava truncar-no-fim, e 2 é kCTLineTruncationMiddle. Ou seja, no macOS a .53 cortava os rótulos ao MEIO — "Documentos e Definições" ficava "Docum…nições". A .54 põe as reticências no fim, onde a entrada sempre afirmou que estavam. Encontrado por um consumidor, não por nós.

A geometria mudou para Primitives

Rect, Point, Size, RRect e Matrix2D mudaram de eQuantic.UI.Native.Engine para eQuantic.UI.Primitives. Recompilar a partir do source é a migração toda — os dois assemblies saem no mesmo SDK e na mesma versão — e só um consumidor que segure uma referência compilada é que religa. Sem type forwarders: em preview uma quebra é de graça, e um forwarder é um segundo nome para o tipo de que acabámos de remover quatro cópias.

A mesma mudança, pela mesma razão, para SemanticRole, SemanticCheck e SemanticNode, que saem de eQuantic.UI.Native.Components.

Os operadores do Point são só-do-host, e o RRect e o Matrix2D também

var meio = a + b;            // EQ2010 num componente

O JavaScript não sobrecarrega operadores, portanto a + b em dois Point emitia o + do próprio JavaScript e concatenava dois objetos em "[object Object][object Object]"; a * 2 dava NaN. Fazia isto em silêncio, que é a razão de agora ser um erro de compilação em vez de uma página a mostrar o número errado. No servidor e na track nativa funcionam como antes. Num componente, escreva a aritmética:

var meio = new Point(a.X + b.X, a.Y + b.Y);

RRect e Matrix2D estão cercados inteiros — são o que um rasterizador consome, e uma página que nomeasse Matrix2D estava a emitir um import para um símbolo que o runtime não exporta.

O ICanvasPainter recebe uma caixa e um ponto

void FillRect(Rect box, ColorToken color, float cornerRadius = 0);
void FillCircle(Point center, float radius, ColorToken color);
void Line(Point from, Point to, ColorToken color, float strokeWidth);
Size Size { get; }

Só quem escreveu um pintor PRÓPRIO é que muda alguma coisa. Código que desenha no pintor que nós entregamos fica igual. Os quatro floats eram sempre os quatro que um Rect é, soletrados em cada chamada.

A superfície de rotas do runtime

routeData() e o tipo RouteData desapareceram. RouteValues.from(params, query) e RouteValues substituem-nos, e o getCurrentRoute() continua a existir e responde um RouteValues — um rename e um import. Dois comportamentos mudaram com isso:

  • param e query respondem null onde o TypeScript respondia undefined, porque o Param do C# devolve string?. O ?? não é afetado; uma comparação === undefined é.
  • Uma chave de query repetida responde o PRIMEIRO valor dos dois lados. ?tag=a&tag=b chegava a uma página como "a,b" vindo do servidor e "a" no browser; agora ambos dizem "a".

A superfície de serviços do RenderContext é um método só

Foram-se cinco membros, não um: ServiceProvider, SetGlobalServiceProvider, SetScopedServiceProvider, RegisterService<T> e TryGetService<T>. Fica o GetService<T>(), e devolve T? onde devolvia T.

context.RegisterService(new Clock());        // desapareceu — registe no container da própria app
var clock = context.TryGetService<IClock>(); // desapareceu
var clock = context.GetService<IClock>();    // agora T?, portanto a verificação de nulo é sua

O TryGetService nunca teve gémeo nenhum — o transpilador emitia context.tryGetService(...), um método que o runtime nunca teve, portanto qualquer componente que o chamasse já estava partido no browser.

O parâmetro do Navigator.Go é destination

Navigator.Go(href: "/relatorios");   // deixa de compilar
Navigator.Go("/relatorios");         // continua bem
Navigator.Go(destination: "/relatorios");

Uma chamada posicional fica intacta. Só quebra com argumento NOMEADO, que é o tipo de quebra que o compilador reporta com clareza e que ninguém espera encontrar nas notas de versão.

O BarRect leva um Rect

public sealed record BarRect(int Category, int Series, Rect Box, bool Negative, bool DataEnd);

Eram oito parâmetros posicionais com X, Y, Width e Height soletrados. O construtor gerado, o Deconstruct e essas quatro propriedades foram com a mudança: leia bar.Box.X em vez de bar.X, e construa com um Rect. Só afeta código que lê diretamente a geometria resolvida do gráfico — o BarChart em si fica intacto.

Para 0.2.0-preview.53

O que ficou mais simples. Uma face de código de marca funciona para o tema que a tem — o IAppTheme.MonoFamily saiu na .51 e era inerte precisamente para os temas para que foi feito, por isso se nomeava a face em cada chamada para contornar isso, pode parar. Um link de marcador aterra no mesmo sítio a frio e a quente. Uma folha de cálculo existe no HTML do servidor. E o motor de layout nativo deixou de ter estado mutável, o que não muda nada do que você escreve e muda bastante do que quem o lê tem de segurar na cabeça.

Nada quebra. Nenhuma API se mexeu, nada foi renomeado, e a única mudança de código é dentro do track de layout nativo, cujo único chamador é o próprio SDK.

A face de um papel é um valor por omissão, não uma escolha

O MonoFamily não fazia nada para a forma normal de marcar tipografia:

public TypeStyle Type(TypeRole role) => Base.Type(role) with { Family = "IBM Plex Sans" };

Todos os papéis nomeiam uma face, portanto a face de código do tema encontrava sempre a Family já preenchida e afastava-se, pela regra de que um estilo que NOMEIA uma família fica com ela. Só que nada naquela família tinha sido escolhido para aquele texto — veio do PAPEL. Logo a feature funcionava para um tema que deixa a Family nula e era inerte para um que não deixa, que é ao contrário.

O que muda para si: se nomeou a face de código em cada chamada para contornar isto, essas linhas ficam redundantes, não erradas.

Um carregamento a frio com fragmento aterra onde um quente aterra

A .52 corrigiu isto e a correção não funcionou. A medição que o explica vale a pena guardar:

frio,  loadEventEnd 1310 ms  →  certo
quente, loadEventEnd   36 ms  →  errado

Quanto mais rápida a página, mais fiável a falha. A correção da .52 gastava a única oportunidade no load, e o load pode disparar ANTES de o browser fazer o salto do fragmento. Um visitante que volta é quase toda a gente, e estava sempre na coluna partida.

Agora vigia frames, com limite de orçamento depois de o documento completar e um relógio de parede faça a página o que fizer — e reforma-se quando o leitor navega, para não o seguir para outra página.

Nada a mudar no seu código. Um empurrão de scroll que tenha acrescentado para contornar isto fica redundante.

Uma folha de cálculo existe no HTML do servidor

O SheetSurface não tinha braço no realizador web, portanto o SSR escrevia um <span> vazio onde o browser desenha uma grelha. Desde o dia em que saiu: um crawler nunca via os dados, um leitor sem scripts via um buraco. O servidor escreve agora a grelha, o filho, o role="grid", o nome e um tabindex.

O CodeSurface fica deliberadamente de fora por agora — o cliente acrescenta um caret a cada editor, portanto uma árvore de servidor só com o filho fica com um elemento a menos do que a hidratação espera, e essa forma é uma decisão ainda por tomar.

Uma linha cortada acaba de uma só maneira

O ITextMeasurer promete reticências desde que foi escrito, e uma linha cortada acabava de quatro maneiras diferentes. O CoreText passa a truncar no próprio layout, portanto a largura reportada inclui a marca e os glifos desenhados são essa mesma linha.

Como encontrar: uma etiqueta truncada no macOS acaba em … e mede um pouco mais que antes, porque uma truncagem vai até ao caráter onde uma quebra pára na última palavra inteira. Se guarda goldens de píxeis próprios sobre texto nativo truncado, eles mexem-se uma vez. O DirectWrite continua a cortar sem marca.

Correção, e é a 0.2.0-preview.53 que está errada. Essa versão corta o MEIO de uma etiqueta truncada no macOS — Replace hardco…with IAppTheme — e não o fim. Passámos a constante de truncagem do CoreText 2 com um comentário a chamar-lhe kCTLineTruncationEnd, e o CTLine.h diz que 2 é kCTLineTruncationMiddle. Portanto a marca lá está, no sítio errado, e o realizador web ao lado corta no fim como o design system pede. Corrigido depois da .53, com a afirmação que faltava: duas strings com a mesma cabeça têm de cortar à mesma largura. Só o texto nativo é afetado — a web sempre esteve certa.

LayoutConstraints, se hospeda o motor de layout

Só layout nativo. O LayoutContext deixou de carregar IndeterminateWidth, IndeterminateHeight, StretchWidth e StretchHeight — são factos sobre o que um pai ofereceu a um filho, portanto viajam na constraint do filho. O LayoutEngine.Layout ganhou um rootStretch opcional.

Neutro em comportamento e medido assim: os goldens de píxeis intactos e a alocação oito bytes por frame MAIS BAIXA nos dois caminhos, porque o objeto de contexto tem menos quatro campos.

Para 0.2.0-preview.52

O que ficou mais simples. Uma coisa que pode deixar de contornar: um link de marcador aterra no mesmo sítio quer o leitor o tenha seguido dentro da sua app, quer o tenha colado num separador novo. Se tinha um empurrãozinho de scroll próprio à frente disso, pode sair.

Nada quebra. Nenhuma API se mexeu, nada foi renomeado, e não há aqui comportamento nenhum que tenha de ir procurar — a única mudança é uma correcção que teria notado como leitor.

Um carregamento a frio com fragmento aterra onde um quente aterra

Abrir um link como /privacy#rights num separador novo deixava o alvo DEBAIXO do cabeçalho fixo, enquanto navegar para o mesmo fragmento de dentro da app o punha no sítio certo. Esse par é o diagnóstico inteiro: o scroll-margin-top a ler a altura medida da barra estava certo, e só a ORDEM estava errada.

A barra é medida depois de um passe de render, portanto num carregamento a frio o browser já saltou com a altura ainda por saber. Existia uma correcção exatamente para isso, com uma oportunidade só, que era gasta na MEDIÇÃO e não na correcção — e como o browser volta a correr o salto à medida que conteúdo tardio assenta o layout, essa medição aterrava muitas vezes com o alvo ainda lá em baixo. Nada a corrigir, oportunidade perdida.

Agora gasta a oportunidade na correcção, e marca uma segunda no próprio load do documento, medindo a barra aí em vez de confiar no número que marcou a segunda volta — uma barra que quebra de linha, ou que cresce com uma webfont tardia, é mais alta no load do que na primeira pintura.

Como encontrar: abra um link de marcador para uma página longa num separador novo, com cabeçalho fixo nessa página. O título da secção ficava atrás do cabeçalho; agora fica por baixo dele.

Nada a mudar no seu código. Se acrescentou um empurrão de scroll para contornar isto, fica redundante, não errado.

Para 0.2.0-preview.51

O que ficou mais simples. Um tipo de letra de marca é uma propriedade no seu tema (TypeStyle.Family por papel, IAppTheme.MonoFamily para código) em vez de um StyleOverride em cada chamada, e uma face que a máquina não tem é NOMEADA na linha da execução em vez de substituída em silêncio. O Text.Align funciona no Photon, logo texto centrado deixa de precisar de uma caixa à volta. Um nó disposto conhece o seu Parent, logo "onde estou" deixa de ser uma string de caminho a ser interpretada. E dois mistérios passam a erros de build com remédio: um que só aparecia na hidratação (EQ2010) e um que nomeava um ficheiro gerado que você nunca tinha visto (EQ3006, uma app a adotar o SDK que manteve o seu próprio Main).

Nada foi renomeado. Um membro mudou de forma. Cinco comportamentos mudaram debaixo de código que continua a compilar, e o compilador não ajuda em nenhum deles — por isso cada um diz como o encontrar.

LayoutNode.Children é só de leitura — a única quebra de código

public IReadOnlyList<LayoutNode> Children { get; }   // era List<LayoutNode>

node.Children.Add(child) deixa de compilar. Todo nó disposto carrega agora Parent, recursivamente até à raiz, e anexar é trabalho do motor para que a ligação não possa ser esquecida. Ler não mudou, e foreach (var child in node) — iterar o NÓ, não a lista — não aloca nada nos caminhos por frame.

EQ2010 — um símbolo de host nomeado num componente

Um componente que chamasse FaceName.Usable(...) ou lesse FaceResolution.Unresolved compilava, emitia, e morria na hidratação com "does not provide an export named" enquanto o SSR continuava a responder 200. Agora é erro de build. Mova a chamada para um [ServerAction], uma classe [ServerOnly] ou um realizer. A cerca é por SÍMBOLO, portanto FaceName.IsWellFormed continua a atravessar e Usable não.

Um Mac em modo escuro abre a app em modo escuro

PhotonOptions.Mode = null documenta "segue o sistema" desde que existe, e o shell do macOS respondia Light incondicionalmente — o único alvo de quatro que não honrava isso.

Como encontrar: abra a sua app num Mac com modo escuro ligado. Se dependia do comportamento antigo, diga-o com Mode = ThemeMode.Light em vez de depender de um defeito.

O Photon honra o Text.Align

Os três serviços de texto cortavam o raster justo à linha mais longa e desenhavam cada linha em x=0, portanto o Text.Align não tinha para onde ir e era descartado.

Como encontrar: qualquer Text que definia Align e parecia alinhado à esquerda no Photon move-se agora. Um Row ou uma caixa posicionada à mão que tenha acrescentado para contornar isto passa a centrar duas vezes.

Uma cultura regional cai pelo PAI

O catálogo plano de strings do cliente era dobrado apenas a partir da cultura NEUTRA, logo pt-BR saltava o pt: uma chave traduzida uma vez em Strings.pt.resx voltava em INGLÊS no cliente enquanto o servidor a renderizava em português.

Como encontrar: uma cultura regional cujo pai carrega traduções mostra mais delas agora. As chaves que copiou para o .resx regional para contornar isto ficam redundantes, não erradas.

Variant.Link carrega a tinta em Base e Pressed

O tema tinha ambos transparentes com a tinta em OnBase, e a tinta de pressionado não existia de todo — portanto a troca de texto pressionado que o Button delega não tinha para onde trocar. Um tema ou realizer próprio que leia Colors(Variant.Link).Base recebe agora uma cor. Nada PREENCHE um Link, logo a tinta fica ali sem pintar retângulo nenhum — o que um Link nunca tem é preenchimento, e esse é o slot Subtle.

Estilo web: o papel estiliza a classe, o nó estiliza o elemento

A face, a inclinação, a pilha monoespaçada e o white-space de um papel de código saíram dos estilos inline para a classe .eq-type-*. O lowering do cliente não consegue ler a escala tipográfica do tema, portanto uma propriedade de papel emitida inline pelo SSR era descartada no primeiro re-render — uma discrepância de hidratação, não um detalhe cosmético.

Como encontrar: CSS escrito à mão que sobrepunha um font-family, font-style ou white-space inline no nosso texto compete agora com uma classe em vez de um estilo inline, portanto ganha onde antes perdia. Os controlos de entrada recebem a face por .eq-entry.eq-type-* em vez de um font-family: inherit inline.

Para 0.2.0-preview.50

Nada foi renomeado e nada foi removido. O que esta release mudou foram quatro comportamentos por baixo de código que continua a compilar, e um pacote que passou a ser outra coisa. O compilador só o ajuda no último, portanto cada um dos outros diz como o encontrar.

O IWorkspace.OpenUrl só entrega os esquemas que a sua app abre

Entregar um URL ao sistema operativo é entregá-lo a quem quer que reclame o esquema: https chega a um navegador, file: lança o que o caminho nomeia, e qualquer esquema que outra app tenha registado corre essa app. Um link que chegou em conteúdo não pode escolher entre esses, por isso o OpenUrl consulta agora uma política primeiro. Ela entrega http, https e todo o esquema que você já declarou com builder.Bundle.UrlScheme(…). Tudo o resto é uma linha no Program.cs:

builder.Workspace.OpensMail();                          // mailto:
builder.Workspace.OpensPhone().OpensMessages();         // tel:, sms:
builder.Workspace.Opens("x-apple.systempreferences");   // o esquema de outra app, ou do sistema

Como o encontra: a chamada devolve false e a realização regista um aviso com o esquema e a linha exata que o abriria, portanto o primeiro clique diz-lhe. Não lança exceção, porque a política existe para URLs que a sua app não escreveu. O CanOpen aplica a mesma política, por isso um botão condicionado por ele desaparece exatamente para os links que o OpenUrl recusa.

file: não se abre por opt-in. Use o OpenFile ou o Reveal, que recebem um caminho, verificam que existe e nunca lançam uma pasta. E o builder.Bundle.UrlScheme("acme://") passa a lançar na própria linha, em vez de escrever uma entrada de manifesto que nada casa.

O VisualNode.Key passa a fazer o que sempre disse que fazia

O Key está documentado como identidade do reconciliador desde que o vocabulário existe, e nenhum dos dois realizers o lia. Agora ambos leem, o que é uma correção: uma lista virtualizada que desliza a janela deixa de renumerar os filhos, portanto o anel de foco deixa de sair da linha onde estava.

Como o encontra: torna real um contrato que ninguém verificava. Chaves duplicadas entre irmãos eram inofensivas porque ninguém as lia. Se você constrói a chave a partir dos seus próprios dados, sendo o row.Key de um DataTable a forma a procurar, confirme que é única dentro da lista.

O scroll do foco salta em vez de deslizar

Mover o foco encaixa o alvo à vista. Um deslize não tinha chegado no frame que desenha o anel de foco, portanto um percurso de teclado podia ler o anel como fora do ecrã. A roda e o arrasto continuam a deslizar.

Duas correções de layout que podem mexer em algo

Um stack passa a medir os filhos contra a sua própria caixa, onde antes lhes oferecia as restrições de entrada, portanto um filho Fill num stack de altura fixa media zero dentro de um scroll. Se você fixou uma altura para contornar isso, o remendo pode sair. E um canvas com handlers fica com o ponteiro, onde as camadas de um Stack desligavam o pointer-events e todo o hover caía por baixo.

O eQuantic.UI.Charts é outro pacote

O único erro de compilação aqui, e o que ninguém deve encontrar: nenhuma das três apps que seguem este SDK referenciava esse pacote. Era um pequeno assembly partilhado pelos dois wrappers só-web; passou a ser a biblioteca de gráficos write-once, e os três tipos mudaram-se para os wrappers que os usam.

Se você usava Agora é
eQuantic.UI.Charts.IChart com o wrapper do Chart.js eQuantic.UI.Charts.ChartJs.IChart — troque o using
eQuantic.UI.Charts.IChart com o wrapper do ApexCharts eQuantic.UI.Charts.ApexCharts.IChart — troque o using
ChartData<T>, Dataset Mesmo nome, mesmo namespace; passam a vir dentro do eQuantic.UI.Charts.ChartJs, que você já referencia

Se você usa os wrappers pelos pacotes deles, não tem nada a fazer. Só um projeto que referenciava o eQuantic.UI.Charts diretamente por causa desses tipos tem de se mudar.

Para 0.2.0-preview.49

O eQuantic.UI.Core deixou de existir. Nunca foi um assembly: eram quatro coisas sem relação a partilhar um nome, e toda app web instalava as quatro para usar algumas. Cada parte mudou-se para o seu único consumidor.

O quê Onde vive agora
[Page], [ServerAction], [ServerOnly], [Authorize], [AllowAnonymous] eQuantic.UI.Primitives, ao lado dos outros atributos de contrato
HtmlElement, HtmlNode, HtmlStyle, DynamicElement, IComponent, RenderContext, ClassBuilder eQuantic.UI.Web, onde o DOM é realizado
SeoBuilder, MetadataCollection, AssetCollection, IRequireAssets eQuantic.UI.Server.Metadata / eQuantic.UI.Server.Assets
ImageOptimizationState eQuantic.UI.Images

Comece por assumir que a linha está morta. Num site, 31 de 36 ficheiros que importavam o Core não usavam nada dele. O instinto é procurar para onde cada nome foi; para a maioria dos ficheiros a resposta é lado nenhum. Apague o using, compile, e deixe o compilador nomear os poucos que precisam de algo.

Para esses, é um de quatro casos:

  • O ficheiro também importa eQuantic.UI.Primitives → apague a linha. Foram 45 de 51 ficheiros num site, e 18 de 18 nos samples do próprio SDK.
  • O ficheiro usa só um atributo de contrato e não importa Primitives → troque a linha por using eQuantic.UI.Primitives;.
  • O ficheiro usa um tipo do DOM → troque a linha por using eQuantic.UI.Web;.
  • O ficheiro importa um sub-namespace como eQuantic.UI.Core.Metadata → a tabela acima diz para onde foi. Estes são os fáceis de falhar, porque não se parecem com a linha que você procurou.

Um namespace também pode viver numa string. Se a sua app gera, faz template ou compila C# dela própria, sendo um playground ou um exemplo de código o caso habitual, o compilador não os vê. Procure também nos literais de string.

Três correções que você pode ter contornado. Uma regressão do offset de âncora da .48 está corrigida, portanto remeça quaisquer offsets que tenha afinado contra ela. O [ServerOnly] numa declaração deixa de calar a partial inteira. E um AssemblyName terminado em .App deixa de perder o executável.

Para 0.2.0-preview.48

O modelo de componentes pré-write-once desapareceu. É a maior remoção que o projeto já fez, e a primeira release em que "quebra" quer dizer alguma coisa. Se a sua app está escrita contra o vocabulário write-once — StatelessComponent/StatefulComponent do eQuantic.UI.Primitives, VisualNode, as factories — nada muda para si. Os três samples constroem sem tocar em nada.

O que foi removido, tudo do eQuantic.UI.Core:

Desapareceu Se você usava
StatelessComponent / StatefulComponent sobre HtmlElement, ComponentState, ComponentState<T>, InputComponent<T> Passe ao modelo write-once: o estado e o ciclo de vida vivem no COMPONENTE, portanto uma classe de estado separada vira campos mais OnMount — veja a forma em Architecture
IIconProvider Já não há provedor. Um .svg em Assets/ vira um Vectors.Mark, e um glifo escrito à mão é uma propriedade IconGlyph — veja Ícones
SvgElement HtmlElement("svg", …) na web, ou um Vectors.Mark para algo que tenha de funcionar nos três alvos
ComponentAttribute Não marcava nada que o compilador lesse. Apague
Styling/Colors, Styling/Animations, Styling/EQ Valores CSS como string, que o princípio do produto proíbe. Use ColorToken, Space, TypeRole e os canais de transição — veja Styling

A metade TypeScript foi junto: 260 linhas de core/component.ts, e o export do ComponentState. Um bundle do runtime que você tenha copiado à mão precisa de ser substituído, não remendado.

Também removidas: as três árvores de template não empacotadas em Templates/content/. Ninguém as conseguia scaffoldar (o dotnet new equantic-app sempre usou as write-once), mas estavam no repositório a ensinar o modelo errado a quem abrisse a pasta.

Porquê agora. O SDK está em preview e todos os consumidores são projetos nossos. Fazer isto depois da 1.0 significaria carregar dois modelos de componentes e um compilador que lê os dois, por causa de código que ninguém fora deste repositório alguma vez escreveu.

E três renomeações, porque o vocabulário deixou de falar a língua da web

Estas mordem uma app write-once qualquer, ao contrário de tudo o que está acima. A regra por trás delas é uma que o projeto já tinha e não tinha acabado de aplicar: a camada abstrata nomeia o que uma coisa É, não o que um alvo lhe chama.

Era É Porquê
Sticky Pinned "Sticky" é a palavra do CSS. Um nó que fica no sítio enquanto o contentor rola está fixado em todos os alvos
ZIndex (no Positioned) Layer Um z-index é uma coordenada do CSS. O que o nó declara é em que camada está
Alt (na Image) Label "Alt text" é o nome do HTML para um nome acessível. A mesma string vira accessibilityLabel nos telemóveis e um label de semântica no Photon

O compilador não ajuda aqui: são renomeações, portanto os nomes antigos simplesmente não resolvem. O Sticky é o primeiro a procurar, porque um cabeçalho fixo é a forma que quase toda app tem.

Para 0.2.0-preview.47

Nada quebra. Dito assim porque a release anterior quebrou quatro coisas e a pergunta passou a ser razoável. Tudo abaixo é superfície nova, e o código existente compila sem mudar.

O único comportamento que MUDA sem dar erro de compilação: o bundle .app de uma app macOS passa a vir do dotnet publish em vez da saída do build, e aterra dentro do diretório de publish. Um script que copiava o bundle de bin/<config>/<tfm>/ depois de um publish continua a copiar o de desenvolvimento — aponte-o para bin/<config>/<tfm>/<rid>/publish/.

Se for aqui que liga o PublishTrimmed pela primeira vez, saiba que agora é suportado e antes não era: uma app Photon com trimming abria a janela e ignorava todas as settings, em silêncio. Os avisos de trim são zero no sample; se vir algum do seu próprio código, é seu.

Para 0.2.0-preview.46

Quatro coisas deixam de compilar. As quatro têm correção de uma linha, e nenhuma delas é uma mudança de comportamento no seu próprio código.

Um ícone é um glifo; um filho é um nó

Icons.Heart já não converte sozinho para nó, então um componente que quer um FILHO quer Icon(...) à volta dele:

// antes
new IconButton(Icons.Heart, onPressed: Curtir)
new EmptyState(Icons.Search, "Nada aqui")
SelectedGlyph = Icons.HeartFilled

// depois
IconButton(Icon(Icons.Heart), onPressed: Curtir)
EmptyState(Icon(Icons.Search), "Nada aqui")
SelectedGlyph = Icon(Icons.HeartFilled)

Os erros são CS1503 e CS0029, e um site relatou dez deles.

A conversão foi removida em vez de corrigida porque não dava para fazer funcionar: uma conversão implícita num tipo do framework é DESCARTADA em silêncio quando o componente atravessa para JavaScript — o gêmeo declarava o tipo do glifo e o sítio da chamada emitia a string crua — então a mesma fonte construía uma coisa no Photon e outra na web. Um nó é o mesmo nó nos dois.

A factory gerada resolve capacidades sozinha

Um componente que recebe uma capacidade pelo construtor já não a recebe pela factory: a factory pergunta ao contêiner. Então a aridade cai, e o erro lê-se como se a sua própria superfície gerada tivesse perdido uma sobrecarga:

No overload for method 'ThemeToggle' takes 1 arguments
ThemeToggle(controller)   →   ThemeToggle()

É esse exatamente o objetivo da mudança — um site estava a passar um IThemeController por dois construtores para chegar a um botão — mas o primeiro erro que você encontra não diz isso.

Tipos só-de-servidor precisam de dizê-lo

O compilador caminha mais longe do que caminhava, então um tipo que só corre no servidor e por acaso vive no projeto web passa a falhar com EQ2004 em coisas como CSharpCompilation.Create, Stopwatch.StartNew ou LoggerExtensions.LogInformation. Marque o TIPO:

[ServerOnly]
public sealed class PlaygroundCompilation { … }

O pin do Roslyn passou para 5.9.0

O eQuantic.UI.Compiler fixa Microsoft.CodeAnalysis.CSharp 5.9.0. Um projeto que referencia Microsoft.CodeAnalysis.CSharp.Features para as suas próprias ferramentas tem de passar para 5.9.0 no mesmo commit, ou o restore falha com NU1107 — nomeando o seu projeto de testes e não o SDK.


Antes disso

Releases anteriores não estão cobertas aqui: esta página começa onde começa, em vez de fingir uma história que ninguém registou na altura.

Clone this wiki locally