main
html 155 lines 5.17 KB
Raw
1 <html>
2 <head>
3 <script type="module">
4 import { store } from "/components/settings/plugins/plugins-subsection-store.js";
5 </script>
6 </head>
7 <body>
8 <!--
9 Reusable plugin subsection for a settings tab.
10 Parent must pass data-tab attribute on the x-component tag, e.g.:
11 <x-component path="settings/plugins/plugins-subsection.html" data-tab="agent"></x-component>
12 The tab value is read at init time from the x-component element's dataset.
13 -->
14 <div x-data="(() => ({ context: $instantiate($store.pluginsSubsectionPrototype) }))()">
15 <template x-if="context">
16 <div x-create="context.init($el)"
17 x-destroy="context.cleanup()">
18 <template x-if="context.loading || context.plugins.length > 0">
19 <div>
20 <div class="section-title">Plugins</div>
21 <div class="section-description">
22 Plugin-specific settings for this tab.
23 </div>
24
25 <div x-show="context.loading" class="loading">
26 <span>Loading plugins...</span>
27 </div>
28
29 <div class="plugins-list">
30 <template x-for="plugin in context.plugins" :key="plugin.path || plugin.name">
31 <div class="plugin-card">
32 <div class="plugin-header">
33 <div class="plugin-heading">
34 <div class="plugin-title" x-text="plugin.display_name || plugin.name || '(unnamed plugin)'"></div>
35 <code class="plugin-path" x-text="plugin.path"></code>
36 </div>
37 <div class="plugin-actions">
38 <button type="button"
39 class="button"
40 title="Config"
41 @click="context.openPluginConfig(plugin)">
42 <x-icon class="icon" name="settings"></x-icon> Config
43 </button>
44 </div>
45 </div>
46 <div class="plugin-description" x-text="plugin.description || 'No description provided.'"></div>
47 </div>
48 </template>
49 </div>
50 </div>
51 </template>
52 </div>
53 </template>
54 </div>
55
56 <style>
57 /* Reused styles from plugin-list.html for uniformity */
58 .plugins-list {
59 margin-top: 0.25rem;
60 }
61
62 .plugin-card {
63 border: 1px solid var(--color-border);
64 border-radius: 10px;
65 padding: 0.75rem;
66 margin-top: 0.75rem;
67 background: var(--color-background);
68 }
69
70 .plugin-header {
71 display: flex;
72 align-items: flex-start;
73 justify-content: space-between;
74 gap: 1rem;
75 flex-wrap: nowrap;
76 min-width: 0;
77 }
78
79 .plugin-heading {
80 display: flex;
81 flex-direction: column;
82 min-width: 0;
83 flex: 1 1 auto;
84 }
85
86 .plugin-title {
87 font-weight: 600;
88 font-size: 1rem;
89 min-width: 0;
90 flex: 1 1 auto;
91 }
92
93 .plugin-actions {
94 display: flex;
95 align-items: center;
96 gap: 0.5rem;
97 padding-bottom: var(--spacing-sm);
98 flex-wrap: nowrap;
99 min-width: 0;
100 flex: 0 0 auto;
101 max-width: 100%;
102 justify-content: flex-end;
103 }
104
105 .plugin-description {
106 margin-top: 0.35rem;
107 color: var(--color-text-secondary);
108 font-size: var(--font-size-small);
109 }
110
111 .plugin-path {
112 margin-top: 0.35rem;
113 font-size: 0.85rem;
114 color: var(--color-text-muted);
115 min-width: 0;
116 max-width: 100%;
117 white-space: pre-wrap;
118 word-break: break-word;
119 overflow-wrap: anywhere;
120 }
121
122 .loading {
123 width: 100%;
124 text-align: center;
125 margin-top: 0.75rem;
126 margin-bottom: 0.75rem;
127 color: var(--color-secondary);
128 }
129
130 @media (max-width: 640px) {
131 .plugin-header {
132 flex-direction: column;
133 align-items: stretch;
134 }
135
136 .plugin-heading {
137 width: 100%;
138 flex: 0 0 auto;
139 }
140
141 .plugin-actions {
142 width: 100%;
143 justify-content: flex-start;
144 padding-bottom: 0;
145 flex-wrap: wrap;
146 }
147
148 .plugin-path {
149 white-space: normal;
150 word-break: break-word;
151 }
152 }
153 </style>
154 </body>
155 </html>