pinokiod 7.5.13 → 7.5.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.5.13",
3
+ "version": "7.5.14",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,6 +22,7 @@
22
22
  const DRAFT_TITLE_NOISE_PATTERN = /^\s*(?:<<PINOKIO_SHELL>>|={6,}|-{6,}|\[api\s+local\.set\]|The default interactive shell is now|To update your account|For more details, please visit)/i
23
23
  const ASK_AI_DEFAULT_PROMPT = 'Investigate what went wrong. Inspect the app logs and explain the likely root cause and next fix.'
24
24
  const TOOL_PREFERENCE_KEY = 'pinokio.universalLauncher.tool'
25
+ const ASK_AI_TOOL_CATEGORY_ORDER = ['Terminal', 'Desktop']
25
26
  const textEncoder = typeof TextEncoder === 'function' ? new TextEncoder() : null
26
27
 
27
28
  const safeJsonParse = (value) => {
@@ -152,8 +153,10 @@
152
153
  installed: typeof plugin.installed === 'boolean' ? plugin.installed : null
153
154
  }
154
155
  }).filter(Boolean).sort((a, b) => {
155
- const categoryDelta = String(a.category || '').localeCompare(String(b.category || ''), undefined, { sensitivity: 'base' })
156
+ const categoryDelta = askAiToolCategoryRank(a.category) - askAiToolCategoryRank(b.category)
156
157
  if (categoryDelta !== 0) return categoryDelta
158
+ const categoryNameDelta = String(a.category || '').localeCompare(String(b.category || ''), undefined, { sensitivity: 'base' })
159
+ if (categoryNameDelta !== 0) return categoryNameDelta
157
160
  return String(a.label || '').localeCompare(String(b.label || ''), undefined, { sensitivity: 'base' })
158
161
  })
159
162
  }
@@ -170,6 +173,12 @@
170
173
  return `${label} (${category})`
171
174
  }
172
175
 
