clay-server 2.27.0-beta.8 → 2.27.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 (72) hide show
  1. package/README.md +10 -0
  2. package/lib/daemon-projects.js +164 -0
  3. package/lib/daemon.js +13 -126
  4. package/lib/mates-identity.js +132 -0
  5. package/lib/mates-knowledge.js +113 -0
  6. package/lib/mates-prompts.js +398 -0
  7. package/lib/mates.js +40 -599
  8. package/lib/project-connection.js +2 -0
  9. package/lib/project-debate.js +19 -12
  10. package/lib/project-http.js +4 -2
  11. package/lib/project-loop.js +110 -48
  12. package/lib/project-mate-interaction.js +4 -0
  13. package/lib/project-notifications.js +210 -0
  14. package/lib/project-sessions.js +5 -2
  15. package/lib/project-user-message.js +2 -1
  16. package/lib/project.js +26 -2
  17. package/lib/public/app.js +1193 -8521
  18. package/lib/public/css/command-palette.css +14 -0
  19. package/lib/public/css/loop.css +301 -0
  20. package/lib/public/css/notifications-center.css +190 -0
  21. package/lib/public/css/rewind.css +6 -0
  22. package/lib/public/index.html +89 -35
  23. package/lib/public/modules/app-connection.js +160 -0
  24. package/lib/public/modules/app-cursors.js +473 -0
  25. package/lib/public/modules/app-debate-ui.js +389 -0
  26. package/lib/public/modules/app-dm.js +627 -0
  27. package/lib/public/modules/app-favicon.js +212 -0
  28. package/lib/public/modules/app-header.js +229 -0
  29. package/lib/public/modules/app-home-hub.js +600 -0
  30. package/lib/public/modules/app-loop-ui.js +589 -0
  31. package/lib/public/modules/app-loop-wizard.js +439 -0
  32. package/lib/public/modules/app-messages.js +1560 -0
  33. package/lib/public/modules/app-misc.js +299 -0
  34. package/lib/public/modules/app-notifications.js +372 -0
  35. package/lib/public/modules/app-panels.js +888 -0
  36. package/lib/public/modules/app-projects.js +798 -0
  37. package/lib/public/modules/app-rate-limit.js +451 -0
  38. package/lib/public/modules/app-rendering.js +597 -0
  39. package/lib/public/modules/app-skills-install.js +234 -0
  40. package/lib/public/modules/command-palette.js +27 -4
  41. package/lib/public/modules/input.js +31 -20
  42. package/lib/public/modules/scheduler-config.js +1532 -0
  43. package/lib/public/modules/scheduler-history.js +79 -0
  44. package/lib/public/modules/scheduler.js +33 -1554
  45. package/lib/public/modules/session-search.js +13 -1
  46. package/lib/public/modules/sidebar-mates.js +812 -0
  47. package/lib/public/modules/sidebar-mobile.js +1269 -0
  48. package/lib/public/modules/sidebar-projects.js +1449 -0
  49. package/lib/public/modules/sidebar-sessions.js +986 -0
  50. package/lib/public/modules/sidebar.js +232 -4591
  51. package/lib/public/modules/store.js +27 -0
  52. package/lib/public/modules/ws-ref.js +7 -0
  53. package/lib/public/style.css +1 -0
  54. package/lib/sdk-bridge.js +96 -717
  55. package/lib/sdk-message-processor.js +587 -0
  56. package/lib/sdk-message-queue.js +42 -0
  57. package/lib/sdk-skill-discovery.js +131 -0
  58. package/lib/server-admin.js +712 -0
  59. package/lib/server-auth.js +737 -0
  60. package/lib/server-dm.js +221 -0
  61. package/lib/server-mates.js +281 -0
  62. package/lib/server-palette.js +110 -0
  63. package/lib/server-settings.js +479 -0
  64. package/lib/server-skills.js +280 -0
  65. package/lib/server.js +246 -2755
  66. package/lib/sessions.js +11 -4
  67. package/lib/users-auth.js +146 -0
  68. package/lib/users-permissions.js +118 -0
  69. package/lib/users-preferences.js +210 -0
  70. package/lib/users.js +48 -398
  71. package/lib/ws-schema.js +498 -0
  72. package/package.json +1 -1
