-
Notifications
You must be signed in to change notification settings - Fork 2
Upgrading pt BR
🌐 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.
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.
using eQuantic.UI.Primitives; // CodeDocument, CodeEditorController, CSharpLanguage… não estão mais aqui
using eQuantic.UI.Code; // acrescente esteTrinta 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(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, 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.
| 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.
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.
Um app que ligava /_equantic/equantic.css pelo ConfigureHtmlShell remove o link: a folha gerada
já vai inline.
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çaFormatExceptiononde respondia 12, e umTryParseque falha deixa 0, nãoNaN. - Uma divisão inteira ou um resto por zero lança
DivideByZeroExceptiononde a página mostravaInfinityouNaN. -
x += 1numint?nulo continua nulo onde virava 1. - Uma chave ausente num
SortedDictionary, numSortedListou numDictionarycom chave de valor lançaKeyNotFoundException, e umTryGetValueque não acha escrevedefault. -
true.ToString()éTrue. -
int x = defaulté 0 enew long[3]guarda longs, onde eramundefinede números comuns, e um filtro escritoo.Status != default(OrderStatus)compara com o zero do enum, onde todo item passava.
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.
using eQuantic.UI.Primitives; // PhotonEntitlements, AppCategory… não estão mais aqui
using eQuantic.UI.Native.Hosting; // acrescente estePhotonCapabilityAttribute, 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.
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.
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.
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.
-
UI.Glyph(glyph)viraUI.Icon(glyph).UI.Iconenew Iconrecebem umIconGlyph, com uma conversão implícita a partir deIcons, entãoUI.Icon(Icons.Check)fica igual. -
EmailRenderer.Render(component, theme)liga na forma deVisualNode, que um componente já é, com os mesmos parâmetros. -
WebRealizer.Lower(node, theme, scale, styles)é um método só, comStyleSink? styles = null. -
CodeWriter.BeginScope(line, opener, closer)saiu; a forma de abertura e fechamento fica.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 container = row; // EQ2010 num componenteO 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.
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.
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.
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.
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.
var meio = a + b; // EQ2010 num componenteO 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.
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.
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:
-
paramequeryrespondemnullonde o TypeScript respondiaundefined, porque oParamdo C# devolvestring?. O??não é afetado; uma comparação=== undefinedé. - Uma chave de query repetida responde o PRIMEIRO valor dos dois lados.
?tag=a&tag=bchegava a uma página como"a,b"vindo do servidor e"a"no browser; agora ambos dizem"a".
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 é suaO 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.
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.
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.
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.
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.
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.
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.
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.53que 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 CoreText2com um comentário a chamar-lhekCTLineTruncationEnd, e oCTLine.hdiz 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 sistemaComo 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 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.
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.
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 ú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.
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.
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.
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.
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.
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.
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.
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.
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 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.
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.
🌐 English · Português
🏁 Comece aqui
🏗️ Arquitetura
- Visão geral da arquitetura
- Componentes write-once
- Superfície declarativa
- Arquitetura de pacotes
- Componentes
- Estilo
- Localização
- Analytics e GTM
📱 Write-once
- Motor Photon
- Design System
- Capacidades
- Armazenamento
- Formulários
- Editor de código
- Markdown
- Mermaid
- Renderização de Email
⚙️ Compilação
⚡ Runtime
🔌 Servidor
🎨 Ecossistema
🚀 Desenvolvimento