176
+ const askAiToolCategoryRank = (category) => {
177
+ const normalized = String(category || '').trim().toLowerCase()
178
+ const index = ASK_AI_TOOL_CATEGORY_ORDER.findIndex((item) => item.toLowerCase() === normalized)
179
+ return index >= 0 ? index : ASK_AI_TOOL_CATEGORY_ORDER.length
180
+ }
181
+
173
182
  class PrivacyFilterClient {
174
183
  constructor() {
175
184
  this.worker = null
@@ -695,9 +704,11 @@
695
704
  toolPicker.className = 'universal-launcher-tool-picker logs-ask-ai-tool-picker'
696
705
  toolSection.appendChild(toolPicker)
697
706
 
698
- const toolTrigger = document.createElement('div')
707
+ const toolTrigger = document.createElement('button')
708
+ toolTrigger.type = 'button'
699
709
  toolTrigger.className = 'universal-launcher-tool-trigger has-value logs-ask-ai-tool-trigger'
700
- toolTrigger.setAttribute('aria-hidden', 'true')
710
+ toolTrigger.setAttribute('aria-haspopup', 'listbox')
711
+ toolTrigger.setAttribute('aria-expanded', 'false')
701
712
  toolPicker.appendChild(toolTrigger)
702
713
 
703
714
  const toolIcon = document.createElement('span')
@@ -721,10 +732,51 @@
721
732
  toolCaret.setAttribute('aria-hidden', 'true')
722
733
  toolTrigger.appendChild(toolCaret)
723
734
 
724
- const toolSelect = document.createElement('select')
725
- toolSelect.className = 'logs-ask-ai-tool-select'
726
- toolSelect.setAttribute('aria-label', 'Agent')
727
- toolPicker.appendChild(toolSelect)
735
+ const toolSheetLayer = document.createElement('div')
736
+ toolSheetLayer.className = 'universal-launcher-tool-sheet-layer logs-ask-ai-tool-sheet-layer'
737
+ toolSheetLayer.hidden = true
738
+ panel.appendChild(toolSheetLayer)
739
+
740
+ const toolSheetBackdrop = document.createElement('button')
741
+ toolSheetBackdrop.type = 'button'
742
+ toolSheetBackdrop.className = 'universal-launcher-tool-sheet-backdrop'
743
+ toolSheetBackdrop.setAttribute('aria-label', 'Close agent selection')
744
+ toolSheetLayer.appendChild(toolSheetBackdrop)
745
+
746
+ const toolSheet = document.createElement('section')
747
+ toolSheet.className = 'universal-launcher-tool-sheet logs-ask-ai-tool-sheet'
748
+ toolSheet.setAttribute('aria-label', 'Choose agent')
749
+ toolSheetLayer.appendChild(toolSheet)
750
+
751
+ const toolSheetHeader = document.createElement('div')
752
+ toolSheetHeader.className = 'universal-launcher-tool-sheet-header'
753
+ toolSheet.appendChild(toolSheetHeader)
754
+
755
+ const toolSheetHeading = document.createElement('div')
756
+ toolSheetHeading.className = 'universal-launcher-tool-sheet-heading'
757
+ toolSheetHeader.appendChild(toolSheetHeading)
758
+
759
+ const toolSheetTitle = document.createElement('div')
760
+ toolSheetTitle.className = 'universal-launcher-tool-sheet-title'
761
+ toolSheetTitle.textContent = 'Choose agent'
762
+ toolSheetHeading.appendChild(toolSheetTitle)
763
+
764
+ const toolSheetDescription = document.createElement('div')
765
+ toolSheetDescription.className = 'universal-launcher-tool-sheet-description'
766
+ toolSheetDescription.textContent = 'Launch this report with a local agent.'
767
+ toolSheetHeading.appendChild(toolSheetDescription)
768
+
769
+ const toolSheetClose = document.createElement('button')
770
+ toolSheetClose.type = 'button'
771
+ toolSheetClose.className = 'universal-launcher-tool-sheet-close'
772
+ toolSheetClose.setAttribute('aria-label', 'Close agent selection')
773
+ toolSheetClose.innerHTML = '<i class="fa-solid fa-xmark" aria-hidden="true"></i>'
774
+ toolSheetHeader.appendChild(toolSheetClose)
775
+
776
+ const toolSheetBody = document.createElement('div')
777
+ toolSheetBody.className = 'universal-launcher-tool-sheet-body logs-ask-ai-tool-sheet-body'
778
+ toolSheetBody.setAttribute('role', 'listbox')
779
+ toolSheet.appendChild(toolSheetBody)
728
780
 
729
781
  const footerActions = document.createElement('div')
730
782
  footerActions.className = 'universal-launcher-footer-actions'
@@ -737,14 +789,45 @@
737
789
  footerActions.appendChild(runButton)
738
790
 
739
791
  let returnFocusEl = null
792
+ let toolSheetOpen = false
793
+ const closeToolSheet = (options = {}) => {
794
+ toolSheetOpen = false
795
+ toolSheetLayer.hidden = true
796
+ toolPicker.classList.remove('open')
797
+ toolTrigger.setAttribute('aria-expanded', 'false')
798
+ if (options.focusTrigger !== false) {
799
+ toolTrigger.focus()
800
+ }
801
+ }
802
+ const openToolSheet = () => {
803
+ if (!Array.isArray(this.askAiTools) || this.askAiTools.length === 0) {
804
+ return
805
+ }
806
+ toolSheetOpen = true
807
+ toolSheetLayer.hidden = false
808
+ toolPicker.classList.add('open')
809
+ toolTrigger.setAttribute('aria-expanded', 'true')
810
+ window.requestAnimationFrame(() => {
811
+ const selected = toolSheetBody.querySelector('.universal-launcher-tool.selected')
812
+ const first = toolSheetBody.querySelector('.universal-launcher-tool')
813
+ const target = selected || first || toolSheetClose
814
+ if (target && typeof target.focus === 'function') {
815
+ target.focus()
816
+ }
817
+ })
818
+ }
740
819
  const syncRunState = () => {
741
820
  runButton.disabled = !String(promptTextarea.value || '').trim() || !this.selectedAskAiTool()
742
821
  }
743
822
  const getFocusable = () => {
744
- return Array.from(panel.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), select:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'))
823
+ const scope = toolSheetOpen ? toolSheetLayer : panel
824
+ return Array.from(scope.querySelectorAll('a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'))
745
825
  .filter((node) => node && !node.hidden && node.offsetParent !== null)
746
826
  }
747
827
  const setOpen = (isOpen) => {
828
+ if (!isOpen) {
829
+ closeToolSheet({ focusTrigger: false })
830
+ }
748
831
  overlay.hidden = !isOpen
749
832
  document.documentElement.classList.toggle('universal-launcher-open', isOpen)
750
833
  document.body.classList.toggle('universal-launcher-open', isOpen)
@@ -771,6 +854,8 @@
771
854
  const tool = this.selectedAskAiTool()
772
855
  toolLabel.textContent = tool ? tool.label : 'Choose agent'
773
856
  toolMeta.textContent = tool ? (tool.category || 'Plugin') : ''
857
+ toolTrigger.classList.toggle('has-value', Boolean(tool))
858
+ toolTrigger.setAttribute('aria-label', tool ? `Agent: ${askAiToolOptionLabel(tool)}` : 'Choose agent')
774
859
  toolIcon.textContent = ''
775
860
  while (toolIcon.firstChild) {
776
861
  toolIcon.removeChild(toolIcon.firstChild)
@@ -780,6 +865,15 @@
780
865
  icon.src = tool.iconSrc
781
866
  icon.alt = ''
782
867
  icon.className = 'logs-ask-ai-tool-trigger-image'
868
+ icon.onerror = () => {
869
+ while (toolIcon.firstChild) {
870
+ toolIcon.removeChild(toolIcon.firstChild)
871
+ }
872
+ const fallbackIcon = document.createElement('i')
873
+ fallbackIcon.className = 'fa-solid fa-robot'
874
+ fallbackIcon.setAttribute('aria-hidden', 'true')
875
+ toolIcon.appendChild(fallbackIcon)
876
+ }
783
877
  toolIcon.appendChild(icon)
784
878
  } else {
785
879
  const icon = document.createElement('i')
@@ -787,6 +881,11 @@
787
881
  icon.setAttribute('aria-hidden', 'true')
788
882
  toolIcon.appendChild(icon)
789
883
  }
884
+ ;(this.askAiLauncher && this.askAiLauncher.toolEntries ? this.askAiLauncher.toolEntries : []).forEach((entry) => {
885
+ const selected = Boolean(tool && entry.tool === tool)
886
+ entry.button.classList.toggle('selected', selected)
887
+ entry.button.setAttribute('aria-selected', selected ? 'true' : 'false')
888
+ })
790
889
  syncRunState()
791
890
  }
792
891
  const run = async () => {
@@ -799,7 +898,7 @@
799
898
  }
800
899
  if (!tool) {
801
900
  error.textContent = 'Choose an agent.'
802
- toolSelect.focus()
901
+ toolTrigger.focus()
803
902
  return
804
903
  }
805
904
  error.textContent = ''
@@ -813,23 +912,31 @@
813
912
  }
814
913
 
815
914
  closeButton.addEventListener('click', close)
915
+ toolTrigger.addEventListener('click', () => {
916
+ if (toolSheetOpen) {
917
+ closeToolSheet()
918
+ } else {
919
+ openToolSheet()
920
+ }
921
+ })
922
+ toolSheetBackdrop.addEventListener('click', () => closeToolSheet())
923
+ toolSheetClose.addEventListener('click', () => closeToolSheet())
816
924
  overlay.addEventListener('click', (event) => {
817
925
  if (event.target === overlay) {
818
926
  close()
819
927
  }
820
928
  })
821
- toolSelect.addEventListener('change', syncTool)
822
- toolSelect.addEventListener('change', () => {
823
- const tool = this.selectedAskAiTool()
824
- setStoredToolPreference(tool && tool.value ? tool.value : '')
825
- })
826
929
  promptTextarea.addEventListener('input', syncRunState)
827
930
  runButton.addEventListener('click', run)
828
931
  overlay.addEventListener('keydown', (event) => {
829
932
  event.stopPropagation()
830
933
  if (event.key === 'Escape') {
831
934
  event.preventDefault()
832
- close()
935
+ if (toolSheetOpen) {
936
+ closeToolSheet()
937
+ } else {
938
+ close()
939
+ }
833
940
  } else if (event.key === 'Tab') {
834
941
  const focusable = getFocusable()
835
942
  if (focusable.length === 0) {
@@ -858,38 +965,131 @@
858
965
  overlay,
859
966
  panel,
860
967
  promptTextarea,
861
- toolSelect,
968
+ toolTrigger,
969
+ toolSheetBody,
970
+ toolSheetLayer,
862
971
  toolLabel,
863
972
  toolMeta,
864
973
  toolIcon,
974
+ selectedToolIndex: -1,
975
+ toolEntries: [],
976
+ setToolIndex(index) {
977
+ const nextIndex = Number.isFinite(index) ? index : -1
978
+ this.selectedToolIndex = nextIndex
979
+ const tool = Array.isArray(this.owner.askAiTools) ? this.owner.askAiTools[nextIndex] : null
980
+ setStoredToolPreference(tool && tool.value ? tool.value : '')
981
+ this.syncTool()
982
+ },
865
983
  error,
866
984
  runButton,
867
985
  setOpen,
986
+ closeToolSheet,
868
987
  syncTool,
869
- syncRunState
988
+ syncRunState,
989
+ owner: this
870
990
  }
871
991
  return this.askAiLauncher
872
992
  }
873
993
  populateAskAiLauncherTools(tools) {
874
994
  const launcher = this.createAskAiLauncher()
875
- while (launcher.toolSelect.firstChild) {
876
- launcher.toolSelect.removeChild(launcher.toolSelect.firstChild)
995
+ while (launcher.toolSheetBody.firstChild) {
996
+ launcher.toolSheetBody.removeChild(launcher.toolSheetBody.firstChild)
997
+ }
998
+ launcher.toolEntries = []
999
+ const createFallbackToolIcon = () => {
1000
+ const icon = document.createElement('span')
1001
+ icon.className = 'universal-launcher-tool-icon logs-ask-ai-tool-fallback-icon'
1002
+ icon.innerHTML = '<i class="fa-solid fa-robot" aria-hidden="true"></i>'
1003
+ return icon
877
1004
  }
878
- const placeholder = document.createElement('option')
879
- placeholder.value = ''
880
- placeholder.textContent = 'Choose agent'
881
- launcher.toolSelect.appendChild(placeholder)
1005
+ const groups = new Map()
882
1006
  tools.forEach((tool, index) => {
883
- const option = document.createElement('option')
884
- option.value = String(index)
885
- option.textContent = askAiToolOptionLabel(tool)
886
- launcher.toolSelect.appendChild(option)
1007
+ const category = tool && tool.category ? tool.category : 'Plugin'
1008
+ if (!groups.has(category)) {
1009
+ groups.set(category, [])
1010
+ }
1011
+ groups.get(category).push({ tool, index })
1012
+ })
1013
+ const orderedGroupCategories = []
1014
+ ASK_AI_TOOL_CATEGORY_ORDER.forEach((preferredCategory) => {
1015
+ const match = Array.from(groups.keys()).find((category) => String(category).toLowerCase() === preferredCategory.toLowerCase())
1016
+ if (match && !orderedGroupCategories.includes(match)) {
1017
+ orderedGroupCategories.push(match)
1018
+ }
1019
+ })
1020
+ Array.from(groups.keys()).sort((a, b) => {
1021
+ const rankDelta = askAiToolCategoryRank(a) - askAiToolCategoryRank(b)
1022
+ if (rankDelta !== 0) return rankDelta
1023
+ return String(a || '').localeCompare(String(b || ''), undefined, { sensitivity: 'base' })
1024
+ }).forEach((category) => {
1025
+ if (!orderedGroupCategories.includes(category)) {
1026
+ orderedGroupCategories.push(category)
1027
+ }
1028
+ })
1029
+ orderedGroupCategories.forEach((category) => {
1030
+ const entries = groups.get(category) || []
1031
+ const group = document.createElement('div')
1032
+ group.className = 'universal-launcher-tool-group'
1033
+ const heading = document.createElement('div')
1034
+ heading.className = 'universal-launcher-tool-group-title'
1035
+ heading.textContent = category
1036
+ group.appendChild(heading)
1037
+ const list = document.createElement('div')
1038
+ list.className = 'universal-launcher-tool-list logs-ask-ai-tool-list'
1039
+ group.appendChild(list)
1040
+ entries.forEach(({ tool, index }) => {
1041
+ const option = document.createElement('button')
1042
+ option.type = 'button'
1043
+ option.className = 'universal-launcher-tool'
1044
+ option.setAttribute('role', 'option')
1045
+ option.setAttribute('aria-selected', 'false')
1046
+ option.dataset.toolIndex = String(index)
1047
+
1048
+ const indicator = document.createElement('span')
1049
+ indicator.className = 'universal-launcher-tool-indicator'
1050
+ indicator.setAttribute('aria-hidden', 'true')
1051
+ option.appendChild(indicator)
1052
+
1053
+ if (tool && tool.iconSrc) {
1054
+ const icon = document.createElement('img')
1055
+ icon.className = 'universal-launcher-tool-icon'
1056
+ icon.src = tool.iconSrc
1057
+ icon.alt = ''
1058
+ icon.onerror = () => {
1059
+ icon.replaceWith(createFallbackToolIcon())
1060
+ }
1061
+ option.appendChild(icon)
1062
+ } else {
1063
+ option.appendChild(createFallbackToolIcon())
1064
+ }
1065
+
1066
+ const text = document.createElement('span')
1067
+ text.className = 'universal-launcher-tool-copy'
1068
+ const label = document.createElement('span')
1069
+ label.className = 'universal-launcher-tool-label'
1070
+ label.textContent = tool ? tool.label : 'Agent'
1071
+ text.appendChild(label)
1072
+ const meta = document.createElement('span')
1073
+ meta.className = 'universal-launcher-tool-meta'
1074
+ meta.textContent = tool && tool.category ? tool.category : 'Plugin'
1075
+ text.appendChild(meta)
1076
+ option.appendChild(text)
1077
+
1078
+ option.addEventListener('click', () => {
1079
+ launcher.setToolIndex(index)
1080
+ launcher.closeToolSheet()
1081
+ launcher.toolTrigger.focus()
1082
+ })
1083
+ list.appendChild(option)
1084
+ launcher.toolEntries.push({ button: option, tool })
1085
+ })
1086
+ launcher.toolSheetBody.appendChild(group)
887
1087
  })
888
1088
  const preferredTool = getStoredToolPreference()
889
1089
  const preferredIndex = preferredTool
890
1090
  ? tools.findIndex((tool) => tool && tool.value === preferredTool)
891
1091
  : -1
892
- launcher.toolSelect.value = preferredIndex >= 0 ? String(preferredIndex) : ''
1092
+ launcher.selectedToolIndex = preferredIndex >= 0 ? preferredIndex : -1
893
1093
  if (preferredTool && preferredIndex < 0) {
894
1094
  setStoredToolPreference('')
895
1095
  }
@@ -900,8 +1100,8 @@
900
1100
  if (!launcher || !Array.isArray(this.askAiTools)) {
901
1101
  return null
902
1102
  }
903
- const index = Number.parseInt(launcher.toolSelect.value, 10)
904
- return Number.isFinite(index) ? this.askAiTools[index] || null : null
1103
+ const index = launcher.selectedToolIndex
1104
+ return Number.isFinite(index) && index >= 0 ? this.askAiTools[index] || null : null
905
1105
  }
906
1106
  async openAskAiModal() {
907
1107
  if (!this.currentMarkdown && !this.reviewMarkdown) {
@@ -5298,25 +5298,33 @@ body.dark .logs-help-action--ai:focus-visible {
5298
5298
  width: 100%;
5299
5299
  }
5300
5300
  .logs-ask-ai-tool-trigger {
5301
- pointer-events: none;
5301
+ pointer-events: auto;
5302
+ font: inherit;
5302
5303
  }
5303
5304
  .logs-ask-ai-tool-picker:focus-within .logs-ask-ai-tool-trigger {
5304
5305
  border-color: var(--universal-launcher-border-strong);
5305
5306
  background: var(--universal-launcher-control-surface);
5306
5307
  outline: none;
5307
5308
  }
5308
- .logs-ask-ai-tool-select {
5309
- position: absolute;
5310
- inset: 0;
5311
- width: 100%;
5312
- height: 100%;
5313
- appearance: none;
5314
- border: 0;
5315
- opacity: 0;
5316
- cursor: pointer;
5309
+ .logs-ask-ai-tool-sheet-layer {
5310
+ padding: 12px;
5317
5311
  }
5318
- .logs-ask-ai-tool-select:disabled {
5319
- cursor: default;
5312
+ .logs-ask-ai-tool-sheet {
5313
+ width: min(100%, 520px);
5314
+ max-height: 100%;
5315
+ }
5316
+ .logs-ask-ai-tool-sheet.logs-ask-ai-tool-sheet .universal-launcher-tool-meta {
5317
+ display: block;
5318
+ }
5319
+ .logs-ask-ai-tool-list {
5320
+ grid-template-columns: repeat(2, minmax(0, 1fr));
5321
+ }
5322
+ .logs-ask-ai-tool-fallback-icon {
5323
+ display: inline-flex;
5324
+ align-items: center;
5325
+ justify-content: center;
5326
+ color: var(--universal-launcher-text-muted);
5327
+ font-size: 13px;
5320
5328
  }
5321
5329
  .logs-ask-ai-tool-trigger-icon {
5322
5330
  display: inline-flex;
@@ -5332,6 +5340,14 @@ body.dark .logs-help-action--ai:focus-visible {
5332
5340
  object-fit: contain;
5333
5341
  border-radius: 4px;
5334
5342
  }
5343
+ @media (max-width: 560px), (max-height: 520px) {
5344
+ .logs-ask-ai-tool-list {
5345
+ grid-template-columns: minmax(0, 1fr);
5346
+ }
5347
+ .logs-ask-ai-tool-sheet-layer {
5348
+ padding: 8px;
5349
+ }
5350
+ }
5335
5351
  .logs-report-pane,
5336
5352
  .logs-redaction-pane {
5337
5353
  min-width: 0;
@@ -820,6 +820,13 @@ body.dark .home-app-line {
820
820
  border-radius: 6px;
821
821
  object-fit: cover;
822
822
  }
823
+ .home-app-line .description {
824
+ margin-bottom: 0;
825
+ line-height: 1.32;
826
+ }
827
+ .home-app-line .menu-btns {
828
+ margin-top: 4px;
829
+ }
823
830
  .menu-btns .home-icon-btn {
824
831
  width: 28px;
825
832
  height: 22px;
@@ -37,14 +37,24 @@ function createReportFixture() {
37
37
 
38
38
  function createPluginMenuFixture() {
39
39
  return {
40
- menu: [{
41
- title: "OpenAI Codex Auto",
42
- href: "/pinokio/run/plugin/codex-auto/pinokio.js",
43
- image: "/pinokio/asset/plugin/codex-auto/openai.webp",
44
- category: "cli",
45
- categoryTitle: "Terminal Plugin",
46
- pluginPath: "/pinokio/run/plugin/codex-auto/pinokio.js"
47
- }]
40
+ menu: [
41
+ {
42
+ title: "Claude Desktop",
43
+ href: "/pinokio/run/plugin/claude-desktop/pinokio.js",
44
+ image: "/pinokio/asset/plugin/claude-desktop/icon.png",
45
+ category: "ide",
46
+ categoryTitle: "Desktop Plugin",
47
+ pluginPath: "/pinokio/run/plugin/claude-desktop/pinokio.js"
48
+ },
49
+ {
50
+ title: "OpenAI Codex Auto",
51
+ href: "/pinokio/run/plugin/codex-auto/pinokio.js",
52
+ image: "/pinokio/asset/plugin/codex-auto/openai.webp",
53
+ category: "cli",
54
+ categoryTitle: "Terminal Plugin",
55
+ pluginPath: "/pinokio/run/plugin/codex-auto/pinokio.js"
56
+ }
57
+ ]
48
58
  }
49
59
  }
50
60
 
@@ -165,14 +175,59 @@ test("log Ask AI modal enables Run after assigning the default prompt", async ()
165
175
 
166
176
  await waitFor(() => document.querySelector(".logs-ask-ai-launcher:not([hidden])"), "Ask AI modal")
167
177
  const textarea = document.querySelector(".logs-ask-ai-launcher-textarea")
168
- const select = document.querySelector(".logs-ask-ai-tool-select")
178
+ const trigger = document.querySelector(".logs-ask-ai-tool-trigger")
179
+ const selectedOption = document.querySelector(".logs-ask-ai-tool-sheet-body .universal-launcher-tool.selected")
169
180
  const runButton = document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary")
170
181
 
171
182
  assert.match(textarea.value, /Investigate what went wrong/)
172
- assert.equal(select.selectedOptions[0].textContent, "OpenAI Codex Auto (Terminal)")
183
+ assert.equal(document.querySelector(".logs-ask-ai-tool-select"), null)
184
+ assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-label").textContent, "OpenAI Codex Auto")
185
+ assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-meta").textContent, "Terminal")
186
+ assert.equal(selectedOption.querySelector(".universal-launcher-tool-label").textContent, "OpenAI Codex Auto")
173
187
  assert.equal(runButton.disabled, false)
174
188
  })
175
189
 
190
+ test("log Ask AI modal uses custom plugin sheet and closes it before the modal", async () => {
191
+ const { dom } = await createLogsDom()
192
+ const { window } = dom
193
+ const { document } = window
194
+
195
+ document.getElementById("logs-ask-ai").click()
196
+ await waitFor(() => document.querySelector(".logs-ask-ai-launcher:not([hidden])"), "Ask AI modal")
197
+
198
+ const trigger = document.querySelector(".logs-ask-ai-tool-trigger")
199
+ const sheet = document.querySelector(".logs-ask-ai-tool-sheet-layer")
200
+ assert.equal(sheet.hidden, true)
201
+ assert.equal(document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary").disabled, true)
202
+
203
+ trigger.click()
204
+ assert.equal(sheet.hidden, false)
205
+ assert.equal(trigger.getAttribute("aria-expanded"), "true")
206
+ assert.deepEqual(
207
+ Array.from(document.querySelectorAll(".logs-ask-ai-tool-sheet-body .universal-launcher-tool-group-title")).map((node) => node.textContent),
208
+ ["Terminal", "Desktop"]
209
+ )
210
+
211
+ const claudeOption = Array.from(document.querySelectorAll(".logs-ask-ai-tool-sheet-body .universal-launcher-tool"))
212
+ .find((option) => option.querySelector(".universal-launcher-tool-label").textContent === "Claude Desktop")
213
+ assert.ok(claudeOption)
214
+ claudeOption.click()
215
+
216
+ assert.equal(sheet.hidden, true)
217
+ assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-label").textContent, "Claude Desktop")
218
+ assert.equal(window.localStorage.getItem("pinokio.universalLauncher.tool"), "pinokio/run/plugin/claude-desktop")
219
+ assert.equal(document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary").disabled, false)
220
+
221
+ trigger.click()
222
+ assert.equal(sheet.hidden, false)
223
+ document.querySelector(".logs-ask-ai-launcher").dispatchEvent(new window.KeyboardEvent("keydown", {
224
+ key: "Escape",
225
+ bubbles: true
226
+ }))
227
+ assert.equal(sheet.hidden, true)
228
+ assert.equal(document.querySelector(".logs-ask-ai-launcher").hidden, false)
229
+ })
230
+
176
231
  test("log Ask AI launch calls a same-origin parent drawer before postMessage fallback", async () => {
177
232
  const opened = []
178
233
  const posted = []