@mooncellwiki/prts-design-css 0.1.0

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.
Files changed (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/prts-design.css +6762 -0
  4. package/dist/prts-design.min.css +2 -0
  5. package/package.json +55 -0
  6. package/src/arknights/archive.css +21 -0
  7. package/src/arknights/attrs.css +11 -0
  8. package/src/arknights/camp.css +8 -0
  9. package/src/arknights/dialogue.css +7 -0
  10. package/src/arknights/dossier.css +13 -0
  11. package/src/arknights/elite.css +5 -0
  12. package/src/arknights/enemy.css +12 -0
  13. package/src/arknights/event.css +14 -0
  14. package/src/arknights/hero.css +11 -0
  15. package/src/arknights/index.css +41 -0
  16. package/src/arknights/item.css +32 -0
  17. package/src/arknights/kv.css +15 -0
  18. package/src/arknights/level.css +9 -0
  19. package/src/arknights/materials.css +6 -0
  20. package/src/arknights/module.css +49 -0
  21. package/src/arknights/news.css +6 -0
  22. package/src/arknights/op-card.css +26 -0
  23. package/src/arknights/op-row.css +6 -0
  24. package/src/arknights/phase-tabs.css +8 -0
  25. package/src/arknights/pot-list.css +7 -0
  26. package/src/arknights/potential.css +4 -0
  27. package/src/arknights/profession.css +16 -0
  28. package/src/arknights/range.css +10 -0
  29. package/src/arknights/rarity.css +21 -0
  30. package/src/arknights/rich-text.css +18 -0
  31. package/src/arknights/skill-levels.css +13 -0
  32. package/src/arknights/skill-matrix.css +25 -0
  33. package/src/arknights/skill-sheet.css +30 -0
  34. package/src/arknights/skill.css +28 -0
  35. package/src/arknights/sp.css +15 -0
  36. package/src/arknights/stage.css +22 -0
  37. package/src/arknights/table-numerals.css +7 -0
  38. package/src/arknights/talent-table.css +30 -0
  39. package/src/arknights/talent.css +7 -0
  40. package/src/arknights/trust.css +4 -0
  41. package/src/arknights/voice.css +21 -0
  42. package/src/base/catlinks.css +64 -0
  43. package/src/base/collapsible.css +10 -0
  44. package/src/base/forms.css +121 -0
  45. package/src/base/index.css +20 -0
  46. package/src/base/media.css +26 -0
  47. package/src/base/notices.css +17 -0
  48. package/src/base/print.css +11 -0
  49. package/src/base/references.css +9 -0
  50. package/src/base/root.css +16 -0
  51. package/src/base/special-pages.css +43 -0
  52. package/src/base/tabber.css +31 -0
  53. package/src/base/tables.css +45 -0
  54. package/src/base/toc.css +19 -0
  55. package/src/base/typography.css +133 -0
  56. package/src/bridge-codex.css +98 -0
  57. package/src/chrome/body.css +13 -0
  58. package/src/chrome/footer.css +34 -0
  59. package/src/chrome/header.css +104 -0
  60. package/src/chrome/index.css +29 -0
  61. package/src/chrome/keyart.css +15 -0
  62. package/src/chrome/layout.css +15 -0
  63. package/src/chrome/local-nav.css +17 -0
  64. package/src/chrome/page-header.css +68 -0
  65. package/src/chrome/responsive.css +127 -0
  66. package/src/chrome/search-palette.css +121 -0
  67. package/src/chrome/shell.css +4 -0
  68. package/src/chrome/sidebar-tree.css +36 -0
  69. package/src/chrome/sidebar.css +39 -0
  70. package/src/chrome/special-pages.css +4 -0
  71. package/src/chrome/theme-toggle.css +10 -0
  72. package/src/chrome/toc.css +38 -0
  73. package/src/components/accordion.css +10 -0
  74. package/src/components/avatar.css +8 -0
  75. package/src/components/badge.css +16 -0
  76. package/src/components/breadcrumb.css +7 -0
  77. package/src/components/button.css +45 -0
  78. package/src/components/card.css +28 -0
  79. package/src/components/cbox.css +18 -0
  80. package/src/components/chip.css +9 -0
  81. package/src/components/dialog.css +14 -0
  82. package/src/components/divider.css +8 -0
  83. package/src/components/dropdown.css +30 -0
  84. package/src/components/empty.css +5 -0
  85. package/src/components/fab.css +9 -0
  86. package/src/components/form.css +66 -0
  87. package/src/components/heading.css +11 -0
  88. package/src/components/index.css +38 -0
  89. package/src/components/keyframes.css +14 -0
  90. package/src/components/message.css +21 -0
  91. package/src/components/pagination.css +11 -0
  92. package/src/components/panel.css +15 -0
  93. package/src/components/progress.css +19 -0
  94. package/src/components/skeleton.css +5 -0
  95. package/src/components/spinner.css +23 -0
  96. package/src/components/stat.css +12 -0
  97. package/src/components/stepper.css +14 -0
  98. package/src/components/table.css +17 -0
  99. package/src/components/tabs.css +29 -0
  100. package/src/components/tag.css +26 -0
  101. package/src/components/timeline.css +13 -0
  102. package/src/components/title-reset.css +8 -0
  103. package/src/components/toast.css +14 -0
  104. package/src/components/tooltip.css +31 -0
  105. package/src/decor/bilingual.css +6 -0
  106. package/src/decor/brackets.css +6 -0
  107. package/src/decor/chevrons.css +5 -0
  108. package/src/decor/corner.css +12 -0
  109. package/src/decor/glyph.css +7 -0
  110. package/src/decor/halftone.css +13 -0
  111. package/src/decor/index.css +18 -0
  112. package/src/decor/inverse.css +4 -0
  113. package/src/decor/patterns.css +7 -0
  114. package/src/decor/stripes.css +10 -0
  115. package/src/decor/type.css +10 -0
  116. package/src/forced-colors.css +54 -0
  117. package/src/index.css +18 -0
  118. package/src/scope.css +120 -0
  119. package/src/search-palette.js +549 -0
  120. package/src/sidebar-tree.js +220 -0
  121. package/src/standalone.css +12 -0
  122. package/src/tokens.css +665 -0
  123. package/src/utilities.css +43 -0
@@ -0,0 +1,549 @@
1
+ /*! ═══════════════════════════════════════════════════════════════════════════
2
+ * PRTS Design · search-palette.js — 悬浮搜索面板(Command Palette)
3
+ * 皮肤(skins.akds.js 通过 require 引入)与 preview 共用;无依赖,不带任何数据源 ——
4
+ * 数据由调用方以 providers 注入(MW:skin/resources/search-providers.js;预览:preview/search-mock.js)。
5
+ *
6
+ * 交互模型(参考 Citizen 的 Command Palette,视觉换成 PRTS Design):
7
+ * · 页眉里的 form.ak-header__search 被换成 button.ak-search-trigger(长得像输入框);真表单被搬进面板顶部,
8
+ * #searchInput / action / hidden title 都还在 → Gadget 与无 JS 提交不受影响。
9
+ * · 开:点触发器 / .ak-header__search-toggle / 键 "/"、Ctrl(⌘)K、accesskey F。
10
+ * 关:Esc(有字先清空,再按退出模式,再按关闭)/ 点遮罩 / 关闭按钮 / 选中结果。
11
+ * · 空查询:最近访问(localStorage)+ 提示 + 快捷入口;有字:providers.search() 分组结果 + 末尾固定「全文搜索」行;
12
+ * "/" 开头:命令列表;命中触发符(>、#、@、~)或选中命令:进入模式(斜切 chip),退格空输入 / ← 退出。
13
+ * · ↑↓ 循环高亮,回车打开(⌘/Ctrl 回车新标签),⇧回车全文搜索;结果还没到就回车 → urls.go(q)(MW 原生 Go:精确跳转,无则全文)。
14
+ * · 输入框始终持有焦点(列表 mousedown 阻止夺焦),aria-activedescendant 播报高亮项;Tab 在面板内循环。
15
+ *
16
+ * API:window.akdsSearchPalette.init( options ) → { open, close, toggle, refresh, isOpen }
17
+ * options = {
18
+ * trigger: 'form.ak-header__search', // 会被换成按钮并搬进面板;缺省时面板自建输入框
19
+ * toggles: '.ak-header__search-toggle', // 其它打开按钮(手机图标等)
20
+ * placeholder, messages: {…}, // 文案(默认中文;MW 侧用 mw.msg 覆盖)
21
+ * urls: { go(q), fulltext(q) }, // 回车兜底 / 全文搜索
22
+ * providers: {
23
+ * search(q, signal, ctx) → Promise<Group[]> | Group[] // Group = { id, label, en?, items: Item[] }
24
+ * empty(ctx) → Promise<Group[]> | Group[] // 空查询时最近访问之后的分组(可选)
25
+ * shortcuts() → [{ label, url }] // 空态快捷入口(可选)
26
+ * },
27
+ * modes: [ { id, trigger: '/action', alias: '>', label, desc, placeholder, fulltext:false,
28
+ * search(q, signal, ctx) → Group[] } ],
29
+ * recent: { key: 'akds-recent', max: 8 } | false,
30
+ * onSelect(item, event) → false 阻止默认导航
31
+ * }
32
+ * Item = { id?, type: 'page'|'operator'|'item'|'category'|'action'|…, label, url?, desc?, thumb?(url), icon?(svg id: 'page'|'search'|'category'|'user'|'file'|'action'|'clock'),
33
+ * glyph?(单字,深底), meta?: [{ text?, html?, kbd? }], match?: boolean(默认 true:高亮已输入部分), en?, keepOpen?, noRecent?, onSelect?() ,
34
+ * actions?: [{ id, label, icon: 'edit'|'close'|'external', url?, onClick?(item) }] } ← 行内动作始终占位、高亮时可见;设计上只给最近访问用「移除」,搜索结果不放额外点击动作
35
+ * ═══════════════════════════════════════════════════════════════════════════ */
36
+ ( function () {
37
+ 'use strict';
38
+
39
+ const ICONS = {
40
+ search: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M10.5 3a7.5 7.5 0 1 1 0 15 7.5 7.5 0 0 1 0-15zM16 16l5 5"/></svg>',
41
+ fulltext: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M4 4h16v16H4zM8 9h8M8 13h5"/><path fill="none" stroke="currentColor" stroke-width="2" d="M14 14.5a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5zm1.8 4.3L18 21"/></svg>',
42
+ page: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M6 3h8l5 5v13H6zM14 3v5h5M9 13h6M9 17h6"/></svg>',
43
+ category: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z"/></svg>',
44
+ user: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M12 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8zM4 21c0-4 3.6-7 8-7s8 3 8 7"/></svg>',
45
+ file: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M4 5h16v14H4zM4 16l5-5 4 4 3-3 4 4"/><circle cx="16" cy="9" r="1.5" fill="currentColor"/></svg>',
46
+ action: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M13 3 5 14h6l-1 7 8-11h-6z"/></svg>',
47
+ clock: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="2"/><path fill="none" stroke="currentColor" stroke-width="2" d="M12 7v5l3 2"/></svg>',
48
+ redirect: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M4 6h9a5 5 0 0 1 0 10H8m3-3-3 3 3 3"/></svg>',
49
+ back: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M15 5l-7 7 7 7"/></svg>',
50
+ close: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M6 6l12 12M18 6 6 18"/></svg>',
51
+ edit: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M4 20h4l11-11-4-4L4 16zM13 7l4 4"/></svg>',
52
+ external: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M14 4h6v6M20 4l-9 9M18 14v6H4V6h6"/></svg>',
53
+ command: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M9 5 5 12l4 7M15 5l4 7-4 7"/></svg>'
54
+ };
55
+
56
+ const MSG = {
57
+ label: '搜索',
58
+ placeholder: '搜索 PRTS…',
59
+ recent: '最近访问', recentEn: 'Recent',
60
+ modes: '命令', modesEn: 'Commands',
61
+ fulltext: '全文搜索 “$1”',
62
+ fulltextDesc: '在全站正文中查找',
63
+ go: '前往 “$1”',
64
+ emptyTitle: '搜索 PRTS',
65
+ emptyDesc: '干员、敌人、道具、关卡或任意页面名;输入 $1 查看命令',
66
+ shortcuts: '快捷入口', shortcutsEn: 'Shortcuts',
67
+ noResults: '没有标题匹配 “$1”',
68
+ noResultsDesc: '检查拼写,或试试全文搜索',
69
+ error: '搜索服务暂时不可用,回车执行全文搜索',
70
+ results: '$1 条结果',
71
+ hintNavigate: '选择', hintOpen: '打开', hintFulltext: '全文', hintClose: '关闭', hintClear: '清空', hintBack: '返回', hintCommands: '命令',
72
+ close: '取消', clear: '清空', back: '返回', remove: '从最近访问中移除', edit: '编辑', newTab: '在新标签页打开',
73
+ brand: 'PRTS · Search'
74
+ };
75
+
76
+ function h( tag, cls, attrs ) {
77
+ const el = document.createElement( tag );
78
+ if ( cls ) { el.className = cls; }
79
+ if ( attrs ) { Object.keys( attrs ).forEach( ( k ) => { if ( attrs[ k ] != null ) { el.setAttribute( k, attrs[ k ] ); } } ); }
80
+ return el;
81
+ }
82
+ function svg( name, cls ) {
83
+ const wrap = h( 'span' ); wrap.innerHTML = ICONS[ name ] || ICONS.page;
84
+ const el = wrap.firstChild; if ( cls ) { el.setAttribute( 'class', cls ); }
85
+ return el;
86
+ }
87
+ function fmt( s, a ) { return String( s ).replace( /\$1/g, a == null ? '' : a ); }
88
+ function isMac() { return /Mac|iPhone|iPad/.test( navigator.platform || '' ); }
89
+ function isFormField( el ) {
90
+ if ( !el || el.nodeType !== 1 ) { return false; }
91
+ const n = el.nodeName.toLowerCase(), t = ( el.getAttribute( 'type' ) || '' ).toLowerCase();
92
+ return n === 'select' || n === 'textarea' || ( n === 'input' && ![ 'submit', 'reset', 'checkbox', 'radio', 'button' ].includes( t ) ) || el.isContentEditable;
93
+ }
94
+
95
+ /* 标题高亮:已输入部分 <mark>(常规字重),其余加粗 —— Codex SearchResultTitle 的约定 */
96
+ function titleNode( label, query, match ) {
97
+ const bdi = h( 'bdi' );
98
+ const q = ( query || '' ).trim();
99
+ const i = ( match !== false && q ) ? label.toLowerCase().indexOf( q.toLowerCase() ) : -1;
100
+ if ( i < 0 ) { bdi.textContent = label; return bdi; }
101
+ bdi.append( label.slice( 0, i ) );
102
+ const m = h( 'mark' ); m.textContent = label.slice( i, i + q.length ); bdi.append( m );
103
+ bdi.append( label.slice( i + q.length ) );
104
+ return bdi;
105
+ }
106
+
107
+ function init( options ) {
108
+ const opt = options || {};
109
+ const M = Object.assign( {}, MSG, opt.messages || {} );
110
+ const providers = opt.providers || {};
111
+ const modes = ( opt.modes || [] ).slice();
112
+ const urls = Object.assign( {
113
+ go: ( q ) => 'Special:Search?search=' + encodeURIComponent( q ) + '&go=Go',
114
+ fulltext: ( q ) => 'Special:Search?search=' + encodeURIComponent( q ) + '&fulltext=1'
115
+ }, opt.urls || {} );
116
+ const recentCfg = opt.recent === false ? null : Object.assign( { key: 'akds-recent', max: 8 }, opt.recent || {} );
117
+ const placeholder = opt.placeholder || M.placeholder;
118
+
119
+ /* ── 触发器:把页眉表单换成按钮,表单本身留给面板 ── */
120
+ let form = null, input = null, triggerBtn = null;
121
+ const oldForm = opt.trigger ? document.querySelector( opt.trigger ) : null;
122
+ if ( oldForm ) {
123
+ form = oldForm;
124
+ input = form.querySelector( 'input[type="search"], input[type="text"], input:not([type])' );
125
+ triggerBtn = h( 'button', 'ak-search-trigger ' + ( form.classList.contains( 'ak-header__search' ) ? 'ak-header__search' : '' ), { type: 'button', 'aria-haspopup': 'dialog', 'aria-expanded': 'false', 'aria-keyshortcuts': '/ ' + ( isMac() ? 'Meta+K' : 'Control+K' ), 'aria-label': M.label } );
126
+ triggerBtn.append( svg( 'search', 'ak-search-trigger__icon' ) );
127
+ const lab = h( 'span', 'ak-search-trigger__label' ); lab.textContent = ( input && input.placeholder ) || placeholder; triggerBtn.append( lab );
128
+ const kbd = h( 'kbd', 'ak-kbd' ); kbd.textContent = '/'; triggerBtn.append( kbd );
129
+ form.parentNode.replaceChild( triggerBtn, form );
130
+ form.classList.remove( 'ak-header__search', 'ak-search', 'is-open' );
131
+ }
132
+ if ( !form ) {
133
+ form = h( 'form', '', { role: 'search', action: typeof urls.form === 'string' ? urls.form : '' } );
134
+ input = h( 'input', '', { type: 'search', name: 'search', id: 'searchInput' } );
135
+ form.append( input );
136
+ }
137
+ if ( !input ) { input = h( 'input', '', { type: 'search', name: 'search' } ); form.append( input ); }
138
+ form.classList.add( 'ak-palette__form' );
139
+ form.setAttribute( 'autocomplete', 'off' );
140
+ input.setAttribute( 'autocomplete', 'off' ); input.setAttribute( 'autocapitalize', 'off' ); input.setAttribute( 'spellcheck', 'false' );
141
+ input.setAttribute( 'role', 'combobox' ); input.setAttribute( 'aria-autocomplete', 'list' ); input.setAttribute( 'aria-expanded', 'false' ); input.setAttribute( 'aria-controls', 'ak-palette-listbox' );
142
+ input.setAttribute( 'aria-label', M.label );
143
+ input.placeholder = placeholder;
144
+ input.removeAttribute( 'accesskey' ); // MW 给的 accesskey F 由我们接管(否则聚焦到看不见的输入框)
145
+
146
+ /* ── 面板 DOM ── */
147
+ const backdrop = h( 'div', 'ak-palette-backdrop', { hidden: '' } );
148
+ const root = h( 'div', 'ak-palette', { role: 'dialog', 'aria-modal': 'true', 'aria-label': M.label, hidden: '' } );
149
+ const head = h( 'div', 'ak-palette__head' );
150
+ const backBtn = h( 'button', 'ak-palette__back', { type: 'button', 'aria-label': M.back, title: M.back } ); backBtn.append( svg( 'back' ) );
151
+ const icon = svg( 'search', 'ak-palette__icon' );
152
+ const chip = h( 'span', 'ak-palette__chip' );
153
+ const clearBtn = h( 'button', 'ak-palette__clear', { type: 'button', 'aria-label': M.clear, title: M.clear } ); clearBtn.append( svg( 'close' ) );
154
+ const closeBtn = h( 'button', 'ak-palette__close', { type: 'button', 'aria-label': M.close } );
155
+ const closeKbd = h( 'kbd', 'ak-kbd' ); closeKbd.textContent = 'Esc'; const closeTxt = h( 'span' ); closeTxt.textContent = M.close; closeBtn.append( closeKbd, closeTxt );
156
+ const loading = h( 'div', 'ak-palette__loading ak-progress ak-progress--indeterminate', { 'aria-hidden': 'true' } );
157
+ head.append( backBtn, icon, chip, form, clearBtn, closeBtn, loading );
158
+ const body = h( 'div', 'ak-palette__body' );
159
+ const viewport = h( 'div', 'ak-palette__viewport' );
160
+ const list = h( 'div', 'ak-palette__list', { role: 'listbox', id: 'ak-palette-listbox', 'aria-label': M.label } );
161
+ const empty = h( 'div', 'ak-palette__empty', { hidden: '' } );
162
+ viewport.append( list, empty ); body.append( viewport );
163
+ const foot = h( 'div', 'ak-palette__foot' );
164
+ const footLeft = h( 'div', 'ak-palette__foot-left' );
165
+ const brand = h( 'span', 'ak-en' ); brand.textContent = M.brand;
166
+ const cmdBtn = h( 'button', 'ak-palette__foot-btn', { type: 'button' } ); const cmdKbd = h( 'kbd', 'ak-kbd' ); cmdKbd.textContent = '/'; cmdBtn.append( cmdKbd, document.createTextNode( M.hintCommands ) );
167
+ footLeft.append( brand ); if ( modes.length ) { footLeft.append( cmdBtn ); }
168
+ const hints = h( 'div', 'ak-palette__hints', { 'aria-hidden': 'true' } );
169
+ foot.append( footLeft, hints );
170
+ const live = h( 'div', 'ak-palette__live', { 'aria-live': 'polite', 'aria-atomic': 'true' } );
171
+ root.append( head, body, foot, live );
172
+ document.body.append( backdrop, root );
173
+
174
+ /* ── 状态 ── */
175
+ let isOpen = false, closing = false, closeTimer = 0;
176
+ let query = '', mode = null;
177
+ let items = []; // 扁平项(与 DOM 顺序一致)
178
+ let active = -1;
179
+ let seq = 0, ctrl = null, debounceTimer = 0, pendingTimer = 0;
180
+ let lastGroups = null; // keepStale:新结果没到之前保留旧列表
181
+ let lastResizeH = 0;
182
+ const ro = window.ResizeObserver ? new ResizeObserver( () => syncHeight() ) : null;
183
+
184
+ function syncHeight() {
185
+ if ( !isOpen ) { return; }
186
+ const hgt = viewport.offsetHeight;
187
+ if ( hgt === lastResizeH ) { return; }
188
+ lastResizeH = hgt;
189
+ body.style.height = hgt + 'px';
190
+ }
191
+ function setLoading( on ) {
192
+ clearTimeout( pendingTimer );
193
+ if ( on ) { pendingTimer = setTimeout( () => root.classList.add( 'is-loading' ), 160 ); } // 快返回不闪进度条
194
+ else { root.classList.remove( 'is-loading' ); }
195
+ }
196
+
197
+ /* ── 最近访问 ── */
198
+ function readRecent() { if ( !recentCfg ) { return []; } try { return JSON.parse( localStorage.getItem( recentCfg.key ) ) || []; } catch ( e ) { return []; } }
199
+ function writeRecent( arr ) { if ( !recentCfg ) { return; } try { localStorage.setItem( recentCfg.key, JSON.stringify( arr.slice( 0, recentCfg.max ) ) ); } catch ( e ) { /* private mode */ } }
200
+ function pushRecent( item ) {
201
+ if ( !recentCfg || !item.url || item.noRecent || item.type === 'search' || item.type === 'mode' ) { return; }
202
+ const rec = { type: item.type || 'page', label: item.label, url: item.url, desc: item.desc || '', thumb: item.thumb || '', icon: item.icon || '', en: item.en || '' };
203
+ writeRecent( [ rec ].concat( readRecent().filter( ( r ) => r.url !== rec.url ) ) );
204
+ }
205
+ function removeRecent( url ) { writeRecent( readRecent().filter( ( r ) => r.url !== url ) ); }
206
+
207
+ /* ── 渲染 ── */
208
+ function clearList() { list.textContent = ''; items = []; active = -1; }
209
+ function renderGroups( groups, q, opts ) {
210
+ opts = opts || {};
211
+ clearList();
212
+ empty.hidden = true;
213
+ ( groups || [] ).forEach( ( g ) => {
214
+ if ( !g || !g.items || !g.items.length ) { return; }
215
+ const gid = 'ak-palette-g-' + ( g.id || Math.random().toString( 36 ).slice( 2 ) );
216
+ const grp = h( 'div', 'ak-palette__group' + ( g.tail ? ' ak-palette__group--tail' : '' ), { role: 'group', 'aria-labelledby': g.label ? gid : null } );
217
+ if ( g.label ) {
218
+ const lab = h( 'div', 'ak-palette__label', { id: gid } ); lab.textContent = g.label;
219
+ if ( g.en ) { const en = h( 'span', 'ak-en' ); en.textContent = g.en; lab.append( en ); }
220
+ if ( g.hint ) { const sm = h( 'small' ); sm.textContent = g.hint; lab.append( sm ); }
221
+ grp.append( lab );
222
+ }
223
+ g.items.forEach( ( it ) => grp.append( renderItem( it, q ) ) );
224
+ list.append( grp );
225
+ } );
226
+ if ( opts.notice ) { const n = h( 'div', 'ak-palette__notice' + ( opts.error ? ' ak-palette__notice--error' : '' ) ); n.textContent = opts.notice; list.append( n ); }
227
+ input.setAttribute( 'aria-expanded', items.length ? 'true' : 'false' );
228
+ setActive( opts.highlight != null ? opts.highlight : ( q ? 0 : -1 ), false );
229
+ live.textContent = items.length ? fmt( M.results, items.length ) : '';
230
+ syncHeight();
231
+ }
232
+ function renderItem( it, q ) {
233
+ const idx = items.length; items.push( it );
234
+ const row = h( 'div', 'ak-palette__item', { role: 'option', id: 'ak-palette-item-' + idx, 'aria-selected': 'false', 'data-index': idx } );
235
+ const link = it.url ? h( 'a', 'ak-palette__link', { href: it.url } ) : h( 'button', 'ak-palette__link', { type: 'button' } );
236
+ link.tabIndex = -1;
237
+ // 缩略图 / 图标 / 单字
238
+ const th = h( 'span', 'ak-palette__thumb' + ( it.thumb ? '' : ( it.glyph ? ' ak-palette__thumb--dark ak-palette__thumb--mono' : ( it.dark ? ' ak-palette__thumb--dark' : ' ak-palette__thumb--icon' ) ) ) );
239
+ if ( it.thumb ) { const img = h( 'img', '', { src: it.thumb, alt: '', loading: 'lazy' } ); th.append( img ); }
240
+ else if ( it.glyph ) { th.textContent = it.glyph; }
241
+ else { th.append( svg( it.icon || ( it.type === 'category' ? 'category' : it.type === 'user' ? 'user' : it.type === 'file' ? 'file' : it.type === 'action' ? 'action' : it.type === 'search' ? 'fulltext' : it.type === 'recent' ? 'clock' : it.type === 'mode' ? 'command' : 'page' ) ) ); }
242
+ // 文字
243
+ const text = h( 'span', 'ak-palette__text' );
244
+ const title = h( 'span', 'ak-palette__title' ); title.append( titleNode( it.label, q, it.match ) );
245
+ if ( it.en ) { const en = h( 'span', 'ak-en' ); en.textContent = it.en; title.append( en ); }
246
+ text.append( title );
247
+ if ( it.desc || it.redirect ) {
248
+ const d = h( 'span', 'ak-palette__desc' );
249
+ if ( it.redirect ) { d.append( svg( 'redirect', 'ak-icon' ) ); d.append( it.redirect ); if ( it.desc ) { d.append( ' · ' ); } }
250
+ if ( it.desc ) { d.append( it.desc ); }
251
+ text.append( d );
252
+ }
253
+ link.append( th, text );
254
+ // 右侧元数据
255
+ if ( it.meta && it.meta.length ) {
256
+ const meta = h( 'span', 'ak-palette__meta' );
257
+ it.meta.forEach( ( m ) => {
258
+ if ( m.kbd ) { const k = h( 'kbd', 'ak-kbd' ); k.textContent = m.kbd; meta.append( k ); }
259
+ else if ( m.html ) { const s = h( 'span' ); s.innerHTML = m.html; meta.append( s ); } // 受信任来源(皮肤/预览自己拼的稀有度、职业图标)
260
+ else if ( m.text ) { const s = h( 'span', m.cls || '' ); s.textContent = m.text; meta.append( s ); }
261
+ } );
262
+ link.append( meta );
263
+ }
264
+ row.append( link );
265
+ // 行内动作(高亮 / 聚焦时可见;Tab 可达)
266
+ const acts = ( it.actions || [] ).slice();
267
+ if ( it.type === 'recent' ) { acts.push( { id: 'remove', label: M.remove, icon: 'close', onClick: ( x ) => { removeRecent( x.url ); showEmpty(); } } ); }
268
+ if ( acts.length ) {
269
+ const box = h( 'div', 'ak-palette__actions' );
270
+ acts.forEach( ( a ) => {
271
+ const b = a.url ? h( 'a', 'ak-palette__action', { href: a.url, 'aria-label': a.label, title: a.label } ) : h( 'button', 'ak-palette__action', { type: 'button', 'aria-label': a.label, title: a.label } );
272
+ b.append( svg( a.icon || 'edit' ) );
273
+ b.addEventListener( 'click', ( e ) => { if ( a.onClick ) { e.preventDefault(); a.onClick( it, e ); } else { close(); } e.stopPropagation(); } );
274
+ box.append( b );
275
+ } );
276
+ row.append( box );
277
+ }
278
+ return row;
279
+ }
280
+ function setActive( i, scroll ) {
281
+ const rows = list.querySelectorAll( '.ak-palette__item' );
282
+ if ( !rows.length ) { active = -1; input.removeAttribute( 'aria-activedescendant' ); return; }
283
+ if ( i < 0 ) { i = -1; } else { i = ( ( i % rows.length ) + rows.length ) % rows.length; }
284
+ rows.forEach( ( r, k ) => { r.classList.toggle( 'is-active', k === i ); r.setAttribute( 'aria-selected', k === i ? 'true' : 'false' ); } );
285
+ active = i;
286
+ if ( i >= 0 ) {
287
+ input.setAttribute( 'aria-activedescendant', rows[ i ].id );
288
+ if ( scroll !== false ) { rows[ i ].scrollIntoView( { block: 'nearest' } ); }
289
+ } else { input.removeAttribute( 'aria-activedescendant' ); }
290
+ }
291
+ function renderHints( arr ) {
292
+ hints.textContent = '';
293
+ arr.forEach( ( x ) => { const s = h( 'span' ); const k = h( 'kbd', 'ak-kbd' ); k.textContent = x[ 0 ]; s.append( k, document.createTextNode( x[ 1 ] ) ); hints.append( s ); } );
294
+ }
295
+ /* 页脚提示随状态变:Esc 的含义 = 有字「清空」→ 模式中「返回」→ 否则「关闭」 */
296
+ function updateHints() {
297
+ const q = query.trim();
298
+ const esc = q ? M.hintClear : ( mode ? M.hintBack : M.hintClose );
299
+ const arr = [];
300
+ if ( q && !mode && q[ 0 ] !== '/' ) { arr.push( [ '⇧↵', M.hintFulltext ] ); }
301
+ arr.push( [ '↑↓', M.hintNavigate ], [ '↵', M.hintOpen ], [ 'Esc', esc ] );
302
+ renderHints( arr );
303
+ }
304
+
305
+ /* ── 空态 / 模式列表 / 搜索 ── */
306
+ function fulltextItem( q ) { return { type: 'search', label: fmt( M.fulltext, q ), desc: M.fulltextDesc, url: urls.fulltext( q ), match: false, dark: true, icon: 'fulltext', noRecent: true }; }
307
+ function modeItems( filter ) {
308
+ const f = ( filter || '' ).toLowerCase();
309
+ return modes.filter( ( m ) => !f || m.id.includes( f ) || ( m.trigger || '' ).toLowerCase().includes( f ) || ( m.label || '' ).toLowerCase().includes( f ) )
310
+ .map( ( m ) => ( { type: 'mode', label: m.trigger || ( '/' + m.id ), en: m.label, desc: m.desc, glyph: m.alias || '/', match: false, meta: m.alias ? [ { kbd: m.alias } ] : [], onSelect: () => enterMode( m, '' ), mode: m } ) );
311
+ }
312
+ let emptySeq = 0;
313
+ function showEmpty() {
314
+ const my = ++emptySeq;
315
+ const groups = [];
316
+ const rec = readRecent();
317
+ if ( rec.length ) { groups.push( { id: 'recent', label: M.recent, en: M.recentEn, items: rec.map( ( r ) => Object.assign( { match: false }, r, { type: 'recent', icon: r.icon || ( r.type === 'category' ? 'category' : r.type === 'file' ? 'file' : r.type === 'user' ? 'user' : 'clock' ) } ) ) } ); }
318
+ const finish = ( more ) => {
319
+ if ( my !== emptySeq || query || mode ) { return; }
320
+ const all = groups.concat( more || [] );
321
+ renderGroups( all, '', { highlight: -1 } );
322
+ renderEmptyCard( !all.length );
323
+ updateHints();
324
+ };
325
+ if ( providers.empty ) { Promise.resolve( providers.empty( { mode: null } ) ).then( finish, () => finish( [] ) ); } else { finish( [] ); }
326
+ }
327
+ function renderEmptyCard( noList ) {
328
+ empty.textContent = '';
329
+ const t = h( 'p', 'ak-palette__empty-title' ); t.textContent = M.emptyTitle;
330
+ const d = h( 'p', 'ak-palette__empty-desc' );
331
+ const parts = fmt( M.emptyDesc, '' ).split( '' );
332
+ d.append( parts[ 0 ] ); if ( parts.length > 1 ) { const k = h( 'kbd', 'ak-kbd' ); k.textContent = '/'; d.append( k, parts[ 1 ] ); }
333
+ if ( noList ) { empty.append( t, d ); } // 有最近访问时只保留快捷入口(更短),没有时显示完整提示
334
+ const sc = providers.shortcuts ? ( providers.shortcuts() || [] ) : [];
335
+ if ( sc.length ) {
336
+ const lab = h( 'div', 'ak-palette__label' ); lab.textContent = M.shortcuts; const en = h( 'span', 'ak-en' ); en.textContent = M.shortcutsEn; lab.append( en );
337
+ const box = h( 'div', 'ak-palette__shortcuts' );
338
+ sc.forEach( ( s ) => { const a = h( 'a', 'ak-palette__shortcut', { href: s.url } ); a.textContent = s.label; a.addEventListener( 'click', () => { close(); } ); box.append( a ); } );
339
+ empty.append( lab, box );
340
+ }
341
+ empty.hidden = !( noList || sc.length );
342
+ empty.classList.toggle( 'ak-palette__empty--compact', !noList );
343
+ syncHeight();
344
+ }
345
+ function showModes( filter ) {
346
+ const its = modeItems( filter );
347
+ renderGroups( [ { id: 'modes', label: M.modes, en: M.modesEn, items: its } ], '', { highlight: 0, notice: its.length ? null : fmt( M.noResults, '/' + filter ) } );
348
+ updateHints();
349
+ }
350
+ function runSearch( q ) {
351
+ const my = ++seq;
352
+ if ( ctrl ) { ctrl.abort(); }
353
+ ctrl = window.AbortController ? new AbortController() : null;
354
+ const fn = mode ? mode.search : providers.search;
355
+ if ( !fn ) { renderGroups( mode || !q ? [] : [ { id: 'ft', tail: true, items: [ fulltextItem( q ) ] } ], q ); return; }
356
+ setLoading( true );
357
+ Promise.resolve().then( () => fn( q, ctrl && ctrl.signal, { mode: mode } ) ).then( ( groups ) => {
358
+ if ( my !== seq ) { return; }
359
+ setLoading( false );
360
+ groups = ( groups || [] ).filter( ( g ) => g && g.items && g.items.length );
361
+ lastGroups = groups;
362
+ const withFt = ( !mode || mode.fulltext !== false ) && q ? groups.concat( [ { id: 'ft', tail: true, items: [ fulltextItem( q ) ] } ] ) : groups;
363
+ const none = !groups.length;
364
+ renderGroups( withFt, q, { highlight: 0, notice: none ? fmt( M.noResults, q ) + ( mode ? '' : ' · ' + M.noResultsDesc ) : null } );
365
+ updateHints();
366
+ }, ( err ) => {
367
+ if ( my !== seq || ( err && err.name === 'AbortError' ) ) { return; }
368
+ setLoading( false );
369
+ renderGroups( q ? [ { id: 'ft', tail: true, items: [ fulltextItem( q ) ] } ] : [], q, { highlight: 0, notice: M.error, error: true } );
370
+ } );
371
+ }
372
+ function refresh( immediate ) {
373
+ clearTimeout( debounceTimer );
374
+ const q = query.trim();
375
+ if ( !mode && !q ) { seq++; setLoading( false ); showEmpty(); return; }
376
+ if ( !mode && q[ 0 ] === '/' ) { seq++; setLoading( false ); showModes( q.slice( 1 ) ); return; }
377
+ empty.hidden = true;
378
+ if ( immediate ) { runSearch( q ); } else { debounceTimer = setTimeout( () => runSearch( q ), 120 ); }
379
+ }
380
+
381
+ /* ── 模式 ── */
382
+ function enterMode( m, rest ) {
383
+ mode = m;
384
+ root.classList.add( 'has-mode' );
385
+ chip.textContent = m.label || m.id;
386
+ input.placeholder = m.placeholder || placeholder;
387
+ setQuery( rest || '' );
388
+ refresh( true );
389
+ input.focus();
390
+ }
391
+ function exitMode() {
392
+ mode = null;
393
+ root.classList.remove( 'has-mode' );
394
+ input.placeholder = placeholder;
395
+ setQuery( '' );
396
+ refresh( true );
397
+ input.focus();
398
+ }
399
+ function setQuery( v ) { query = v; input.value = v; root.classList.toggle( 'has-query', !!v ); }
400
+ function detectTrigger( v ) {
401
+ if ( mode || !v ) { return false; }
402
+ // 单字触发符(> # @ ~):只在开头且只有一个字符时进入,避免误伤正常输入
403
+ const m1 = modes.find( ( m ) => m.alias && v[ 0 ] === m.alias );
404
+ if ( m1 && v.length === 1 ) { enterMode( m1, '' ); return true; }
405
+ // /action: 或 /action␣ → 进入
406
+ const m2 = modes.find( ( m ) => m.trigger && ( v.toLowerCase().indexOf( m.trigger.toLowerCase() + ':' ) === 0 || v.toLowerCase().indexOf( m.trigger.toLowerCase() + ' ' ) === 0 ) );
407
+ if ( m2 ) { enterMode( m2, v.slice( m2.trigger.length + 1 ) ); return true; }
408
+ return false;
409
+ }
410
+
411
+ /* ── 选择 ── */
412
+ function selectIndex( i, ev ) {
413
+ const it = items[ i ]; if ( !it ) { return; }
414
+ if ( it.onSelect ) { const r = it.onSelect( it, ev ); if ( r !== 'navigate' ) { return; } }
415
+ if ( opt.onSelect && opt.onSelect( it, ev ) === false ) { pushRecent( it ); return; }
416
+ if ( !it.url ) { return; }
417
+ pushRecent( it );
418
+ const nt = ev && ( ev.metaKey || ev.ctrlKey );
419
+ if ( nt ) { window.open( it.url, '_blank' ); return; }
420
+ close();
421
+ location.href = it.url;
422
+ }
423
+ function goto( url ) { close(); location.href = url; }
424
+
425
+ /* ── 开关 ── */
426
+ function open( prefill ) {
427
+ if ( closing ) { clearTimeout( closeTimer ); finishClose(); }
428
+ if ( isOpen ) { input.focus(); if ( typeof prefill === 'string' ) { setQuery( prefill ); refresh( true ); } return; }
429
+ isOpen = true;
430
+ backdrop.hidden = false; root.hidden = false;
431
+ backdrop.classList.remove( 'is-closing' ); root.classList.remove( 'is-closing' );
432
+ if ( triggerBtn ) { triggerBtn.setAttribute( 'aria-expanded', 'true' ); }
433
+ document.documentElement.classList.add( 'ak-palette-open' );
434
+ mode = null; root.classList.remove( 'has-mode' ); input.placeholder = placeholder;
435
+ setQuery( typeof prefill === 'string' ? prefill : '' );
436
+ lastResizeH = 0; body.style.transition = 'none'; body.style.height = '';
437
+ refresh( true );
438
+ requestAnimationFrame( () => { syncHeight(); requestAnimationFrame( () => { body.style.transition = ''; } ); } );
439
+ if ( ro ) { ro.observe( viewport ); }
440
+ input.focus(); input.select();
441
+ document.dispatchEvent( new CustomEvent( 'akds:palette-open' ) );
442
+ }
443
+ function finishClose() {
444
+ closing = false;
445
+ backdrop.hidden = true; root.hidden = true;
446
+ backdrop.classList.remove( 'is-closing' ); root.classList.remove( 'is-closing' );
447
+ if ( ro ) { ro.unobserve( viewport ); }
448
+ body.style.height = '';
449
+ }
450
+ function close( restoreFocus ) {
451
+ if ( !isOpen ) { return; }
452
+ isOpen = false; closing = true;
453
+ seq++; emptySeq++; setLoading( false ); clearTimeout( debounceTimer ); if ( ctrl ) { ctrl.abort(); }
454
+ document.documentElement.classList.remove( 'ak-palette-open' );
455
+ if ( triggerBtn ) { triggerBtn.setAttribute( 'aria-expanded', 'false' ); }
456
+ backdrop.classList.add( 'is-closing' ); root.classList.add( 'is-closing' );
457
+ const reduce = window.matchMedia && matchMedia( '(prefers-reduced-motion: reduce)' ).matches;
458
+ closeTimer = setTimeout( finishClose, reduce ? 0 : 160 );
459
+ if ( restoreFocus !== false && triggerBtn && triggerBtn.offsetParent ) { triggerBtn.focus(); }
460
+ else if ( restoreFocus !== false ) { const t = document.querySelector( opt.toggles || '.ak-header__search-toggle' ); if ( t && t.offsetParent ) { t.focus(); } }
461
+ document.dispatchEvent( new CustomEvent( 'akds:palette-close' ) );
462
+ }
463
+ function toggle() { if ( isOpen ) { close(); } else { open(); } }
464
+
465
+ /* ── 事件 ── */
466
+ if ( triggerBtn ) { triggerBtn.addEventListener( 'click', () => open() ); }
467
+ document.addEventListener( 'click', ( e ) => {
468
+ const t = e.target.closest( opt.toggles || '.ak-header__search-toggle' );
469
+ if ( t ) { e.preventDefault(); toggle(); return; }
470
+ const trig = e.target.closest( '.ak-search-trigger[data-prefill], [data-akds-search]' );
471
+ if ( trig && trig !== triggerBtn ) { e.preventDefault(); open( trig.dataset.prefill || trig.dataset.akdsSearch || '' ); }
472
+ } );
473
+ backdrop.addEventListener( 'click', () => close() );
474
+ closeBtn.addEventListener( 'click', () => close() );
475
+ clearBtn.addEventListener( 'click', () => { setQuery( '' ); refresh( true ); input.focus(); } );
476
+ backBtn.addEventListener( 'click', () => exitMode() );
477
+ cmdBtn.addEventListener( 'click', () => { if ( mode ) { exitMode(); } setQuery( '/' ); refresh( true ); input.focus(); } );
478
+ form.addEventListener( 'submit', ( e ) => {
479
+ // 表单原生提交只在「没有高亮项」时才有意义(= MW 的 Go);有高亮项则由 keydown 处理
480
+ if ( active >= 0 && items[ active ] ) { e.preventDefault(); return; }
481
+ if ( urls.go && query.trim() ) { e.preventDefault(); goto( urls.go( query.trim() ) ); }
482
+ } );
483
+ input.addEventListener( 'input', () => {
484
+ const v = input.value;
485
+ query = v; root.classList.toggle( 'has-query', !!v );
486
+ if ( detectTrigger( v ) ) { return; }
487
+ updateHints();
488
+ refresh( false );
489
+ } );
490
+ list.addEventListener( 'mousedown', ( e ) => { if ( !e.target.closest( '.ak-palette__action' ) ) { e.preventDefault(); } } ); // 保住输入框焦点
491
+ list.addEventListener( 'mousemove', ( e ) => { const row = e.target.closest( '.ak-palette__item' ); if ( row ) { const i = +row.dataset.index; if ( i !== active ) { setActive( i, false ); } } } );
492
+ list.addEventListener( 'click', ( e ) => {
493
+ if ( e.target.closest( '.ak-palette__action' ) ) { return; }
494
+ const row = e.target.closest( '.ak-palette__item' ); if ( !row ) { return; }
495
+ e.preventDefault();
496
+ selectIndex( +row.dataset.index, e );
497
+ } );
498
+ root.addEventListener( 'keydown', ( e ) => {
499
+ if ( e.key === 'ArrowDown' ) { e.preventDefault(); setActive( active + 1 ); return; }
500
+ if ( e.key === 'ArrowUp' ) { e.preventDefault(); setActive( active < 0 ? -1 : active - 1 ); return; }
501
+ if ( e.key === 'Home' && e.target === input && !query ) { e.preventDefault(); setActive( 0 ); return; }
502
+ if ( e.key === 'End' && e.target === input && !query ) { e.preventDefault(); setActive( items.length - 1 ); return; }
503
+ if ( e.key === 'Enter' ) {
504
+ if ( e.target !== input && e.target.closest( '.ak-palette__action, .ak-palette__shortcut, button, a' ) ) { return; } // 让按钮自己处理
505
+ e.preventDefault();
506
+ const q = query.trim();
507
+ if ( e.shiftKey && q && !mode && q[ 0 ] !== '/' ) { goto( urls.fulltext( q ) ); return; }
508
+ if ( active >= 0 && items[ active ] ) { selectIndex( active, e ); return; }
509
+ if ( q && urls.go && !mode ) { goto( urls.go( q ) ); }
510
+ return;
511
+ }
512
+ if ( e.key === 'Escape' ) {
513
+ e.preventDefault(); e.stopPropagation();
514
+ if ( query ) { setQuery( '' ); refresh( true ); return; }
515
+ if ( mode ) { exitMode(); return; }
516
+ close();
517
+ return;
518
+ }
519
+ if ( e.key === 'Backspace' && mode && !input.value && e.target === input ) { e.preventDefault(); exitMode(); return; }
520
+ if ( e.key === 'ArrowLeft' && mode && !input.value && e.target === input ) { e.preventDefault(); exitMode(); return; }
521
+ if ( e.key === 'Tab' ) {
522
+ // 焦点在面板内循环:输入框 → 清空 → 关闭 → 高亮行的动作 → 快捷入口 → 页脚按钮 → 回到输入框
523
+ const f = Array.from( root.querySelectorAll( 'input, button, a[href], [tabindex="0"]' ) ).filter( ( el ) => el.offsetParent !== null && !el.disabled && el.tabIndex !== -1 && !el.closest( '[hidden]' ) );
524
+ if ( !f.length ) { return; }
525
+ let i = f.indexOf( document.activeElement ); if ( i < 0 ) { i = 0; }
526
+ i = ( i + ( e.shiftKey ? -1 : 1 ) + f.length ) % f.length;
527
+ e.preventDefault(); f[ i ].focus();
528
+ }
529
+ } );
530
+ // 全局快捷键:"/"(非表单焦点)、Ctrl/⌘K、accesskey F(Alt(+Shift)+F / Ctrl+Alt+F)
531
+ window.addEventListener( 'keydown', ( e ) => {
532
+ if ( isOpen ) { return; }
533
+ const mac = isMac();
534
+ const slash = e.key === '/' && !e.metaKey && !e.ctrlKey && !e.altKey;
535
+ const ctrlK = ( e.ctrlKey || e.metaKey ) && !e.altKey && !e.shiftKey && ( e.key === 'k' || e.key === 'K' );
536
+ const accessF = ( e.key === 'f' || e.key === 'F' || e.code === 'KeyF' ) && ( mac ? ( e.ctrlKey && e.altKey ) : ( e.altKey && ( e.shiftKey || !e.ctrlKey ) ) );
537
+ if ( ( slash || ctrlK || accessF ) && !isFormField( e.target ) ) { e.preventDefault(); open(); }
538
+ else if ( ctrlK && isFormField( e.target ) && e.target !== input ) { e.preventDefault(); open(); }
539
+ }, true );
540
+
541
+ const api = { open, close, toggle, refresh: () => refresh( true ), isOpen: () => isOpen, root, input, form, trigger: triggerBtn };
542
+ document.dispatchEvent( new CustomEvent( 'akds:palette-ready', { detail: api } ) );
543
+ return api;
544
+ }
545
+
546
+ const mod = { init, ICONS, MSG };
547
+ window.akdsSearchPalette = mod;
548
+ if ( typeof module !== 'undefined' && module.exports ) { module.exports = mod; }
549
+ }() );