10
<body>
11
<div x-data>
12
<template x-if="$store.pluginInstallStore">
13
- <div x-init="$store.pluginInstallStore.setTab('store')"
13
+ <div x-create="$store.pluginInstallStore.setTab('store')"
14
x-destroy="$store.pluginInstallStore.refreshPluginList()">
15
16
<ul class="nav nav-tabs" role="tablist">
34
</li>
35
</ul>
36
37
- <!-- ═══ Store Tab ═══ -->
38
- <div x-show="$store.pluginInstallStore.activeTab === 'store'"
39
- x-init="$store.pluginInstallStore.resetIndex(); $store.pluginInstallStore.fetchIndex()">
40
-
41
- <div class="pi-browse-shell">
42
- <section class="pi-browse-hero">
43
- <div class="pi-browse-copy">
44
- <div class="pi-browse-eyebrow">Community Marketplace</div>
45
- <h2 class="pi-browse-title">Discover community plugins for Agent Zero</h2>
46
- <p class="pi-browse-description">
47
- These plugins are provided by the community of Agent Zero. Contribute by publishing
48
- in the <a href="https://github.com/agent0ai/a0-plugins" target="_blank">index
49
- repository</a>.
50
- </p>
51
- </div>
52
- <div class="pi-browse-summary" x-text="$store.pluginInstallStore.browseResultsSummary">
53
- </div>
54
- </section>
55
-
56
- <div class="pi-index-toolbar">
57
- <label class="pi-search-field">
58
- <span class="material-symbols-outlined">search</span>
59
- <input type="text" class="pi-search-input" x-model="$store.pluginInstallStore.search"
60
- @input="$store.pluginInstallStore.page = 1"
61
- placeholder="Search plugins, authors, or tags...">
62
- </label>
63
-
64
- <label class="pi-sort-field">
65
- <span class="pi-field-label">Sort</span>
66
- <select class="pi-sort-select" x-model="$store.pluginInstallStore.sortBy">
67
- <option value="stars">Stars</option>
68
- <option value="name">Name</option>
69
- </select>
70
- </label>
71
- </div>
72
-
73
- <div class="pi-filter-row" x-show="$store.pluginInstallStore.browseFilters.length > 1">
74
- <template x-for="filter in $store.pluginInstallStore.browseFilters" :key="filter.key">
75
- <button type="button" class="pi-filter-chip"
76
- :class="{ active: $store.pluginInstallStore.browseFilter === filter.key }"
77
- @click="$store.pluginInstallStore.setBrowseFilter(filter.key)">
78
- <span x-text="filter.label"></span>
79
- <span class="pi-filter-count" x-text="filter.count"></span>
80
- </button>
81
- </template>
82
- </div>
83
-
84
- <div x-show="$store.pluginInstallStore.loading" class="pi-state-card pi-state-loading">
85
- <span class="material-symbols-outlined">progress_activity</span>
86
- <div>
87
- <div class="pi-state-title">Loading marketplace</div>
88
- <div class="pi-state-text"
89
- x-text="$store.pluginInstallStore.loadingMessage || 'Loading plugins...'"></div>
90
- </div>
91
- </div>
92
-
93
- <div x-show="$store.pluginInstallStore.error && !$store.pluginInstallStore.loading"
94
- class="pi-state-card pi-state-error">
95
- <span class="material-symbols-outlined">error</span>
96
- <div>
97
- <div class="pi-state-title">Marketplace unavailable</div>
98
- <div class="pi-state-text" x-text="$store.pluginInstallStore.error"></div>
99
- </div>
100
- </div>
101
-
102
- <div x-show="$store.pluginInstallStore.index && !$store.pluginInstallStore.loading && $store.pluginInstallStore.filteredPlugins.length > 0"
103
- class="pi-grid">
104
- <template x-for="plugin in $store.pluginInstallStore.paginatedPlugins" :key="plugin.key">
105
- <button type="button" class="pi-card"
106
- @click="$store.pluginInstallStore.openDetail(plugin)">
107
- <div class="pi-card-thumb">
108
- <template x-if="plugin.thumbnail">
109
- <img :src="plugin.thumbnail" :alt="plugin.title" loading="lazy">
110
- </template>
111
- <template x-if="!plugin.thumbnail">
112
- <span class="material-symbols-outlined pi-card-placeholder">extension</span>
113
- </template>
114
-
115
- <template x-if="plugin.installed">
116
- <span class="pi-card-installed-pill">Installed</span>
117
- </template>
118
- </div>
119
-
120
- <div class="pi-card-body">
121
- <div class="pi-card-head">
122
- <div class="pi-card-heading">
123
- <h3 class="pi-card-title" x-text="plugin.title || plugin.key"></h3>
124
- <div class="pi-card-subtitle"
125
- x-text="$store.pluginInstallStore.getBrowseSubtitle(plugin)">
126
- </div>
127
- </div>
128
- <span class="material-symbols-outlined pi-card-arrow">arrow_outward</span>
129
- </div>
130
-
131
- <div class="pi-card-desc"
132
- x-text="$store.pluginInstallStore.truncate(plugin.description, 110)">
133
- </div>
134
- </div>
135
-
136
- <div class="pi-card-footer">
137
- <span class="pi-card-stars">
138
- <span class="material-symbols-outlined">star</span>
139
- <span x-text="plugin.stars || 0"></span>
140
- </span>
141
-
142
- <template x-if="$store.pluginInstallStore.getBrowsePrimaryTag(plugin)">
143
- <span class="pi-card-tag"
144
- x-text="$store.pluginInstallStore.getBrowsePrimaryTag(plugin)">
145
- </span>
146
- </template>
147
- </div>
148
- </button>
149
- </template>
150
- </div>
151
-
152
- <div x-show="$store.pluginInstallStore.index && !$store.pluginInstallStore.loading && $store.pluginInstallStore.filteredPlugins.length === 0"
153
- class="pi-state-card pi-state-empty">
154
- <span class="material-symbols-outlined">search_off</span>
155
- <div>
156
- <div class="pi-state-title">No plugins match this view</div>
157
- <div class="pi-state-text">
158
- Try a different search or switch to another marketplace filter.
159
- </div>
160
- </div>
161
- </div>
162
-
163
- <div x-show="$store.pluginInstallStore.totalPages > 1" class="pi-pagination">
164
- <button class="button" :disabled="$store.pluginInstallStore.page <= 1"
165
- @click="$store.pluginInstallStore.setPage($store.pluginInstallStore.page - 1)">
166
- <span class="material-symbols-outlined">chevron_left</span>
167
- </button>
168
- <span class="pi-page-info"
169
- x-text="'Page ' + $store.pluginInstallStore.page + ' of ' + $store.pluginInstallStore.totalPages">
170
- </span>
171
- <button class="button"
172
- :disabled="$store.pluginInstallStore.page >= $store.pluginInstallStore.totalPages"
173
- @click="$store.pluginInstallStore.setPage($store.pluginInstallStore.page + 1)">
174
- <span class="material-symbols-outlined">chevron_right</span>
175
- </button>
176
- </div>
177
- </div>
178
- </div>
179
-
180
- <!-- ═══ Git Tab ═══ -->
181
- <div x-show="$store.pluginInstallStore.activeTab === 'git'"
182
- x-init="$store.pluginInstallStore.resetGit()">
183
-
184
- <div class="pi-form-group">
185
- <label class="pi-label">Git Repository URL</label>
186
- <input type="text" class="pi-input" x-model="$store.pluginInstallStore.gitUrl"
187
- :disabled="$store.pluginInstallStore.loading"
188
- placeholder="https://github.com/user/my-plugin.git">
189
- </div>
190
-
191
- <div class="pi-form-group">
192
- <label class="pi-label">Access Token <span class="pi-optional">(optional, for private
193
- repos)</span></label>
194
- <input type="password" class="pi-input" x-model="$store.pluginInstallStore.gitToken"
195
- :disabled="$store.pluginInstallStore.loading" placeholder="ghp_xxxx or glpat-xxxx">
196
- <div class="pi-hint">Token is used only for cloning and is not stored.</div>
197
- </div>
198
-
199
- <div class="pi-actions">
200
- <button class="button confirm" @click="$store.pluginInstallStore.installGit()"
201
- :disabled="$store.pluginInstallStore.loading || !$store.pluginInstallStore.gitUrl.trim()">
202
- <template x-if="$store.pluginInstallStore.loading">
203
- <span class="pi-button-loading">
204
- <span class="spinner"></span>
205
- <span x-text="$store.pluginInstallStore.loadingMessage || 'Cloning...'"></span>
206
- </span>
207
- </template>
208
- <template x-if="!$store.pluginInstallStore.loading">
209
- <span>
210
- <span class="icon material-symbols-outlined">terminal</span> Clone & Install
211
- </span>
212
- </template>
213
- </button>
214
- </div>
215
-
216
- <div x-show="$store.pluginInstallStore.error" class="pi-error">
217
- <span x-text="$store.pluginInstallStore.error"></span>
218
- </div>
219
-
220
- <div x-show="$store.pluginInstallStore.result" class="pi-result">
221
- <div class="pi-result-icon">
222
- <span class="material-symbols-outlined">check_circle</span>
223
- </div>
224
- <div class="pi-result-text">
225
- <strong
226
- x-text="'Plugin installed: ' + ($store.pluginInstallStore.result?.title || $store.pluginInstallStore.result?.plugin_name || '')"></strong>
227
- <div class="pi-result-path" x-text="$store.pluginInstallStore.result?.path || ''"></div>
228
- </div>
229
- </div>
230
- </div>
231
-
232
- <!-- ═══ ZIP Tab ═══ -->
233
- <div x-show="$store.pluginInstallStore.activeTab === 'zip'"
234
- x-init="$store.pluginInstallStore.resetZip()">
235
-
236
- <div class="pi-upload-section">
237
- <label for="plugin-zip-file" class="pi-upload-btn button confirm"
238
- :class="{ 'pi-has-file': $store.pluginInstallStore.zipFile }">
239
- <span class="icon material-symbols-outlined">upload_file</span>
240
- <span x-text="$store.pluginInstallStore.zipFileName || 'Select Plugin ZIP File'"></span>
241
- </label>
242
- <input type="file" id="plugin-zip-file" accept=".zip" style="display:none"
243
- @change="$store.pluginInstallStore.handleFileUpload($event)">
244
- <div class="pi-hint">
245
- The ZIP should contain a folder with a plugin.yaml file.
246
- </div>
247
- </div>
248
-
249
- <div x-show="$store.pluginInstallStore.zipFile" class="pi-actions-center">
250
- <button class="button confirm" @click="$store.pluginInstallStore.installZip()"
251
- :disabled="$store.pluginInstallStore.loading">
252
- <span class="icon material-symbols-outlined">download</span> Install Plugin
253
- </button>
254
- </div>
255
-
256
- <div x-show="$store.pluginInstallStore.loading" class="pi-loading">
257
- <span x-text="$store.pluginInstallStore.loadingMessage || 'Processing...'"></span>
258
- </div>
259
-
260
- <div x-show="$store.pluginInstallStore.error" class="pi-error">
261
- <span x-text="$store.pluginInstallStore.error"></span>
262
- </div>
263
-
264
- <div x-show="$store.pluginInstallStore.result" class="pi-result">
265
- <div class="pi-result-icon">
266
- <span class="material-symbols-outlined">check_circle</span>
267
- </div>
268
- <div class="pi-result-text">
269
- <strong
270
- x-text="'Plugin installed: ' + ($store.pluginInstallStore.result?.title || $store.pluginInstallStore.result?.plugin_name || '')"></strong>
271
- <div class="pi-result-path" x-text="$store.pluginInstallStore.result?.path || ''"></div>
272
- </div>
273
- </div>
274
- </div>
37
+ <template x-if="$store.pluginInstallStore.activeTab === 'store'">
38
+ <x-component path="/plugins/plugin_installer/webui/install-index.html"></x-component>
39
+ </template>
40
+
41
+ <template x-if="$store.pluginInstallStore.activeTab === 'git'">
42
+ <x-component path="/plugins/plugin_installer/webui/install-git.html"></x-component>
43
+ </template>
44
+
45
+ <template x-if="$store.pluginInstallStore.activeTab === 'zip'">
46
+ <x-component path="/plugins/plugin_installer/webui/install-zip.html"></x-component>
47
+ </template>
48
49
</div>
50
</template>
592
</style>
593
</body>
594
822
-</html>
\ No newline at end of file
595
+</html>