@@ -0,0 +1,1449 @@
1
+ // sidebar-projects.js - Project icon strip, context menus, emoji picker, drag-and-drop, worktree modal
2
+ // Extracted from sidebar.js (PR-36)
3
+
4
+ import { escapeHtml } from './utils.js';
5
+ import { iconHtml, refreshIcons } from './icons.js';
6
+ import { openProjectSettings } from './project-settings.js';
7
+ import { triggerShare } from './qrcode.js';
8
+ import { parseEmojis } from './markdown.js';
9
+
10
+ var _ctx = null;
11
+
12
+ // --- Project state ---
13
+ var cachedProjectList = [];
14
+ var cachedCurrentSlug = null;
15
+
16
+ // --- Project context menu ---
17
+ var projectCtxMenu = null;
18
+
19
+ // --- Project Access Popover ---
20
+ var projectAccessPopover = null;
21
+
22
+ // --- Emoji picker ---
23
+ var emojiPickerEl = null;
24
+
25
+ // --- Drag-and-drop state ---
26
+ var draggedSlug = null;
27
+ var draggedEl = null;
28
+
29
+ // --- Worktree folder collapse state (persisted in localStorage) ---
30
+ var wtCollapsed = {};
31
+ try {
32
+ wtCollapsed = JSON.parse(localStorage.getItem("clay-wt-collapsed") || "{}");
33
+ } catch (e) {}
34
+
35
+ var EMOJI_CATEGORIES = [
36
+ { id: "frequent", icon: "🕐", label: "Frequent", emojis: [
37
+ "😀","😎","🤓","🧠","💡","🔥","⚡","🚀",
38
+ "🎯","🎮","🎨","🎵","📦","📁","📝","💻",
39
+ "🖥️","⌨️","🔧","🛠️","⚙️","🧪","🔬","🧬",
40
+ "🌍","🌱","🌊","🌸","🍀","🌈","☀️","🌙",
41
+ "🐱","🐶","🐼","🦊","🦋","🐝","🐙","🦄",
42
+ "🍕","🍔","☕","🍩","🍎","🍇","🧁","🍣",
43
+ "❤️","💜","💙","💚","💛","🧡","🤍","🖤",
44
+ "⭐","✨","💎","🏆","👑","🎪","🎭","🃏",
45
+ ]},
46
+ { id: "smileys", icon: "😀", label: "Smileys & People", emojis: [
47
+ "😀","😃","😄","😁","😆","😅","🤣","😂",
48
+ "🙂","😊","😇","🥰","😍","🤩","😘","😗",
49
+ "😚","😙","🥲","😋","😛","😜","🤪","😝",
50
+ "🤑","🤗","🤭","🫢","🤫","🤔","🫡","🤐",
51
+ "🤨","😐","😑","😶","🫥","😏","😒","🙄",
52
+ "😬","🤥","😌","😔","😪","🤤","😴","😷",
53
+ "🤒","🤕","🤢","🤮","🥴","😵","🤯","🥳",
54
+ "🥸","😎","🤓","🧐","😕","🫤","😟","🙁",
55
+ "😮","😯","😲","😳","🥺","🥹","😦","😧",
56
+ "😨","😰","😥","😢","😭","😱","😖","😣",
57
+ "😞","😓","😩","😫","🥱","😤","😡","😠",
58
+ "🤬","😈","👿","💀","☠️","💩","🤡","👹",
59
+ "👺","👻","👽","👾","🤖","😺","😸","😹",
60
+ "😻","😼","😽","🙀","😿","😾","🙈","🙉",
61
+ "🙊","👋","🤚","🖐️","✋","🖖","🫱","🫲",
62
+ "🫳","🫴","👌","🤌","🤏","✌️","🤞","🫰",
63
+ "🤟","🤘","🤙","👈","👉","👆","🖕","👇",
64
+ "☝️","🫵","👍","👎","✊","👊","🤛","🤜",
65
+ "👏","🙌","🫶","👐","🤲","🤝","🙏","💪",
66
+ ]},
67
+ { id: "animals", icon: "🐻", label: "Animals & Nature", emojis: [
68
+ "🐶","🐱","🐭","🐹","🐰","🦊","🐻","🐼",
69
+ "🐻‍❄️","🐨","🐯","🦁","🐮","🐷","🐽","🐸",
70
+ "🐵","🙈","🙉","🙊","🐒","🐔","🐧","🐦",
71
+ "🐤","🐣","🐥","🦆","🦅","🦉","🦇","🐺",
72
+ "🐗","🐴","🦄","🐝","🪱","🐛","🦋","🐌",
73
+ "🐞","🐜","🪰","🪲","🪳","🦟","🦗","🕷️",
74
+ "🦂","🐢","🐍","🦎","🦖","🦕","🐙","🦑",
75
+ "🦐","🦞","🦀","🪸","🐡","🐠","🐟","🐬",
76
+ "🐳","🐋","🦈","🐊","🐅","🐆","🦓","🫏",
77
+ "🦍","🦧","🦣","🐘","🦛","🦏","🐪","🐫",
78
+ "🦒","🦘","🦬","🐃","🐂","🐄","🐎","🐖",
79
+ "🐏","🐑","🦙","🐐","🦌","🫎","🐕","🐩",
80
+ "🦮","🐕‍🦺","🐈","🐈‍⬛","🪶","🐓","🦃","🦤",
81
+ "🦚","🦜","🦢","🪿","🦩","🕊️","🐇","🦝",
82
+ "🦨","🦡","🦫","🦦","🦥","🐁","🐀","🐿️",
83
+ "🦔","🌵","🎄","🌲","🌳","🌴","🪵","🌱",
84
+ "🌿","☘️","🍀","🎍","🪴","🎋","🍃","🍂",
85
+ "🍁","🪺","🪹","🍄","🌾","💐","🌷","🌹",
86
+ "🥀","🪻","🌺","🌸","🌼","🌻","🌞","🌝",
87
+ "🌛","🌜","🌚","🌕","🌖","🌗","🌘","🌑",
88
+ "🌒","🌓","🌔","🌙","🌎","🌍","🌏","🪐",
89
+ "💫","⭐","🌟","✨","⚡","☄️","💥","🔥",
90
+ "🌪️","🌈","☀️","🌤️","⛅","🌥️","☁️","🌦️",
91
+ "🌧️","⛈️","🌩️","❄️","☃️","⛄","🌬️","💨",
92
+ "💧","💦","🫧","☔","☂️","🌊","🌫️",
93
+ ]},
94
+ { id: "food", icon: "🍔", label: "Food & Drink", emojis: [
95
+ "🍇","🍈","🍉","🍊","🍋","🍌","🍍","🥭",
96
+ "🍎","🍏","🍐","🍑","🍒","🍓","🫐","🥝",
97
+ "🍅","🫒","🥥","🥑","🍆","🥔","🥕","🌽",
98
+ "🌶️","🫑","🥒","🥬","🥦","🧄","🧅","🥜",
99
+ "🫘","🌰","🫚","🫛","🍞","🥐","🥖","🫓",
100
+ "🥨","🥯","🥞","🧇","🧀","🍖","🍗","🥩",
101
+ "🥓","🍔","🍟","🍕","🌭","🥪","🌮","🌯",
102
+ "🫔","🥙","🧆","🥚","🍳","🥘","🍲","🫕",
103
+ "🥣","🥗","🍿","🧈","🧂","🥫","🍱","🍘",
104
+ "🍙","🍚","🍛","🍜","🍝","🍠","🍢","🍣",
105
+ "🍤","🍥","🥮","🍡","🥟","🥠","🥡","🦀",
106
+ "🦞","🦐","🦑","🦪","🍦","🍧","🍨","🍩",
107
+ "🍪","🎂","🍰","🧁","🥧","🍫","🍬","🍭",
108
+ "🍮","🍯","🍼","🥛","☕","🫖","🍵","🍶",
109
+ "🍾","🍷","🍸","🍹","🍺","🍻","🥂","🥃",
110
+ "🫗","🥤","🧋","🧃","🧉","🧊",
111
+ ]},
112
+ { id: "activity", icon: "⚽", label: "Activity", emojis: [
113
+ "⚽","🏀","🏈","⚾","🥎","🎾","🏐","🏉",
114
+ "🥏","🎱","🪀","🏓","🏸","🏒","🏑","🥍",
115
+ "🏏","🪃","🥅","⛳","🪁","🛝","🏹","🎣",
116
+ "🤿","🥊","🥋","🎽","🛹","🛼","🛷","⛸️",
117
+ "🥌","🎿","⛷️","🏂","🪂","🏋️","🤸","🤺",
118
+ "⛹️","🤾","🏌️","🏇","🧘","🏄","🏊","🤽",
119
+ "🚣","🧗","🚵","🚴","🎪","🤹","🎭","🎨",
120
+ "🎬","🎤","🎧","🎼","🎹","🥁","🪘","🎷",
121
+ "🎺","🪗","🎸","🪕","🎻","🪈","🎲","♟️",
122
+ "🎯","🎳","🎮","🕹️","🧩","🪩",
123
+ ]},
124
+ { id: "travel", icon: "🚗", label: "Travel & Places", emojis: [
125
+ "🚗","🚕","🚙","🚌","🚎","🏎️","🚓","🚑",
126
+ "🚒","🚐","🛻","🚚","🚛","🚜","🛵","🏍️",
127
+ "🛺","🚲","🛴","🛹","🚏","🛣️","🛤️","⛽",
128
+ "🛞","🚨","🚥","🚦","🛑","🚧","⚓","🛟",
129
+ "⛵","🛶","🚤","🛳️","⛴️","🛥️","🚢","✈️",
130
+ "🛩️","🛫","🛬","🪂","💺","🚁","🚟","🚠",
131
+ "🚡","🛰️","🚀","🛸","🏠","🏡","🏘️","🏚️",
132
+ "🏗️","🏭","🏢","🏬","🏣","🏤","🏥","🏦",
133
+ "🏨","🏪","🏫","🏩","💒","🏛️","⛪","🕌",
134
+ "🛕","🕍","⛩️","🕋","⛲","⛺","🌁","🌃",
135
+ "🏙️","🌄","🌅","🌆","🌇","🌉","🗼","🗽",
136
+ "🗻","🏕️","🎠","🎡","🎢","🏖️","🏝️","🏜️",
137
+ "🌋","⛰️","🗺️","🧭","🏔️",
138
+ ]},
139
+ { id: "objects", icon: "💡", label: "Objects", emojis: [
140
+ "⌚","📱","📲","💻","⌨️","🖥️","🖨️","🖱️",
141
+ "🖲️","🕹️","🗜️","💽","💾","💿","📀","📼",
142
+ "📷","📸","📹","🎥","📽️","🎞️","📞","☎️",
143
+ "📟","📠","📺","📻","🎙️","🎚️","🎛️","🧭",
144
+ "⏱️","⏲️","⏰","🕰️","⌛","⏳","📡","🔋",
145
+ "🪫","🔌","💡","🔦","🕯️","🪔","🧯","🛢️",
146
+ "🛍️","💰","💴","💵","💶","💷","🪙","💸",
147
+ "💳","🧾","💹","✉️","📧","📨","📩","📤",
148
+ "📥","📦","📫","📬","📭","📮","🗳️","✏️",
149
+ "✒️","🖋️","🖊️","🖌️","🖍️","📝","💼","📁",
150
+ "📂","🗂️","📅","📆","🗒️","🗓️","📇","📈",
151
+ "📉","📊","📋","📌","📍","📎","🖇️","📏",
152
+ "📐","✂️","🗃️","🗄️","🗑️","🔒","🔓","🔏",
153
+ "🔐","🔑","🗝️","🔨","🪓","⛏️","⚒️","🛠️",
154
+ "🗡️","⚔️","💣","🪃","🏹","🛡️","🪚","🔧",
155
+ "🪛","🔩","⚙️","🗜️","⚖️","🦯","🔗","⛓️",
156
+ "🪝","🧰","🧲","🪜","⚗️","🧪","🧫","🧬",
157
+ "🔬","🔭","📡","💉","🩸","💊","🩹","🩼",
158
+ "🩺","🩻","🚪","🛗","🪞","🪟","🛏️","🛋️",
159
+ "🪑","🚽","🪠","🚿","🛁","🪤","🪒","🧴",
160
+ "🧷","🧹","🧺","🧻","🪣","🧼","🫧","🪥",
161
+ "🧽","🧯","🛒","🚬","⚰️","🪦","⚱️","🧿",
162
+ "🪬","🗿","🪧","🪪",
163
+ ]},
164
+ { id: "symbols", icon: "❤️", label: "Symbols", emojis: [
165
+ "❤️","🧡","💛","💚","💙","💜","🖤","🤍",
166
+ "🤎","💔","❤️‍🔥","❤️‍🩹","❣️","💕","💞","💓",
167
+ "💗","💖","💘","💝","💟","☮️","✝️","☪️",
168
+ "🕉️","☸️","🪯","✡️","🔯","🕎","☯️","☦️",
169
+ "🛐","⛎","♈","♉","♊","♋","♌","♍",
170
+ "♎","♏","♐","♑","♒","♓","🆔","⚛️",
171
+ "🉑","☢️","☣️","📴","📳","🈶","🈚","🈸",
172
+ "🈺","🈷️","✴️","🆚","💮","🉐","㊙️","㊗️",
173
+ "🈴","🈵","🈹","🈲","🅰️","🅱️","🆎","🆑",
174
+ "🅾️","🆘","❌","⭕","🛑","⛔","📛","🚫",
175
+ "💯","💢","♨️","🚷","🚯","🚳","🚱","🔞",
176
+ "📵","🚭","❗","❕","❓","❔","‼️","⁉️",
177
+ "🔅","🔆","〽️","⚠️","🚸","🔱","⚜️","🔰",
178
+ "♻️","✅","🈯","💹","❇️","✳️","❎","🌐",
179
+ "💠","Ⓜ️","🌀","💤","🏧","🚾","♿","🅿️",
180
+ "🛗","🈳","🈂️","🛂","🛃","🛄","🛅","🚹",
181
+ "🚺","🚼","⚧️","🚻","🚮","🎦","📶","🈁",
182
+ "🔣","ℹ️","🔤","🔡","🔠","🆖","🆗","🆙",
183
+ "🆒","🆕","🆓","0️⃣","1️⃣","2️⃣","3️⃣","4️⃣",
184
+ "5️⃣","6️⃣","7️⃣","8️⃣","9️⃣","🔟","🔢","#️⃣",
185
+ "*️⃣","⏏️","▶️","⏸️","⏯️","⏹️","⏺️","⏭️",
186
+ "⏮️","⏩","⏪","⏫","⏬","◀️","🔼","🔽",
187
+ "➡️","⬅️","⬆️","⬇️","↗️","↘️","↙️","↖️",
188
+ "↕️","↔️","↩️","↪️","⤴️","⤵️","🔀","🔁",
189
+ "🔂","🔄","🔃","🎵","🎶","✖️","➕","➖",
190
+ "➗","🟰","♾️","💲","💱","™️","©️","®️",
191
+ "〰️","➰","➿","🔚","🔙","🔛","🔝","🔜",
192
+ "✔️","☑️","🔘","🔴","🟠","🟡","🟢","🔵",
193
+ "🟣","⚫","⚪","🟤","🔺","🔻","🔸","🔹",
194
+ "🔶","🔷","🔳","🔲","▪️","▫️","◾","◽",
195
+ "◼️","◻️","🟥","🟧","🟨","🟩","🟦","🟪",
196
+ "⬛","⬜","🟫","🔈","🔇","🔉","🔊","🔔",
197
+ "🔕","📣","📢","👁️‍🗨️","💬","💭","🗯️","♠️",
198
+ "♣️","♥️","♦️","🃏","🎴","🀄","🕐","🕑",
199
+ "🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚","🕛",
200
+ ]},
201
+ { id: "flags", icon: "🏁", label: "Flags", emojis: [
202
+ "🏁","🚩","🎌","🏴","🏳️","🏳️‍🌈","🏳️‍⚧️","🏴‍☠️",
203
+ "🇦🇨","🇦🇩","🇦🇪","🇦🇫","🇦🇬","🇦🇮","🇦🇱","🇦🇲",
204
+ "🇦🇴","🇦🇶","🇦🇷","🇦🇸","🇦🇹","🇦🇺","🇦🇼","🇦🇽",
205
+ "🇦🇿","🇧🇦","🇧🇧","🇧🇩","🇧🇪","🇧🇫","🇧🇬","🇧🇭",
206
+ "🇧🇮","🇧🇯","🇧🇱","🇧🇲","🇧🇳","🇧🇴","🇧🇶","🇧🇷",
207
+ "🇧🇸","🇧🇹","🇧🇻","🇧🇼","🇧🇾","🇧🇿","🇨🇦","🇨🇨",
208
+ "🇨🇩","🇨🇫","🇨🇬","🇨🇭","🇨🇮","🇨🇰","🇨🇱","🇨🇲",
209
+ "🇨🇳","🇨🇴","🇨🇵","🇨🇷","🇨🇺","🇨🇻","🇨🇼","🇨🇽",
210
+ "🇨🇾","🇨🇿","🇩🇪","🇩🇬","🇩🇯","🇩🇰","🇩🇲","🇩🇴",
211
+ "🇩🇿","🇪🇦","🇪🇨","🇪🇪","🇪🇬","🇪🇭","🇪🇷","🇪🇸",
212
+ "🇪🇹","🇪🇺","🇫🇮","🇫🇯","🇫🇰","🇫🇲","🇫🇴","🇫🇷",
213
+ "🇬🇦","🇬🇧","🇬🇩","🇬🇪","🇬🇫","🇬🇬","🇬🇭","🇬🇮",
214
+ "🇬🇱","🇬🇲","🇬🇳","🇬🇵","🇬🇶","🇬🇷","🇬🇸","🇬🇹",
215
+ "🇬🇺","🇬🇼","🇬🇾","🇭🇰","🇭🇲","🇭🇳","🇭🇷","🇭🇹",
216
+ "🇭🇺","🇮🇨","🇮🇩","🇮🇪","🇮🇱","🇮🇲","🇮🇳","🇮🇴",
217
+ "🇮🇶","🇮🇷","🇮🇸","🇮🇹","🇯🇪","🇯🇲","🇯🇴","🇯🇵",
218
+ "🇰🇪","🇰🇬","🇰🇭","🇰🇮","🇰🇲","🇰🇳","🇰🇵","🇰🇷",
219
+ "🇰🇼","🇰🇾","🇰🇿","🇱🇦","🇱🇧","🇱🇨","🇱🇮","🇱🇰",
220
+ "🇱🇷","🇱🇸","🇱🇹","🇱🇺","🇱🇻","🇱🇾","🇲🇦","🇲🇨",
221
+ "🇲🇩","🇲🇪","🇲🇫","🇲🇬","🇲🇭","🇲🇰","🇲🇱","🇲🇲",
222
+ "🇲🇳","🇲🇴","🇲🇵","🇲🇶","🇲🇷","🇲🇸","🇲🇹","🇲🇺",
223
+ "🇲🇻","🇲🇼","🇲🇽","🇲🇾","🇲🇿","🇳🇦","🇳🇨","🇳🇪",
224
+ "🇳🇫","🇳🇬","🇳🇮","🇳🇱","🇳🇴","🇳🇵","🇳🇷","🇳🇺",
225
+ "🇳🇿","🇴🇲","🇵🇦","🇵🇪","🇵🇫","🇵🇬","🇵🇭","🇵🇰",
226
+ "🇵🇱","🇵🇲","🇵🇳","🇵🇷","🇵🇸","🇵🇹","🇵🇼","🇵🇾",
227
+ "🇶🇦","🇷🇪","🇷🇴","🇷🇸","🇷🇺","🇷🇼","🇸🇦","🇸🇧",
228
+ "🇸🇨","🇸🇩","🇸🇪","🇸🇬","🇸🇭","🇸🇮","🇸🇯","🇸🇰",
229
+ "🇸🇱","🇸🇲","🇸🇳","🇸🇴","🇸🇷","🇸🇸","🇸🇹","🇸🇻",
230
+ "🇸🇽","🇸🇾","🇸🇿","🇹🇦","🇹🇨","🇹🇩","🇹🇫","🇹🇬",
231
+ "🇹🇭","🇹🇯","🇹🇰","🇹🇱","🇹🇲","🇹🇳","🇹🇴","🇹🇷",
232
+ "🇹🇹","🇹🇻","🇹🇼","🇹🇿","🇺🇦","🇺🇬","🇺🇲","🇺🇳",
233
+ "🇺🇸","🇺🇾","🇺🇿","🇻🇦","🇻🇨","🇻🇪","🇻🇬","🇻🇮",
234
+ "🇻🇳","🇻🇺","🇼🇫","🇼🇸","🇽🇰","🇾🇪","🇾🇹","🇿🇦",
235
+ "🇿🇲","🇿🇼",
236
+ ]},
237
+ ];
238
+
239
+ export function initSidebarProjects(ctx) {
240
+ _ctx = ctx;
241
+
242
+ // Close project ctx menu and emoji picker on document click
243
+ document.addEventListener("click", function () {
244
+ closeProjectCtxMenu();
245
+ closeEmojiPicker();
246
+ });
247
+
248
+ // Initialize icon strip buttons
249
+ var addBtn = document.getElementById("icon-strip-add");
250
+ if (addBtn) {
251
+ addBtn.addEventListener("click", function () {
252
+ if (_ctx.openAddProjectModal) {
253
+ _ctx.openAddProjectModal();
254
+ } else {
255
+ var modal = _ctx.$("add-project-modal");
256
+ if (modal) modal.classList.remove("hidden");
257
+ }
258
+ });
259
+ addBtn.addEventListener("mouseenter", function () { _ctx.showIconTooltip(addBtn, "Add project"); });
260
+ addBtn.addEventListener("mouseleave", _ctx.hideIconTooltip);
261
+ }
262
+
263
+ var exploreBtn = document.getElementById("icon-strip-explore");
264
+ if (exploreBtn) {
265
+ exploreBtn.addEventListener("click", function () {
266
+ var fileBrowserBtn = _ctx.$("file-browser-btn");
267
+ if (fileBrowserBtn) fileBrowserBtn.click();
268
+ });
269
+ exploreBtn.addEventListener("mouseenter", function () { _ctx.showIconTooltip(exploreBtn, "File browser"); });
270
+ exploreBtn.addEventListener("mouseleave", _ctx.hideIconTooltip);
271
+ }
272
+
273
+ // Tooltip + click for home icon
274
+ var homeIcon = document.querySelector(".icon-strip-home");
275
+ if (homeIcon) {
276
+ homeIcon.addEventListener("mouseenter", function () { _ctx.showIconTooltip(homeIcon, "Clay"); });
277
+ homeIcon.addEventListener("mouseleave", _ctx.hideIconTooltip);
278
+ homeIcon.addEventListener("click", function (e) {
279
+ e.preventDefault();
280
+ if (_ctx.showHomeHub) _ctx.showHomeHub();
281
+ });
282
+ homeIcon.style.cursor = "pointer";
283
+ }
284
+
285
+ // Chevron dropdown on project name
286
+ var dropdownBtn = document.getElementById("title-bar-project-dropdown");
287
+ if (dropdownBtn) {
288
+ dropdownBtn.addEventListener("click", function (e) {
289
+ e.stopPropagation();
290
+ var current = null;
291
+ for (var i = 0; i < cachedProjectList.length; i++) {
292
+ if (cachedProjectList[i].slug === cachedCurrentSlug) {
293
+ current = cachedProjectList[i];
294
+ break;
295
+ }
296
+ }
297
+ if (!current) return;
298
+
299
+ if (projectCtxMenu) {
300
+ closeProjectCtxMenu();
301
+ dropdownBtn.classList.remove("open");
302
+ return;
303
+ }
304
+ dropdownBtn.classList.add("open");
305
+ showProjectCtxMenu(dropdownBtn, current.slug, current.name, current.icon, "below");
306
+ var observer = new MutationObserver(function () {
307
+ if (!projectCtxMenu) {
308
+ dropdownBtn.classList.remove("open");
309
+ observer.disconnect();
310
+ }
311
+ });
312
+ observer.observe(document.body, { childList: true });
313
+ });
314
+ }
315
+
316
+ return {
317
+ renderIconStrip: renderIconStrip,
318
+ renderProjectList: renderProjectList,
319
+ updateBadge: updateProjectBadge,
320
+ getEmojiCategories: getEmojiCategories
321
+ };
322
+ }
323
+
324
+ // --- Getters for cached state (used by mobile sheet in sidebar.js) ---
325
+ export function getCachedProjectList() { return cachedProjectList; }
326
+ export function getCachedCurrentSlug() { return cachedCurrentSlug; }
327
+
328
+ function getProjectAbbrev(name) {
329
+ if (!name) return "?";
330
+ var words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
331
+ if (words.length >= 2) {
332
+ return (words[0][0] + words[1][0]).toUpperCase();
333
+ }
334
+ return name.substring(0, 2).toUpperCase();
335
+ }
336
+
337
+ export { getProjectAbbrev };
338
+
339
+ // --- Project Access Popover ---
340
+
341
+ function closeAccessOnOutside(e) {
342
+ if (projectAccessPopover && !projectAccessPopover.contains(e.target)) closeProjectAccessPopover();
343
+ }
344
+ function closeAccessOnEscape(e) {
345
+ if (e.key === "Escape") closeProjectAccessPopover();
346
+ }
347
+
348
+ function closeProjectAccessPopover() {
349
+ if (projectAccessPopover) {
350
+ projectAccessPopover.remove();
351
+ projectAccessPopover = null;
352
+ document.removeEventListener("click", closeAccessOnOutside);
353
+ document.removeEventListener("keydown", closeAccessOnEscape);
354
+ }
355
+ }
356
+
357
+ function showProjectAccessPopover(anchorEl, slug) {
358
+ closeProjectAccessPopover();
359
+
360
+ var popover = document.createElement("div");
361
+ popover.className = "project-access-popover";
362
+ popover.innerHTML = '<div class="project-access-loading">Loading...</div>';
363
+ popover.addEventListener("click", function (e) { e.stopPropagation(); });
364
+ document.body.appendChild(popover);
365
+ projectAccessPopover = popover;
366
+
367
+ requestAnimationFrame(function () {
368
+ var rect = anchorEl.getBoundingClientRect();
369
+ popover.style.position = "fixed";
370
+ popover.style.left = (rect.right + 8) + "px";
371
+ popover.style.top = rect.top + "px";
372
+ popover.style.zIndex = "9999";
373
+ var popRect = popover.getBoundingClientRect();
374
+ if (popRect.right > window.innerWidth - 8) {
375
+ popover.style.left = (rect.left - popRect.width - 8) + "px";
376
+ }
377
+ if (popRect.bottom > window.innerHeight - 8) {
378
+ popover.style.top = (window.innerHeight - popRect.height - 8) + "px";
379
+ }
380
+ });
381
+
382
+ setTimeout(function () {
383
+ document.addEventListener("click", closeAccessOnOutside);
384
+ document.addEventListener("keydown", closeAccessOnEscape);
385
+ }, 0);
386
+
387
+ Promise.all([
388
+ fetch("/api/admin/projects/" + encodeURIComponent(slug) + "/access").then(function (r) { return r.json(); }),
389
+ fetch("/api/admin/users").then(function (r) { return r.json(); }),
390
+ ]).then(function (results) {
391
+ var access = results[0];
392
+ var usersData = results[1];
393
+ if (access.error || usersData.error) {
394
+ popover.innerHTML = '<div class="project-access-loading">Failed to load</div>';
395
+ return;
396
+ }
397
+ renderAccessPopover(popover, slug, access, usersData.users || []);
398
+ }).catch(function () {
399
+ popover.innerHTML = '<div class="project-access-loading">Failed to load</div>';
400
+ });
401
+ }
402
+
403
+ function renderAccessPopover(popover, slug, access, allUsers) {
404
+ var visibility = access.visibility || "public";
405
+ var allowedUsers = access.allowedUsers || [];
406
+ var ownerId = access.ownerId;
407
+
408
+ var selectableUsers = allUsers.filter(function (u) { return u.id !== ownerId; });
409
+
410
+ var html = '';
411
+ html += '<div class="project-access-header">';
412
+ html += '<span class="project-access-title">Project Access</span>';
413
+ html += '<button class="project-access-close">&times;</button>';
414
+ html += '</div>';
415
+
416
+ html += '<div class="project-access-section">';
417
+ html += '<label class="project-access-label">Visibility</label>';
418
+ html += '<div class="project-access-vis-row">';
419
+ html += '<button class="project-access-vis-btn' + (visibility === "private" ? ' active' : '') + '" data-vis="private">';
420
+ html += iconHtml("lock") + ' Private';
421
+ html += '</button>';
422
+ html += '<button class="project-access-vis-btn' + (visibility === "public" ? ' active' : '') + '" data-vis="public">';
423
+ html += iconHtml("globe") + ' Public';
424
+ html += '</button>';
425
+ html += '</div>';
426
+ html += '</div>';
427
+
428
+ html += '<div class="project-access-section project-access-users-section"' + (visibility !== "private" ? ' style="display:none"' : '') + '>';
429
+ html += '<label class="project-access-label">Allowed Users</label>';
430
+ html += '<div class="project-access-user-list">';
431
+ for (var i = 0; i < selectableUsers.length; i++) {
432
+ var u = selectableUsers[i];
433
+ var checked = allowedUsers.indexOf(u.id) !== -1 ? " checked" : "";
434
+ html += '<label class="project-access-user-item">';
435
+ html += '<input type="checkbox" data-uid="' + u.id + '"' + checked + '>';
436
+ html += '<span>' + escapeHtml(u.displayName || u.username || u.id) + '</span>';
437
+ html += '</label>';
438
+ }
439
+ if (selectableUsers.length === 0) {
440
+ html += '<div class="project-access-empty">No other users</div>';
441
+ }
442
+ html += '</div>';
443
+ html += '</div>';
444
+
445
+ popover.innerHTML = html;
446
+ refreshIcons();
447
+
448
+ popover.querySelector(".project-access-close").addEventListener("click", function () {
449
+ closeProjectAccessPopover();
450
+ });
451
+
452
+ popover.querySelectorAll(".project-access-vis-btn").forEach(function (btn) {
453
+ btn.addEventListener("click", function () {
454
+ var newVis = btn.dataset.vis;
455
+ popover.querySelectorAll(".project-access-vis-btn").forEach(function (b) { b.classList.remove("active"); });
456
+ btn.classList.add("active");
457
+ var usersSection = popover.querySelector(".project-access-users-section");
458
+ if (usersSection) usersSection.style.display = newVis === "private" ? "" : "none";
459
+ fetch("/api/admin/projects/" + encodeURIComponent(slug) + "/visibility", {
460
+ method: "PUT",
461
+ headers: { "Content-Type": "application/json" },
462
+ body: JSON.stringify({ visibility: newVis }),
463
+ });
464
+ });
465
+ });
466
+
467
+ popover.querySelectorAll('.project-access-user-item input[type="checkbox"]').forEach(function (cb) {
468
+ cb.addEventListener("change", function () {
469
+ var selected = [];
470
+ popover.querySelectorAll('.project-access-user-item input[type="checkbox"]:checked').forEach(function (c) {
471
+ selected.push(c.dataset.uid);
472
+ });
473
+ fetch("/api/admin/projects/" + encodeURIComponent(slug) + "/users", {
474
+ method: "PUT",
475
+ headers: { "Content-Type": "application/json" },
476
+ body: JSON.stringify({ allowedUsers: selected }),
477
+ });
478
+ });
479
+ });
480
+ }
481
+
482
+ // --- Project context menu ---
483
+
484
+ export function closeProjectCtxMenu() {
485
+ if (projectCtxMenu) {
486
+ projectCtxMenu.remove();
487
+ projectCtxMenu = null;
488
+ }
489
+ }
490
+
491
+ function showIconCtxMenu(anchorEl, slug, name) {
492
+ closeProjectCtxMenu();
493
+ if (_ctx.closeUserCtxMenu) _ctx.closeUserCtxMenu();
494
+ closeEmojiPicker();
495
+
496
+ var menu = document.createElement("div");
497
+ menu.className = "project-ctx-menu";
498
+
499
+ var isWorktree = slug.indexOf("--") !== -1;
500
+
501
+ if (isWorktree) {
502
+ var removeWtItem = document.createElement("button");
503
+ removeWtItem.className = "project-ctx-item project-ctx-delete";
504
+ removeWtItem.innerHTML = iconHtml("trash-2") + " <span>Remove Worktree</span>";
505
+ removeWtItem.addEventListener("click", function (e) {
506
+ e.stopPropagation();
507
+ closeProjectCtxMenu();
508
+ if (_ctx.ws && _ctx.connected) {
509
+ _ctx.ws.send(JSON.stringify({ type: "remove_project_check", slug: slug, name: name || slug }));
510
+ }
511
+ });
512
+ menu.appendChild(removeWtItem);
513
+ } else {
514
+ var iconItem = document.createElement("button");
515
+ iconItem.className = "project-ctx-item";
516
+ iconItem.innerHTML = iconHtml("smile") + " <span>Set Icon</span>";
517
+ iconItem.addEventListener("click", function (e) {
518
+ e.stopPropagation();
519
+ closeProjectCtxMenu();
520
+ showEmojiPicker(slug, anchorEl);
521
+ });
522
+ menu.appendChild(iconItem);
523
+
524
+ var wtItem = document.createElement("button");
525
+ wtItem.className = "project-ctx-item";
526
+ wtItem.innerHTML = iconHtml("git-branch") + " <span>Add Worktree</span>";
527
+ wtItem.addEventListener("click", function (e) {
528
+ e.stopPropagation();
529
+ closeProjectCtxMenu();
530
+ showWorktreeModal(slug, name || slug);
531
+ });
532
+ menu.appendChild(wtItem);
533
+ }
534
+
535
+ document.body.appendChild(menu);
536
+ projectCtxMenu = menu;
537
+ refreshIcons();
538
+
539
+ requestAnimationFrame(function () {
540
+ var rect = anchorEl.getBoundingClientRect();
541
+ menu.style.position = "fixed";
542
+ menu.style.left = (rect.right + 6) + "px";
543
+ menu.style.top = rect.top + "px";
544
+ var menuRect = menu.getBoundingClientRect();
545
+ if (menuRect.right > window.innerWidth - 8) {
546
+ menu.style.left = (rect.left - menuRect.width - 6) + "px";
547
+ }
548
+ if (menuRect.bottom > window.innerHeight - 8) {
549
+ menu.style.top = (window.innerHeight - menuRect.height - 8) + "px";
550
+ }
551
+ });
552
+ }
553
+
554
+ function showProjectCtxMenu(anchorEl, slug, name, icon, position) {
555
+ closeProjectCtxMenu();
556
+ if (_ctx.closeUserCtxMenu) _ctx.closeUserCtxMenu();
557
+ closeEmojiPicker();
558
+
559
+ var menu = document.createElement("div");
560
+ menu.className = "project-ctx-menu";
561
+
562
+ // --- Set Icon ---
563
+ var iconItem = document.createElement("button");
564
+ iconItem.className = "project-ctx-item";
565
+ iconItem.innerHTML = iconHtml("smile") + " <span>Set Icon</span>";
566
+ iconItem.addEventListener("click", function (e) {
567
+ e.stopPropagation();
568
+ closeProjectCtxMenu();
569
+ showEmojiPicker(slug, anchorEl);
570
+ });
571
+ menu.appendChild(iconItem);
572
+
573
+ // --- Project Settings ---
574
+ if (!_ctx.permissions || _ctx.permissions.projectSettings !== false) {
575
+ var settingsItem = document.createElement("button");
576
+ settingsItem.className = "project-ctx-item";
577
+ settingsItem.innerHTML = iconHtml("settings") + " <span>Project Settings</span>";
578
+ settingsItem.addEventListener("click", function (e) {
579
+ e.stopPropagation();
580
+ closeProjectCtxMenu();
581
+ openProjectSettings(slug, { slug: slug, name: name, icon: icon, projectOwnerId: _ctx.projectOwnerId });
582
+ });
583
+ menu.appendChild(settingsItem);
584
+ }
585
+
586
+ var sep1 = document.createElement("div");
587
+ sep1.className = "project-ctx-separator";
588
+ menu.appendChild(sep1);
589
+
590
+ // --- Share ---
591
+ var shareItem = document.createElement("button");
592
+ shareItem.className = "project-ctx-item";
593
+ shareItem.innerHTML = iconHtml("share") + " <span>Share</span>";
594
+ shareItem.addEventListener("click", function (e) {
595
+ e.stopPropagation();
596
+ closeProjectCtxMenu();
597
+ triggerShare();
598
+ });
599
+ menu.appendChild(shareItem);
600
+
601
+ // --- Manage Access ---
602
+ if (_ctx.multiUser && slug.indexOf("--") === -1) {
603
+ var isProjectOwner = _ctx.myUserId && _ctx.projectOwnerId && _ctx.myUserId === _ctx.projectOwnerId;
604
+ var isAdmin = _ctx.permissions && _ctx.permissions.projectSettings !== false;
605
+ if (isProjectOwner || isAdmin) {
606
+ var accessItem = document.createElement("button");
607
+ accessItem.className = "project-ctx-item";
608
+ accessItem.innerHTML = iconHtml("users") + " <span>Manage Access</span>";
609
+ accessItem.addEventListener("click", function (e) {
610
+ e.stopPropagation();
611
+ closeProjectCtxMenu();
612
+ showProjectAccessPopover(anchorEl, slug);
613
+ });
614
+ menu.appendChild(accessItem);
615
+ }
616
+ }
617
+
618
+ var sep2 = document.createElement("div");
619
+ sep2.className = "project-ctx-separator";
620
+ menu.appendChild(sep2);
621
+
622
+ // --- Add Worktree ---
623
+ var wtItem = document.createElement("button");
624
+ wtItem.className = "project-ctx-item";
625
+ wtItem.innerHTML = iconHtml("git-branch") + " <span>Add Worktree</span>";
626
+ wtItem.addEventListener("click", function (e) {
627
+ e.stopPropagation();
628
+ closeProjectCtxMenu();
629
+ showWorktreeModal(slug, name || slug);
630
+ });
631
+ menu.appendChild(wtItem);
632
+
633
+ if (!_ctx.permissions || _ctx.permissions.deleteProject !== false) {
634
+ var sep3 = document.createElement("div");
635
+ sep3.className = "project-ctx-separator";
636
+ menu.appendChild(sep3);
637
+
638
+ var deleteItem = document.createElement("button");
639
+ deleteItem.className = "project-ctx-item project-ctx-delete";
640
+ deleteItem.innerHTML = iconHtml("trash-2") + " <span>Remove Project</span>";
641
+ deleteItem.addEventListener("click", function (e) {
642
+ e.stopPropagation();
643
+ closeProjectCtxMenu();
644
+ if (_ctx.ws && _ctx.connected) {
645
+ _ctx.ws.send(JSON.stringify({ type: "remove_project_check", slug: slug, name: name }));
646
+ }
647
+ });
648
+ menu.appendChild(deleteItem);
649
+ }
650
+
651
+ document.body.appendChild(menu);
652
+ projectCtxMenu = menu;
653
+ refreshIcons();
654
+
655
+ requestAnimationFrame(function () {
656
+ var rect = anchorEl.getBoundingClientRect();
657
+ menu.style.position = "fixed";
658
+ if (position === "below") {
659
+ menu.style.left = rect.left + "px";
660
+ menu.style.top = (rect.bottom + 4) + "px";
661
+ } else {
662
+ menu.style.left = (rect.right + 6) + "px";
663
+ menu.style.top = rect.top + "px";
664
+ }
665
+ var menuRect = menu.getBoundingClientRect();
666
+ if (menuRect.right > window.innerWidth - 8) {
667
+ menu.style.left = (rect.left - menuRect.width - 6) + "px";
668
+ }
669
+ if (menuRect.bottom > window.innerHeight - 8) {
670
+ menu.style.top = (window.innerHeight - menuRect.height - 8) + "px";
671
+ }
672
+ });
673
+ }
674
+
675
+ // --- Emoji picker ---
676
+
677
+ function closeEmojiPicker() {
678
+ if (emojiPickerEl) {
679
+ emojiPickerEl.remove();
680
+ emojiPickerEl = null;
681
+ }
682
+ }
683
+
684
+ function showEmojiPicker(slug, anchorEl) {
685
+ closeEmojiPicker();
686
+
687
+ var picker = document.createElement("div");
688
+ picker.className = "emoji-picker";
689
+ picker.addEventListener("click", function (e) { e.stopPropagation(); });
690
+
691
+ var header = document.createElement("div");
692
+ header.className = "emoji-picker-header";
693
+ header.textContent = "Choose Icon";
694
+
695
+ var removeBtn = document.createElement("button");
696
+ removeBtn.className = "emoji-picker-remove";
697
+ removeBtn.textContent = "Remove";
698
+ removeBtn.addEventListener("click", function (e) {
699
+ e.stopPropagation();
700
+ closeEmojiPicker();
701
+ if (_ctx.ws && _ctx.connected) {
702
+ _ctx.ws.send(JSON.stringify({ type: "set_project_icon", slug: slug, icon: null }));
703
+ }
704
+ });
705
+ header.appendChild(removeBtn);
706
+ picker.appendChild(header);
707
+
708
+ var tabBar = document.createElement("div");
709
+ tabBar.className = "emoji-picker-tabs";
710
+ var tabBtns = [];
711
+
712
+ for (var t = 0; t < EMOJI_CATEGORIES.length; t++) {
713
+ (function (cat, idx) {
714
+ var tab = document.createElement("button");
715
+ tab.className = "emoji-picker-tab" + (idx === 0 ? " active" : "");
716
+ tab.textContent = cat.icon;
717
+ tab.title = cat.label;
718
+ tab.addEventListener("click", function (e) {
719
+ e.stopPropagation();
720
+ switchCategory(idx);
721
+ });
722
+ tabBar.appendChild(tab);
723
+ tabBtns.push(tab);
724
+ })(EMOJI_CATEGORIES[t], t);
725
+ }
726
+ parseEmojis(tabBar);
727
+ picker.appendChild(tabBar);
728
+
729
+ var scrollArea = document.createElement("div");
730
+ scrollArea.className = "emoji-picker-scroll";
731
+
732
+ var grid = document.createElement("div");
733
+ grid.className = "emoji-picker-grid";
734
+ scrollArea.appendChild(grid);
735
+ picker.appendChild(scrollArea);
736
+
737
+ function buildGrid(emojis) {
738
+ grid.innerHTML = "";
739
+ for (var i = 0; i < emojis.length; i++) {
740
+ (function (emoji) {
741
+ var btn = document.createElement("button");
742
+ btn.className = "emoji-picker-item";
743
+ btn.textContent = emoji;
744
+ btn.addEventListener("click", function (e) {
745
+ e.stopPropagation();
746
+ closeEmojiPicker();
747
+ if (_ctx.ws && _ctx.connected) {
748
+ _ctx.ws.send(JSON.stringify({ type: "set_project_icon", slug: slug, icon: emoji }));
749
+ }
750
+ });
751
+ grid.appendChild(btn);
752
+ })(emojis[i]);
753
+ }
754
+ parseEmojis(grid);
755
+ scrollArea.scrollTop = 0;
756
+ }
757
+
758
+ function switchCategory(idx) {
759
+ for (var j = 0; j < tabBtns.length; j++) {
760
+ tabBtns[j].classList.toggle("active", j === idx);
761
+ }
762
+ buildGrid(EMOJI_CATEGORIES[idx].emojis);
763
+ }
764
+
765
+ buildGrid(EMOJI_CATEGORIES[0].emojis);
766
+
767
+ document.body.appendChild(picker);
768
+ emojiPickerEl = picker;
769
+
770
+ requestAnimationFrame(function () {
771
+ var rect = anchorEl.getBoundingClientRect();
772
+ picker.style.left = (rect.right + 6) + "px";
773
+ picker.style.top = rect.top + "px";
774
+ var pRect = picker.getBoundingClientRect();
775
+ if (pRect.right > window.innerWidth - 8) {
776
+ picker.style.left = (rect.left - pRect.width - 6) + "px";
777
+ }
778
+ if (pRect.bottom > window.innerHeight - 8) {
779
+ picker.style.top = (window.innerHeight - pRect.height - 8) + "px";
780
+ }
781
+ });
782
+ }
783
+
784
+ // --- Rename prompt ---
785
+ function showProjectRename(slug, currentName) {
786
+ var nameEl = document.getElementById("title-bar-project-name");
787
+ if (!nameEl) return;
788
+
789
+ var input = document.createElement("input");
790
+ input.type = "text";
791
+ input.className = "project-rename-input";
792
+ input.value = currentName || "";
793
+
794
+ var originalText = nameEl.textContent;
795
+ nameEl.textContent = "";
796
+ nameEl.appendChild(input);
797
+ input.focus();
798
+ input.select();
799
+
800
+ var committed = false;
801
+
802
+ function commitRename() {
803
+ if (committed) return;
804
+ committed = true;
805
+ var newName = input.value.trim();
806
+ if (newName && newName !== currentName && _ctx.ws && _ctx.connected) {
807
+ _ctx.ws.send(JSON.stringify({ type: "set_project_title", slug: slug, title: newName }));
808
+ nameEl.textContent = newName;
809
+ } else {
810
+ nameEl.textContent = originalText;
811
+ }
812
+ }
813
+
814
+ input.addEventListener("keydown", function (e) {
815
+ e.stopPropagation();
816
+ if (e.key === "Enter") { e.preventDefault(); commitRename(); }
817
+ if (e.key === "Escape") { e.preventDefault(); committed = true; nameEl.textContent = originalText; }
818
+ });
819
+ input.addEventListener("blur", commitRename);
820
+ input.addEventListener("click", function (e) { e.stopPropagation(); });
821
+ }
822
+
823
+ // --- Drag-and-drop ---
824
+
825
+ function showTrashZone() {
826
+ var addBtn = document.getElementById("icon-strip-add");
827
+ if (!addBtn) return;
828
+ addBtn.style.display = "none";
829
+
830
+ var existing = document.getElementById("icon-strip-trash");
831
+ if (existing) existing.remove();
832
+
833
+ var trash = document.createElement("div");
834
+ trash.id = "icon-strip-trash";
835
+ trash.className = "icon-strip-trash";
836
+ trash.innerHTML = iconHtml("trash-2");
837
+ addBtn.parentNode.insertBefore(trash, addBtn.nextSibling);
838
+ refreshIcons();
839
+
840
+ trash.addEventListener("mouseenter", function () { _ctx.showIconTooltip(trash, "Remove project"); });
841
+ trash.addEventListener("mouseleave", _ctx.hideIconTooltip);
842
+
843
+ trash.addEventListener("dragover", function (e) {
844
+ e.preventDefault();
845
+ e.dataTransfer.dropEffect = "move";
846
+ trash.classList.add("drag-hover");
847
+ });
848
+ trash.addEventListener("dragleave", function () {
849
+ trash.classList.remove("drag-hover");
850
+ });
851
+ trash.addEventListener("drop", function (e) {
852
+ e.preventDefault();
853
+ trash.classList.remove("drag-hover");
854
+ var slug = e.dataTransfer.getData("text/plain");
855
+ if (slug && _ctx.ws && _ctx.connected) {
856
+ _ctx.ws.send(JSON.stringify({ type: "remove_project_check", slug: slug }));
857
+ }
858
+ });
859
+ }
860
+
861
+ function hideTrashZone() {
862
+ var trash = document.getElementById("icon-strip-trash");
863
+ if (trash) trash.remove();
864
+ var addBtn = document.getElementById("icon-strip-add");
865
+ if (addBtn) addBtn.style.display = "";
866
+ }
867
+
868
+ function clearDragIndicators() {
869
+ var items = document.querySelectorAll(".icon-strip-item.drag-over-above, .icon-strip-item.drag-over-below");
870
+ for (var i = 0; i < items.length; i++) {
871
+ items[i].classList.remove("drag-over-above", "drag-over-below");
872
+ }
873
+ }
874
+
875
+ function setupDragHandlers(el, slug) {
876
+ el.setAttribute("draggable", "true");
877
+
878
+ el.addEventListener("dragstart", function (e) {
879
+ draggedSlug = slug;
880
+ draggedEl = el;
881
+ e.dataTransfer.effectAllowed = "move";
882
+ e.dataTransfer.setData("text/plain", slug);
883
+
884
+ var ghost = document.createElement("div");
885
+ ghost.textContent = el.textContent.trim().split("\n")[0];
886
+ ghost.style.cssText = "position:fixed;left:-200px;top:-200px;width:38px;height:38px;border-radius:12px;" +
887
+ "background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;" +
888
+ "font-size:15px;font-weight:600;pointer-events:none;z-index:-1;";
889
+ document.body.appendChild(ghost);
890
+ e.dataTransfer.setDragImage(ghost, 19, 19);
891
+ setTimeout(function () { ghost.remove(); }, 0);
892
+
893
+ setTimeout(function () { el.classList.add("dragging"); }, 0);
894
+ _ctx.hideIconTooltip();
895
+ showTrashZone();
896
+ });
897
+
898
+ el.addEventListener("dragover", function (e) {
899
+ e.preventDefault();
900
+ if (!draggedSlug || draggedSlug === slug) return;
901
+ e.dataTransfer.dropEffect = "move";
902
+
903
+ clearDragIndicators();
904
+ var rect = el.getBoundingClientRect();
905
+ var midY = rect.top + rect.height / 2;
906
+ if (e.clientY < midY) {
907
+ el.classList.add("drag-over-above");
908
+ } else {
909
+ el.classList.add("drag-over-below");
910
+ }
911
+ });
912
+
913
+ el.addEventListener("dragleave", function () {
914
+ el.classList.remove("drag-over-above", "drag-over-below");
915
+ });
916
+
917
+ el.addEventListener("drop", function (e) {
918
+ e.preventDefault();
919
+ clearDragIndicators();
920
+ if (!draggedSlug || draggedSlug === slug) return;
921
+
922
+ var rect = el.getBoundingClientRect();
923
+ var midY = rect.top + rect.height / 2;
924
+ var insertBefore = e.clientY < midY;
925
+
926
+ var container = document.getElementById("icon-strip-projects");
927
+ var items = container.querySelectorAll(".icon-strip-item");
928
+ var slugs = [];
929
+ for (var i = 0; i < items.length; i++) {
930
+ if (items[i].dataset.slug !== draggedSlug) {
931
+ slugs.push(items[i].dataset.slug);
932
+ }
933
+ }
934
+ var targetIdx = slugs.indexOf(slug);
935
+ if (!insertBefore) targetIdx++;
936
+ slugs.splice(targetIdx, 0, draggedSlug);
937
+
938
+ if (_ctx.ws && _ctx.connected) {
939
+ _ctx.ws.send(JSON.stringify({ type: "reorder_projects", slugs: slugs }));
940
+ }
941
+ });
942
+
943
+ el.addEventListener("dragend", function () {
944
+ el.classList.remove("dragging");
945
+ clearDragIndicators();
946
+ draggedSlug = null;
947
+ draggedEl = null;
948
+ hideTrashZone();
949
+ });
950
+ }
951
+
952
+ // --- Worktree folder collapse ---
953
+
954
+ function setWtCollapsed(slug, collapsed) {
955
+ wtCollapsed[slug] = collapsed;
956
+ try { localStorage.setItem("clay-wt-collapsed", JSON.stringify(wtCollapsed)); } catch (e) {}
957
+ }
958
+
959
+ function groupProjects(projects) {
960
+ var parents = [];
961
+ var wtByParent = {};
962
+ for (var i = 0; i < projects.length; i++) {
963
+ var p = projects[i];
964
+ if (p.isWorktree && p.parentSlug) {
965
+ if (!wtByParent[p.parentSlug]) wtByParent[p.parentSlug] = [];
966
+ wtByParent[p.parentSlug].push(p);
967
+ } else {
968
+ parents.push(p);
969
+ }
970
+ }
971
+ return { parents: parents, wtByParent: wtByParent };
972
+ }
973
+
974
+ // --- Icon item creation ---
975
+
976
+ function createIconItem(p, currentSlug) {
977
+ var currentDmUserId = _ctx.getCurrentDmUserId ? _ctx.getCurrentDmUserId() : null;
978
+ var el = document.createElement("a");
979
+ var isActive = p.slug === currentSlug && !currentDmUserId;
980
+ el.className = "icon-strip-item" + (isActive ? " active" : "");
981
+ el.href = "/p/" + p.slug + "/";
982
+ el.dataset.slug = p.slug;
983
+
984
+ if (p.icon) {
985
+ var emojiSpan = document.createElement("span");
986
+ emojiSpan.className = "project-emoji";
987
+ emojiSpan.textContent = p.icon;
988
+ parseEmojis(emojiSpan);
989
+ el.appendChild(emojiSpan);
990
+ } else {
991
+ el.appendChild(document.createTextNode(getProjectAbbrev(p.name)));
992
+ }
993
+
994
+ var pill = document.createElement("span");
995
+ pill.className = "icon-strip-pill";
996
+ el.appendChild(pill);
997
+
998
+ var statusDot = document.createElement("span");
999
+ statusDot.className = "icon-strip-status";
1000
+ if (p.isProcessing) statusDot.classList.add("processing");
1001
+ el.appendChild(statusDot);
1002
+
1003
+ var projectBadge = document.createElement("span");
1004
+ projectBadge.className = "icon-strip-project-badge";
1005
+ if (p.unread > 0 && !isActive) {
1006
+ projectBadge.textContent = p.unread > 99 ? "99+" : String(p.unread);
1007
+ projectBadge.classList.add("has-unread");
1008
+ }
1009
+ el.appendChild(projectBadge);
1010
+
1011
+ if (p.pendingPermissions > 0 && !isActive) {
1012
+ el.classList.add("has-pending-perm");
1013
+ }
1014
+
1015
+ (function (name, elem) {
1016
+ elem.addEventListener("mouseenter", function () { _ctx.showIconTooltip(elem, name); });
1017
+ elem.addEventListener("mouseleave", _ctx.hideIconTooltip);
1018
+ })(p.name, el);
1019
+
1020
+ (function (slug) {
1021
+ el.addEventListener("click", function (e) {
1022
+ e.preventDefault();
1023
+ if (_ctx.switchProject) _ctx.switchProject(slug);
1024
+ });
1025
+ })(p.slug);
1026
+
1027
+ return el;
1028
+ }
1029
+
1030
+ // --- Worktree creation modal ---
1031
+
1032
+ function showWorktreeModal(parentSlug, parentName) {
1033
+ var existing = document.getElementById("wt-modal-container");
1034
+ if (existing) existing.remove();
1035
+
1036
+ var container = document.createElement("div");
1037
+ container.id = "wt-modal-container";
1038
+
1039
+ var overlay = document.createElement("div");
1040
+ overlay.className = "wt-modal-overlay";
1041
+ container.appendChild(overlay);
1042
+
1043
+ var modal = document.createElement("div");
1044
+ modal.className = "wt-modal";
1045
+
1046
+ var title = document.createElement("div");
1047
+ title.className = "wt-modal-title";
1048
+ title.textContent = "Add Worktree \u2014 " + parentName;
1049
+ modal.appendChild(title);
1050
+
1051
+ var branchLabel = document.createElement("label");
1052
+ branchLabel.className = "wt-modal-label";
1053
+ branchLabel.textContent = "Branch name";
1054
+ modal.appendChild(branchLabel);
1055
+
1056
+ var branchInput = document.createElement("input");
1057
+ branchInput.type = "text";
1058
+ branchInput.className = "wt-modal-input";
1059
+ branchInput.placeholder = "feat/my-feature";
1060
+ branchInput.autocomplete = "off";
1061
+ branchInput.spellcheck = false;
1062
+ modal.appendChild(branchInput);
1063
+
1064
+ var baseLabel = document.createElement("label");
1065
+ baseLabel.className = "wt-modal-label";
1066
+ baseLabel.textContent = "Base branch";
1067
+ modal.appendChild(baseLabel);
1068
+
1069
+ var baseSelect = document.createElement("select");
1070
+ baseSelect.className = "wt-modal-input";
1071
+ var defaultOpt = document.createElement("option");
1072
+ defaultOpt.value = "main";
1073
+ defaultOpt.textContent = "main";
1074
+ baseSelect.appendChild(defaultOpt);
1075
+ modal.appendChild(baseSelect);
1076
+
1077
+ fetch("/p/" + parentSlug + "/api/branches")
1078
+ .then(function (res) { return res.json(); })
1079
+ .then(function (data) {
1080
+ baseSelect.innerHTML = "";
1081
+ var branches = data.branches || ["main"];
1082
+ var defBranch = data.defaultBranch || "main";
1083
+ for (var i = 0; i < branches.length; i++) {
1084
+ var opt = document.createElement("option");
1085
+ opt.value = branches[i];
1086
+ opt.textContent = branches[i];
1087
+ if (branches[i] === defBranch) opt.selected = true;
1088
+ baseSelect.appendChild(opt);
1089
+ }
1090
+ })
1091
+ .catch(function () {});
1092
+
1093
+ var errorDiv = document.createElement("div");
1094
+ errorDiv.className = "wt-modal-error";
1095
+ modal.appendChild(errorDiv);
1096
+
1097
+ var actions = document.createElement("div");
1098
+ actions.className = "wt-modal-actions";
1099
+
1100
+ var cancelBtn = document.createElement("button");
1101
+ cancelBtn.className = "wt-modal-btn";
1102
+ cancelBtn.textContent = "Cancel";
1103
+ actions.appendChild(cancelBtn);
1104
+
1105
+ var createBtn = document.createElement("button");
1106
+ createBtn.className = "wt-modal-btn primary";
1107
+ createBtn.textContent = "Create";
1108
+ actions.appendChild(createBtn);
1109
+
1110
+ modal.appendChild(actions);
1111
+ container.appendChild(modal);
1112
+ document.body.appendChild(container);
1113
+ branchInput.focus();
1114
+
1115
+ function closeModal() { container.remove(); }
1116
+
1117
+ function doCreate() {
1118
+ var branch = branchInput.value.trim();
1119
+ var base = baseSelect.value.trim() || null;
1120
+ if (!branch) {
1121
+ errorDiv.textContent = "Branch name is required";
1122
+ errorDiv.classList.add("visible");
1123
+ return;
1124
+ }
1125
+ var dirName = branch.replace(/\//g, "-");
1126
+ createBtn.disabled = true;
1127
+ createBtn.textContent = "Creating...";
1128
+ errorDiv.classList.remove("visible");
1129
+
1130
+ if (_ctx.ws && _ctx.connected) {
1131
+ _ctx.ws.send(JSON.stringify({
1132
+ type: "create_worktree",
1133
+ branch: branch,
1134
+ dirName: dirName,
1135
+ baseBranch: base
1136
+ }));
1137
+ }
1138
+
1139
+ var handler = function (event) {
1140
+ var msg;
1141
+ try { msg = JSON.parse(event.data); } catch (e) { return; }
1142
+ if (msg.type === "create_worktree_result") {
1143
+ _ctx.ws.removeEventListener("message", handler);
1144
+ if (msg.ok) {
1145
+ closeModal();
1146
+ if (msg.slug && _ctx.switchProject) _ctx.switchProject(msg.slug);
1147
+ } else {
1148
+ createBtn.disabled = false;
1149
+ createBtn.textContent = "Create";
1150
+ errorDiv.textContent = msg.error || "Failed to create worktree";
1151
+ errorDiv.classList.add("visible");
1152
+ }
1153
+ }
1154
+ };
1155
+ _ctx.ws.addEventListener("message", handler);
1156
+ }
1157
+
1158
+ overlay.addEventListener("click", closeModal);
1159
+ cancelBtn.addEventListener("click", closeModal);
1160
+ createBtn.addEventListener("click", doCreate);
1161
+ branchInput.addEventListener("keydown", function (e) {
1162
+ if (e.key === "Enter") doCreate();
1163
+ if (e.key === "Escape") closeModal();
1164
+ });
1165
+ baseSelect.addEventListener("keydown", function (e) {
1166
+ if (e.key === "Enter") doCreate();
1167
+ if (e.key === "Escape") closeModal();
1168
+ });
1169
+ }
1170
+
1171
+ // --- Render icon strip ---
1172
+
1173
+ export function renderIconStrip(projects, currentSlug) {
1174
+ cachedProjectList = projects;
1175
+ cachedCurrentSlug = currentSlug;
1176
+
1177
+ var container = document.getElementById("icon-strip-projects");
1178
+ if (!container) return;
1179
+ container.innerHTML = "";
1180
+
1181
+ var currentDmUserId = _ctx.getCurrentDmUserId ? _ctx.getCurrentDmUserId() : null;
1182
+ var grouped = groupProjects(projects);
1183
+
1184
+ for (var i = 0; i < grouped.parents.length; i++) {
1185
+ var p = grouped.parents[i];
1186
+ var worktrees = grouped.wtByParent[p.slug] || [];
1187
+ var hasWorktrees = worktrees.length > 0;
1188
+
1189
+ if (!hasWorktrees) {
1190
+ var el = createIconItem(p, currentSlug);
1191
+ (function (slug, name, elem) {
1192
+ elem.addEventListener("contextmenu", function (e) {
1193
+ e.preventDefault();
1194
+ e.stopPropagation();
1195
+ showIconCtxMenu(elem, slug, name);
1196
+ });
1197
+ })(p.slug, p.name || p.slug, el);
1198
+ setupDragHandlers(el, p.slug);
1199
+ container.appendChild(el);
1200
+ continue;
1201
+ }
1202
+
1203
+ // Folder group for parent + worktrees
1204
+ var folder = document.createElement("div");
1205
+ folder.className = "icon-strip-group";
1206
+ folder.dataset.parentSlug = p.slug;
1207
+ if (wtCollapsed[p.slug]) folder.classList.add("collapsed");
1208
+
1209
+ if (!p.isProcessing) {
1210
+ for (var wpi = 0; wpi < worktrees.length; wpi++) {
1211
+ if (worktrees[wpi].isProcessing) { p.isProcessing = true; break; }
1212
+ }
1213
+ }
1214
+
1215
+ var header = createIconItem(p, currentSlug);
1216
+ header.classList.add("folder-header");
1217
+ (function (slug, name, elem) {
1218
+ elem.addEventListener("contextmenu", function (e) {
1219
+ e.preventDefault();
1220
+ e.stopPropagation();
1221
+ showIconCtxMenu(elem, slug, name);
1222
+ });
1223
+ })(p.slug, p.name || p.slug, header);
1224
+ setupDragHandlers(header, p.slug);
1225
+
1226
+ var chevron = document.createElement("span");
1227
+ chevron.className = "icon-strip-group-chevron";
1228
+ chevron.innerHTML = '<i data-lucide="git-branch"></i>';
1229
+ (function (parentSlug, folderEl) {
1230
+ chevron.addEventListener("click", function (e) {
1231
+ e.preventDefault();
1232
+ e.stopPropagation();
1233
+ var nowCollapsed = folderEl.classList.toggle("collapsed");
1234
+ setWtCollapsed(parentSlug, nowCollapsed);
1235
+ });
1236
+ chevron.addEventListener("contextmenu", function (e) {
1237
+ e.preventDefault();
1238
+ e.stopPropagation();
1239
+ });
1240
+ })(p.slug, folder);
1241
+ chevron.setAttribute("data-tip", "Toggle worktrees");
1242
+ header.appendChild(chevron);
1243
+ folder.appendChild(header);
1244
+
1245
+ var itemsContainer = document.createElement("div");
1246
+ itemsContainer.className = "icon-strip-group-items";
1247
+
1248
+ for (var wi = 0; wi < worktrees.length; wi++) {
1249
+ (function (wt) {
1250
+ var wtEl = document.createElement("a");
1251
+ var isWtActive = wt.slug === currentSlug && !currentDmUserId;
1252
+ var isAccessible = wt.worktreeAccessible !== false;
1253
+ wtEl.className = "icon-strip-wt-item" + (isWtActive ? " active" : "") + (!isAccessible ? " wt-disabled" : "");
1254
+ wtEl.href = "/p/" + wt.slug + "/";
1255
+ wtEl.dataset.slug = wt.slug;
1256
+
1257
+ var abbrev = document.createElement("span");
1258
+ abbrev.className = "wt-branch-abbrev";
1259
+ abbrev.textContent = getProjectAbbrev(wt.name);
1260
+ wtEl.appendChild(abbrev);
1261
+
1262
+ var wtStatus = document.createElement("span");
1263
+ wtStatus.className = "icon-strip-status";
1264
+ if (wt.isProcessing) wtStatus.classList.add("processing");
1265
+ wtEl.appendChild(wtStatus);
1266
+
1267
+ var tooltipText = wt.name;
1268
+ if (!isAccessible) {
1269
+ tooltipText += " (outside project path, cannot be accessed)";
1270
+ }
1271
+ (function (text, elem) {
1272
+ elem.addEventListener("mouseenter", function () { _ctx.showIconTooltip(elem, text); });
1273
+ elem.addEventListener("mouseleave", _ctx.hideIconTooltip);
1274
+ })(tooltipText, wtEl);
1275
+
1276
+ if (isAccessible) {
1277
+ (function (slug) {
1278
+ wtEl.addEventListener("click", function (e) {
1279
+ e.preventDefault();
1280
+ if (_ctx.switchProject) _ctx.switchProject(slug);
1281
+ });
1282
+ })(wt.slug);
1283
+ } else {
1284
+ wtEl.addEventListener("click", function (e) {
1285
+ e.preventDefault();
1286
+ });
1287
+ }
1288
+
1289
+ if (isAccessible) {
1290
+ (function (slug, name, elem) {
1291
+ elem.addEventListener("contextmenu", function (e) {
1292
+ e.preventDefault();
1293
+ e.stopPropagation();
1294
+ showIconCtxMenu(elem, slug, name);
1295
+ });
1296
+ })(wt.slug, wt.name, wtEl);
1297
+ } else {
1298
+ wtEl.addEventListener("contextmenu", function (e) {
1299
+ e.preventDefault();
1300
+ e.stopPropagation();
1301
+ });
1302
+ }
1303
+
1304
+ if (wt.pendingPermissions > 0 && !isWtActive) {
1305
+ wtEl.classList.add("has-pending-perm");
1306
+ }
1307
+
1308
+ itemsContainer.appendChild(wtEl);
1309
+ })(worktrees[wi]);
1310
+ }
1311
+
1312
+ var hasWtPendingPerm = false;
1313
+ for (var wpi2 = 0; wpi2 < worktrees.length; wpi2++) {
1314
+ if (worktrees[wpi2].pendingPermissions > 0) { hasWtPendingPerm = true; break; }
1315
+ }
1316
+ if (hasWtPendingPerm) folder.classList.remove("collapsed");
1317
+
1318
+ var addBtn = document.createElement("button");
1319
+ addBtn.className = "icon-strip-group-add";
1320
+ addBtn.textContent = "+";
1321
+ (function (parentSlug, parentName, btn) {
1322
+ btn.addEventListener("click", function (e) {
1323
+ e.preventDefault();
1324
+ e.stopPropagation();
1325
+ showWorktreeModal(parentSlug, parentName);
1326
+ });
1327
+ btn.addEventListener("mouseenter", function () { _ctx.showIconTooltip(btn, "New worktree"); });
1328
+ btn.addEventListener("mouseleave", _ctx.hideIconTooltip);
1329
+ })(p.slug, p.name, addBtn);
1330
+ itemsContainer.appendChild(addBtn);
1331
+
1332
+ folder.appendChild(itemsContainer);
1333
+ container.appendChild(folder);
1334
+ }
1335
+
1336
+ // Update home icon active state
1337
+ var homeIcon = document.querySelector(".icon-strip-home");
1338
+ if (homeIcon) {
1339
+ if ((!currentSlug || projects.length === 0) && !currentDmUserId) {
1340
+ homeIcon.classList.add("active");
1341
+ } else {
1342
+ homeIcon.classList.remove("active");
1343
+ }
1344
+ }
1345
+
1346
+ renderProjectList(projects, currentSlug);
1347
+
1348
+ try { lucide.createIcons({ nodes: [container] }); } catch (e) {}
1349
+ }
1350
+
1351
+ function renderProjectList(projects, currentSlug) {
1352
+ var list = document.getElementById("project-list");
1353
+ if (!list) return;
1354
+ list.innerHTML = "";
1355
+
1356
+ var grouped = groupProjects(projects);
1357
+
1358
+ for (var i = 0; i < grouped.parents.length; i++) {
1359
+ var p = grouped.parents[i];
1360
+ var worktrees = grouped.wtByParent[p.slug] || [];
1361
+
1362
+ if (worktrees.length === 0) {
1363
+ list.appendChild(createMobileProjectItem(p, currentSlug, false));
1364
+ continue;
1365
+ }
1366
+
1367
+ var folderDiv = document.createElement("div");
1368
+ folderDiv.className = "mobile-project-folder";
1369
+ if (wtCollapsed[p.slug]) folderDiv.classList.add("collapsed");
1370
+
1371
+ var headerEl = createMobileProjectItem(p, currentSlug, false);
1372
+ var chevron = document.createElement("span");
1373
+ chevron.className = "mobile-folder-chevron";
1374
+ chevron.innerHTML = "&#9660;";
1375
+ (function (parentSlug, fDiv) {
1376
+ chevron.addEventListener("click", function (e) {
1377
+ e.preventDefault();
1378
+ e.stopPropagation();
1379
+ var nowCollapsed = fDiv.classList.toggle("collapsed");
1380
+ setWtCollapsed(parentSlug, nowCollapsed);
1381
+ });
1382
+ })(p.slug, folderDiv);
1383
+ headerEl.appendChild(chevron);
1384
+ folderDiv.appendChild(headerEl);
1385
+
1386
+ var wtList = document.createElement("div");
1387
+ wtList.className = "mobile-folder-items";
1388
+ for (var wi = 0; wi < worktrees.length; wi++) {
1389
+ var isAccessible = worktrees[wi].worktreeAccessible !== false;
1390
+ var wtItem = createMobileProjectItem(worktrees[wi], currentSlug, true);
1391
+ if (!isAccessible) wtItem.classList.add("wt-disabled");
1392
+ if (!isAccessible) {
1393
+ wtItem.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); });
1394
+ }
1395
+ wtList.appendChild(wtItem);
1396
+ }
1397
+ folderDiv.appendChild(wtList);
1398
+ list.appendChild(folderDiv);
1399
+ }
1400
+ }
1401
+
1402
+ function createMobileProjectItem(p, currentSlug, isWorktree) {
1403
+ var el = document.createElement("button");
1404
+ el.className = "mobile-project-item" + (p.slug === currentSlug ? " active" : "") + (isWorktree ? " wt-item" : "");
1405
+
1406
+ var abbrev = document.createElement("span");
1407
+ abbrev.className = "mobile-project-abbrev";
1408
+ if (p.icon) {
1409
+ abbrev.textContent = p.icon;
1410
+ parseEmojis(abbrev);
1411
+ } else {
1412
+ abbrev.textContent = getProjectAbbrev(p.name);
1413
+ }
1414
+ el.appendChild(abbrev);
1415
+
1416
+ var name = document.createElement("span");
1417
+ name.className = "mobile-project-name";
1418
+ name.textContent = p.name;
1419
+ el.appendChild(name);
1420
+
1421
+ if (p.isProcessing) {
1422
+ var dot = document.createElement("span");
1423
+ dot.className = "mobile-project-processing";
1424
+ el.appendChild(dot);
1425
+ }
1426
+
1427
+ el.addEventListener("click", function () {
1428
+ if (_ctx.switchProject) _ctx.switchProject(p.slug);
1429
+ if (_ctx.closeSidebar) _ctx.closeSidebar();
1430
+ });
1431
+
1432
+ return el;
1433
+ }
1434
+
1435
+ export function getEmojiCategories() { return EMOJI_CATEGORIES; }
1436
+
1437
+ export function updateProjectBadge(slug, count) {
1438
+ var icon = document.querySelector('.icon-strip-item[data-slug="' + slug + '"]');
1439
+ if (!icon) return;
1440
+ var badge = icon.querySelector(".icon-strip-project-badge");
1441
+ if (!badge) return;
1442
+ if (count > 0) {
1443
+ badge.textContent = count > 99 ? "99+" : String(count);
1444
+ badge.classList.add("has-unread");
1445
+ } else {
1446
+ badge.textContent = "";
1447
+ badge.classList.remove("has-unread");
1448
+ }
1449
+ }