Skip to content

Let extensions actually ask the user something - #41

Merged
Seungpyo1007 merged 1 commit into
developfrom
feature/ext-prompts
Aug 4, 2026
Merged

Seungpyo1007 merged 1 commit into
developfrom
feature/ext-prompts

Conversation

@Seungpyo1007

Copy link
Copy Markdown
Contributor

Three shim functions ask the user something. All three answered without asking:

showQuickPick: () => Promise.resolve(undefined),
showInputBox:  () => Promise.resolve(undefined),
showInformationMessage: (msg, ..._items) => { toast(...); return Promise.resolve(undefined); },

In the VS Code contract undefined means the user cancelled. So an extension concluded it had been cancelled without a prompt ever appearing, and dropped whatever it was doing — no error, no toast. The third is the worst of them, because a toast does appear, so something looks like it happened. Meanwhile the near-universal

if (await vscode.window.showInformationMessage(msg, "Reload") === "Reload") {  }

was permanently false.

What it does now

All three open a real dialog. A pick returns the item the extension passed in, not a copy — extensions usually branch on a field they attached themselves. Supported: canPickMany, item arrays given as a Promise, matchOnDescription / matchOnDetail, password, and validateInput.

validateInput also runs when the dialog opens. An extension's initial value can already violate its own rule, and with no marking you have to press OK to find out why OK does not work.

A message with no buttons stays a toast. A notification should not block the flow — that distinction is the whole reason VS Code overloads one function.

Every prompt names the extension asking. If a dialog an extension opened reads as one the app opened, uninstalling the extension leaves you suspecting the app.

The filtering logic — normalize, match, cursor, validate — is a pure module with 26 tests.

Also: overlay z-index now comes from the table

The overlay table says a render's zIndex literal must equal its z. Saying it in a comment was not enough, and two had drifted: the confirm dialog is 233 in the table but rendered at 231, below the bundle installer at 232; the tour is 245 but rendered at 240, the same layer as the import dialog. Esc closes top-down by the table while the eye sees the opposite order — you press Esc at the dialog in front and the answer goes to the one hidden behind it. The render now reads overlayZ(id), so there is nowhere left for them to drift.

Verification

A real VS Code extension planted in the extensions folder, calling through require("vscode") — 18 of 18:

  • a pick appears, names the extension, filters on description when asked to, and returns the original object ({label:"파랑", description:"cool", id:2})
  • canPickMany returns ["a","c"] after Space-toggling two rows
  • items handed over as a Promise resolve and are selectable
  • showInputBox starts with the extension's value, shows the validation message straight away, refuses to close while invalid, clears the message when fixed, returns the typed string
  • showInformationMessage(msg, "Reload", {title:"Later", isCloseAffordance:true}) renders both buttons and returns "Reload"
  • a message with no buttons opens no dialog and resolves to undefined
  • Esc closes the prompt and the extension receives undefined

716 tests, npm run typecheck clean.

Measured against the dev bundle — the harness needs localStorage to seed a workspace and file:// blocks it. Verification also required #40; without it the probe extension could not load at all.

셰임에서 묻는 함수는 셋인데 셋 다 묻지 않고 곧장 답했다.

  showQuickPick: () => Promise.resolve(undefined),
  showInputBox:  () => Promise.resolve(undefined),
  showInformationMessage: (msg, ..._items) => { toast(...); return undefined; },

vscode 규약에서 undefined 는 "사용자가 취소했다" 다. 그래서 확장은 물음을 띄운
적도 없이 취소당한 줄 알고 흐름을 접었다. 오류도 토스트도 없다. 특히 세 번째가
고약한데, 토스트는 뜨니까 뭔가 일어난 것처럼 보인다 — 정작
`if (await showInformationMessage(m, "Reload") === "Reload")` 는 영원히 거짓이다.

세 물음을 실제 대화창으로 만든다. 고른 항목은 **확장이 넘긴 값 그대로** 돌려준다
(확장은 대개 자기가 붙인 필드를 보고 다음을 정한다). canPickMany, Promise 로 넘긴
항목 목록, matchOnDescription/Detail, password, validateInput 을 지원한다.

검사기는 열 때도 한 번 돌린다. 확장이 준 초기값이 이미 규칙에 어긋날 수 있는데,
그때 아무 표시가 없으면 멀쩡해 보이는 값에 확인을 눌러야 왜 안 되는지 알게 된다.

버튼 없이 부른 알림은 그대로 토스트다 — 알림은 흐름을 막을 일이 아니다.

물음은 늘 누가 묻는지 머리에 밝힌다. 확장이 띄운 창을 앱이 띄운 것으로 오해하면
확장을 지운 뒤에도 앱을 의심하게 된다.

고르는 로직(정규화·필터·커서·검증)은 순수 모듈로 빼서 26개 테스트로 덮는다.

곁들여, 오버레이 zIndex 를 표에서 읽어 가게 했다. "렌더 리터럴과 같아야 한다" 고
주석으로만 적어 뒀더니 실제로 어긋나 있었다 — 확인창은 표에서 233 인데 231 로
그려져 번들 설치(232) 아래였고, 투어는 245 인데 240 이라 가져오기와 같은 층이었다.
Esc 는 표 순서대로 위엣것부터 닫는데 눈에 보이는 순서는 반대가 되는 자리다.
@Seungpyo1007 Seungpyo1007 added this to the v0.2 (2026-08-31) milestone Aug 4, 2026
@Seungpyo1007 Seungpyo1007 added enhancement New feature or request ecosystem Skills, connectors, MCP, plugins labels Aug 4, 2026
@Seungpyo1007 Seungpyo1007 self-assigned this Aug 4, 2026
@Seungpyo1007
Seungpyo1007 merged commit 338eeda into develop Aug 4, 2026
1 check passed
@Seungpyo1007
Seungpyo1007 deleted the feature/ext-prompts branch August 4, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ecosystem Skills, connectors, MCP, plugins enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant