{{ message }}
Repository navigation
feat(dynamic-island): a full overhaul, with a real fullscreen mode - #171
Open
kellyson71 wants to merge 164 commits into
Open
kellyson71 wants to merge 164 commits into
kellyson71 wants to merge 164 commits into
Conversation
Fase 1: classifica os ids da ilha em CRITICAL/PEEK/LIVE/TOOL como camada aditiva sobre activeIds/interruptIds existentes. O scroll (cycleIds) passa a percorrer só Live Activities + Home; ferramentas fixadas e historico saem da roda do mouse e só abrem por intencao. Pips só aparecem com mais de uma Live Activity ativa. Fase 2: Home (DiXIdle) reescrito para ser so relogio/data/clima + mini player contextual + Tool Dock (Now/Drawer/Clipboard/Agents/More), sem os tiles permanentes de bateria/rede/updates/agentes/F1. ILHA.md documenta o novo modelo.
Hoje um app fullscreen enterra a barra inteira (layer Top vira coberto pelo fullscreen) e o unico sinal que sobra e o hairline passivo do DiFullscreenPeek. Isso ja e Quiet Mode de verdade pra tudo que nao e critico, mas hibernacao iminente/bateria critica/temperatura perigosa ficavam igualmente escondidas atras de uma linha de 3px. Agora esses tres casos sobem a barra pro layer Overlay temporariamente, igual ja acontecia quando o workspace especial fica por cima do fullscreen. O resto continua enterrado normalmente.
DiIdle e DiAgents desenhavam um DiClaudeIcon por entrada de ClaudeCode.openAgents. Com dois tipos de agente abertos (ex: claude e codex) isso rendia dois marcadores quase identicos lado a lado, lidos como duplicacao em vez de "dois agentes". Agora mostra so o agente mais relevante (esperando > trabalhando > primeiro) com um badge "+N" quando ha mais de um, no lugar de repetir o icone. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
Causa real do "Claude duplicado": "activity" (a tarefa especifica, ex. "Lendo DiExpanded.qml") e "agents" (o resumo geral) sao dois ids Live separados, e ambos ficavam ativos ao mesmo tempo pro mesmo agente - um na pilula principal, outro no deck atras dela. Mesma sessao, dois icones. Agora "agents" so entra em activeIds quando nenhuma activity atual ja e de um agente (claude/codex/gemini). A tarefa especifica sempre vence por ser mais informativa; o resumo geral volta quando nao ha nada especifico rodando. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
Duas Tools/Activities lado a lado, sempre por acao explicita: o botao "splitscreen" no pager arma a escolha, o proximo pip vira o parceiro do split em vez de substituir a view atual. X no canto fecha so o painel do split. Nunca abre sozinho. Arquitetura em DynamicIsland.qml (splitId/splitArmed/toggleSplitArm/ selectForSplit/exitSplit, limpo no collapse() e invalidado junto com expandedOverride quando o switcherIds muda) e o layout lado a lado em DiExpanded.qml (par detail+splitDetail centralizado, divisor fino, wantedW/wantedH somando os dois quando ativo). Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
Handler IPC novo "island dev <id> <titulo> <subtitulo> <estado> <url>" em IslandEvents.qml, reaproveitando o upsertActivity generico que ja existia (mesmo usado por Terminal Activity). "ready" fica no estado "running" (anel cheio, sem spin) em vez de "done", porque um servidor dev precisa persistir como Live por horas, nao sumir em 6s como uma tarefa concluida. scripts/island/dev-island.sh: wrapper de pipe framework-agnostico - so procura uma URL localhost e palavras de erro na saida, sem entender nenhuma ferramenta especifica. Faz poll da porta pra manter a activity viva e remove quando o servidor sai do ar ou o comando encerra. Testado com input simulado e contra a instancia ao vivo via IPC direto (qs ipc call island dev/remove). Sem botao de acao na expandida ainda (mesmo nivel que Terminal Activity hoje); suporte por framework fica pra extensao futura. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
IslandEvents.focusOn/toggleFocus(): enquanto ligado, notificacao nao critica nunca vira Peek (isMuted ganhou o check, continua puro - sem side effect em binding). A contagem de quantas foram suprimidas vem de um Connections.onNotify separado (imperativo, seguro pra side effect). Ao desligar mostra um resumo "Focus finished - N min - M waiting" como Live Activity, mesmo padrao ja usado pelo caffeine (upsertActivity + timer de 20s refrescando enquanto ativo). Chip em Home -> More, ao lado de Stay awake/Mute notifications. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
…tus uniforme - Clicar numa capsula lateral abre a expandida dela (antes so trocava de lugar com a principal - por isso a gaveta "nao abria"). - Capacete da F1 na Home compacta ganhou acao (abre a F1). - agents/clipboard/zerotier em standaloneViews: abertos pelo dock, a expandida nao volta mais sozinha pra Home. - Privacidade: ponto fora da pilula, a esquerda, com halo lento e um ripple unico ao ligar - nao cobre mais o relogio/data. - Home compacta: StatusGlyph uniforme (22px, tom neutro, cor so em alerta, contagem como badge, hover circular, tudo clicavel). Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
… uniform status tray
A segunda pilula brota da borda da principal (pescoco que afina e solta), fica ao lado mostrando qualquer ilha escolhida - ativa ou nao (Sistema, Gaveta, Agentes, ZeroTier, Historico, F1, Midia). Scroll em cima troca a ilha com rolagem tipo caca-niquel; arrastar pra longe joga fora; empurrar de volta reabsorve; clique do meio descarta; o X so aparece no hover. Com o split na tela somem capsulas/deck/chips. Entradas: "+" que aparece ao lado no hover, clique direito na pilula e botao fixo no canto da expandida, que abre um seletor com todas as candidatas. O mesmo splitId alimenta a expandida lado a lado e agora sobrevive ao collapse. IPC novo: island split <id>. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
Uma linha tocavel por ilha ativa (e a F1 quando a proxima sessao esta a menos de 3 dias), com icone, nome, valor curto e entrada escalonada; toque abre a tela dela. Cabecalho alinhado a esquerda com a lista, dock com colunas iguais (o ColumnLayout do botao herdava max de 44px e nao crescia), rotulos em pt-BR e contagem da F1 legivel. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
Gaveta compacta: o leque de miniaturas abre no hover (espacamento e rotacao com OutBack). Gaveta expandida: cada arquivo e distribuido em sequencia, caindo inclinado e endireitando ao assentar, e sobe um pouco no hover. Pilula principal achata de leve quando expele a segunda pilula e de novo quando reabsorve - a mitose fica fisica em vez de so geometrica. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
A barra centraliza a ilha, entao o hover alargava dos dois lados e o que estava na borda (pular faixa, relogio, capsula) fugia de baixo do cursor no meio do clique. Agora o lado por onde o ponteiro entrou fica parado: a ilha e deslocada por metade do quanto cresceu, e volta junto com a largura (mesma curva) quando o ponteiro sai. No modo material a superficie da barra acompanha via GlobalStates.islandGrowShift. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
services/WatchRating.qml le o MPRIS do navegador (Netflix/Disney+), consulta a OMDb com cache por serie e por temporada (uma temporada = uma chamada) e calcula nota do episodio, posicao na temporada e se e o melhor. Peek "watchRating" uma vez por episodio: nota contando ate o valor, coroa e um brilho atravessando a pilula no melhor da temporada. Expandida: temporada em barras que crescem em sequencia, atual destacado, melhor em dourado, hover mostra o episodio. O episodio vem de scripts/island/watch-rating.user.js (Tampermonkey), que escreve serie/temporada/episodio nos metadados de midia da pagina; o Chrome sozinho so publica "Serie | Disney+". Sem o script, mostra a nota da serie. A chave da OMDb fica em ~/.config/illogical-impulse/ omdb.key, fora do repositorio. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
services/SmartDrop.qml classifica o que esta sendo arrastado (imagem, PDF, arquivo compactado, codigo, link, texto, varios) e diz quais acoes fazem sentido; a primeira e o padrao. Ao arrastar, a pilula vira uma zona por acao e um destaque desliza pra zona sob o cursor; soltar executa a acao daquela zona e a ilha diz o que fez. Caminhos e texto vao como argv separado pro sh, nunca interpolados: testado com nomes contendo aspas e $(...) sem executar nada. Claude-Session: https://claude.ai/code/session_01DtMxTe84DyvRGVR1WWP3tk
- resource rings (and their numbers) turn tertiary 15 points before the limit and error past it; the CPU temperature gets a limit too (85 °C) - utility buttons rest as a soft cookie shape that turns as it opens into the filled pill on hover, and squeeze under a press; fixes the undefined easing warning they logged
Dev notes, the earbuds case art and the ZeroTier backend/views become optional local files. Tracked code loads them by URL and hides the related UI when they are missing; caseArtDevices now defaults to empty.
…ttons - the pill widens on hover to fit the title whole (up to 360 px) and keeps the artist next to the controls - a title that still doesn't fit glides across instead of being cut with an ellipsis - fixes the binding loop in the pill's width (it measured the trailing item's laid-out width, which depends on the pill itself; now its natural width)
The highlight only moved while playing, so opening the player paused (or after a reload) showed every line dimmed as if there were none. It now syncs when the lyrics arrive, on pause/resume and when the view opens.
- Python: run it in a terminal that stays open, or open it in VS Code from ~/Documentos/snippets - shell: run it in the terminal; media links (YouTube, Instagram, TikTok, WhatsApp media, direct audio/video): download video or audio with yt-dlp into the drawer; a short title: its IMDb rating on demand - a file path: open its folder - the actions wrap onto two lines, this kind's shortcuts first, instead of a single row that cut them off; code shows in a monospace font, and multi-line clips keep their line breaks
Jellyfin's web player names its media session "Series - S1:E5 - Episode title (2025)"; that is read as series, season and episode, no userscript needed. Adds the Jellyfin mark.
The mic/camera dots beside the island breathed and rippled for as long as something was in use, which read as an alert for a state that is normal (a call, a recording). They are now two still dots; "Animate privacy indicators" in the bar settings brings the halo and the ripple back.
While an external monitor is plugged in, "display" stays in the scroll cycle so its layout (extend / mirror / external only) is always one scroll away, not only in the few seconds after the cable goes in. It is zero priority: it never takes the pill from home on its own. The monitor view is redone as a stage: both screens standing on one desk line, the model/resolution/refresh in the header, and the layout picker full width. The old side-by-side layout let the external screen spill into the column next to it and needed a scroll at the standard height. The same view is shared with the "monitor connected" moment through an overridable payload/action. It can be turned off with "Second screen as a Live Activity".
Home kept a row of unrelated glyphs with number badges (an update count that overflowed its icon) and a faded date next to the clock. Between the photo and the time there is now a two-line glance that shows only the one thing worth reading: an agent waiting on you, the latest unread notification (app colour, subject, count), an agent at work with its 5h usage, pending updates past the strong threshold, or else the day and the weather. Whatever the glance did not pick stays as a quiet glyph without a number; unread notifications also put a dot on the photo. The width is measured from the text rather than from visibility: a hidden copy of home reported a narrower width and clipped the clock.
…the island - The region went to wf-recorder in screen-relative coordinates. On any monitor not at the layout origin that is a place with no output, so wf-recorder died right after "Starting recording". It now gets global coordinates, with an even size (yuv420p rejects odd ones). - record.sh wrote record.enable into states.json behind Persistent's back and Persistent wrote its own copy over it, so the island never saw the recording and the shortcut opened a new selector instead of stopping. It now reports through `qs ipc call recorder setRecording`, falling back to the file when the shell is not running. - A click that selects nothing records the whole screen instead of aborting and then touching the destroyed selector. - If the recorder never reports that it started, the region outline no longer stays on screen with no way out. - The recording dot in the island's anchor stops the recording; the recorder's own notices no longer show on the island, and the recording pill's label no longer fails on a missing import.
kellyson71
force-pushed
the
feat/dynamic-island-overhaul
branch
from
September 29, 2026 13:00
2649c2e to
3db0e5f
Compare
kellyson71
marked this pull request as ready for review
September 29, 2026 13:02
kellyson71
marked this pull request as draft
September 30, 2026 13:11
… in the player - expanded cards are as tall as what they show (up to the usual maximum), so a near-empty view like an empty drawer no longer sits in a full-height card; the uniform height is now an option (bar.dynamicIsland.uniformHeight, in the island settings) - the player gets a lyrics button next to like: it flips the same setting as the island settings, and with lyrics off the card is just the player, in one column
The green outline around the whole pill now only marks the drop target while something is being dragged; the confirmation after the drop is a small tonal check that fades in, without the popping solid badge.
…e-column player - settings instead of a button in the player: lyrics on the compact island, lyrics in the expanded player, and tinting the island with the song's colours (the pill and the player), each on its own; lyrics are only fetched when shown somewhere - with the lyrics column off the player is a real one-column card (~400 px): seek bar and controls across the full width instead of a narrow column stranded in a wide card
… when clearing - empty, the drawer drops its header (the empty state says it all) and shows one centred block: the icon in a tonal shape, the title, and the hint on two lines - the armed "Clear all → Confirm" chip uses Material 3's error-container pair instead of dark red on a dark red tint
A hugging card only had the pager strip at the bottom, so its content sat high: tight on top, a gap below. It now gets a matching strip on top, centring the content between the top edge and the dots.
…sport horizontally - the empty drawer's column was only as wide as its widest child (the hint, capped at 280 px), so it sat left of centre; its texts now fill the width with centred text - without the lyrics column, prev/play/next sit centred, with a stand-in as wide as the like button on the other side so it doesn't push them off-centre
Author
kellyson71
marked this pull request as ready for review
September 30, 2026 13:40
The overlay window stayed mapped at rest (full width, 700 px tall, one per monitor). Animations inside its hidden compact ghost and views still dirtied it, so it was re-rendered and committed every frame whenever an agent was working: ~160 commits/s across two monitors, down to ~20 with this change. The bar pill now hides only once the surface is actually on screen.
Unpinned files leave the drawer shelfExpireHours after they were last added, opened, dragged or pinned, and do not survive a reboot (shelfClearOnBoot). Pinned ones stay, also through "Clear all". A single-shot timer aimed at the next expiry replaces the hourly sweep, so nothing ticks while the drawer is empty or fully pinned. Tiles show the time left and a pin button. Replaces shelfExpireDays.
Downloading from a copied link wrote into the drawer's hidden state folder, so the file was out of reach once the item left the drawer. It now goes to an "Island" folder inside Downloads (linkDownloadDir) and the drawer only points at it. scripts/island/link_download.py is the single entry point and reports through a small line protocol (TITLE/ISLAND/FILE/ERROR): - Spotify tracks, albums and playlists and Deezer tracks are matched on YouTube Music from the titles on their public pages. - Posts holding only pictures fall back to the public tweet endpoint for X, then to gallery-dl when it is installed. - Direct files (PDF, images, archives) are fetched as they are, and small Google Drive files that are not videos too. - Albums and playlists get their own subfolder, up to 100 items. The clipboard card reads the kind of link from linkDownloadSites, which adds Reddit, Bluesky, Pinterest, Imgur, Tumblr, Kick, Twitch VODs, Bandcamp, Mixcloud, Streamable, Telegram and others, plus a "Try to download media" action for the rest of the web. Downloads queue instead of being dropped while one is running, failures say why, and video prefers h264/aac so it plays where it is sent next. Also reachable from outside: qs ipc call island download <url> <mode>.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.





An overhaul of the Dynamic Island that grew out of my daily setup: more islands (notifications, devices, battery, system pressure, media, timers, downloads, F1, terminal activity), an expanded view for each one, and a proper fullscreen mode. Everything is optional and can be turned off in Settings → Bar → Dynamic Island, and all services are passive (no background polling).
What's new in this update
wf-recorderin screen-relative coordinates, so on any monitor not at the layout origin it died right after "Starting recording".record.shnow also reports through IPC instead of editingstates.jsonbehindPersistent, which overwrote it: the island shows the recording and the shortcut stops it. The recording dot in the island stops it too.Home: day and weather
Home: unread message in the glance
Second screen, compact
Still privacy dots (mic, camera)
Second screen, expanded
Screenshots
Fullscreen
Compact
Expanded
Settings: every feature says what it costs while it's on, and turning it off stops the work, not just the drawing.
Notes
qs ipc call island quiet(or a middle click on the hairline) silences it until fullscreen ends. Games start quiet.HyprlandData.monitors[].activeWorkspacewent stale on workspace switches (only workspaces were refreshed), which kept the bar hidden after leaving a fullscreen workspace.BarContentbuilt both layouts and hid one, so every widget, including the island, existed twice. Only the visible layout is built now.leftWidget/rightWidgetbeside the island keep working.scripts/island/. Translations only for pt_BR.Testing
qs -c end4-pC ipc call island simulate <bluetooth|headphonesLow|audioOutput|batteryLow|charging|osd|timer|download|recording|lights|flag|f1Fastest|songRec>qs -c end4-pC ipc call hardware simulate <diskLow|monitor|dock|drive|charger>qs -c end4-pC ipc call island open <system|calendar|audioOutput|f1|download|shelf|privacy|display>activeWorkspacebug showed up.notify-send -a WhatsApp/Telegram/Discordfor the message island, group chats and the discreet line in fullscreen.