@things-factory/integration-label-studio 9.1.19

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 (152) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/EXTERNAL_DATA_SOURCING.md +484 -0
  3. package/IMPLEMENTATION_GUIDE.md +469 -0
  4. package/INTEGRATION.md +279 -0
  5. package/README.md +1014 -0
  6. package/SETUP_GUIDE.md +577 -0
  7. package/TEST_GUIDE.md +387 -0
  8. package/UI_CUSTOMIZATION.md +395 -0
  9. package/USER_SYNC_GUIDE.md +514 -0
  10. package/client/bootstrap.ts +1 -0
  11. package/client/index.ts +1 -0
  12. package/client/label-studio-label-page.ts +52 -0
  13. package/client/label-studio-project-create.ts +216 -0
  14. package/client/label-studio-project-list.ts +214 -0
  15. package/client/label-studio-wrapper.ts +294 -0
  16. package/client/route.ts +15 -0
  17. package/client/tsconfig.json +13 -0
  18. package/config/config.development.js +124 -0
  19. package/config/config.production.js +182 -0
  20. package/dist-client/bootstrap.d.ts +1 -0
  21. package/dist-client/bootstrap.js +2 -0
  22. package/dist-client/bootstrap.js.map +1 -0
  23. package/dist-client/index.d.ts +1 -0
  24. package/dist-client/index.js +2 -0
  25. package/dist-client/index.js.map +1 -0
  26. package/dist-client/label-studio-label-page.d.ts +8 -0
  27. package/dist-client/label-studio-label-page.js +54 -0
  28. package/dist-client/label-studio-label-page.js.map +1 -0
  29. package/dist-client/label-studio-project-create.d.ts +16 -0
  30. package/dist-client/label-studio-project-create.js +235 -0
  31. package/dist-client/label-studio-project-create.js.map +1 -0
  32. package/dist-client/label-studio-project-list.d.ts +16 -0
  33. package/dist-client/label-studio-project-list.js +222 -0
  34. package/dist-client/label-studio-project-list.js.map +1 -0
  35. package/dist-client/label-studio-wrapper.d.ts +57 -0
  36. package/dist-client/label-studio-wrapper.js +304 -0
  37. package/dist-client/label-studio-wrapper.js.map +1 -0
  38. package/dist-client/route.d.ts +1 -0
  39. package/dist-client/route.js +14 -0
  40. package/dist-client/route.js.map +1 -0
  41. package/dist-client/tsconfig.tsbuildinfo +1 -0
  42. package/dist-server/controller/label-studio-role-mapper.d.ts +35 -0
  43. package/dist-server/controller/label-studio-role-mapper.js +65 -0
  44. package/dist-server/controller/label-studio-role-mapper.js.map +1 -0
  45. package/dist-server/controller/user-provisioning-service.d.ts +66 -0
  46. package/dist-server/controller/user-provisioning-service.js +264 -0
  47. package/dist-server/controller/user-provisioning-service.js.map +1 -0
  48. package/dist-server/index.d.ts +7 -0
  49. package/dist-server/index.js +19 -0
  50. package/dist-server/index.js.map +1 -0
  51. package/dist-server/route/label-studio-sso.d.ts +2 -0
  52. package/dist-server/route/label-studio-sso.js +156 -0
  53. package/dist-server/route/label-studio-sso.js.map +1 -0
  54. package/dist-server/route/webhook.d.ts +65 -0
  55. package/dist-server/route/webhook.js +248 -0
  56. package/dist-server/route/webhook.js.map +1 -0
  57. package/dist-server/route.d.ts +1 -0
  58. package/dist-server/route.js +21 -0
  59. package/dist-server/route.js.map +1 -0
  60. package/dist-server/service/ai-prediction-service.d.ts +27 -0
  61. package/dist-server/service/ai-prediction-service.js +222 -0
  62. package/dist-server/service/ai-prediction-service.js.map +1 -0
  63. package/dist-server/service/dataset-labeling-integration.d.ts +44 -0
  64. package/dist-server/service/dataset-labeling-integration.js +512 -0
  65. package/dist-server/service/dataset-labeling-integration.js.map +1 -0
  66. package/dist-server/service/external-data-source-service.d.ts +78 -0
  67. package/dist-server/service/external-data-source-service.js +415 -0
  68. package/dist-server/service/external-data-source-service.js.map +1 -0
  69. package/dist-server/service/index.d.ts +12 -0
  70. package/dist-server/service/index.js +27 -0
  71. package/dist-server/service/index.js.map +1 -0
  72. package/dist-server/service/label-studio-sso-service.d.ts +38 -0
  73. package/dist-server/service/label-studio-sso-service.js +98 -0
  74. package/dist-server/service/label-studio-sso-service.js.map +1 -0
  75. package/dist-server/service/ml/ml-backend-service.d.ts +23 -0
  76. package/dist-server/service/ml/ml-backend-service.js +153 -0
  77. package/dist-server/service/ml/ml-backend-service.js.map +1 -0
  78. package/dist-server/service/prediction/prediction-management.d.ts +32 -0
  79. package/dist-server/service/prediction/prediction-management.js +299 -0
  80. package/dist-server/service/prediction/prediction-management.js.map +1 -0
  81. package/dist-server/service/project/project-management.d.ts +36 -0
  82. package/dist-server/service/project/project-management.js +309 -0
  83. package/dist-server/service/project/project-management.js.map +1 -0
  84. package/dist-server/service/task/task-management.d.ts +42 -0
  85. package/dist-server/service/task/task-management.js +372 -0
  86. package/dist-server/service/task/task-management.js.map +1 -0
  87. package/dist-server/service/user-provisioning/user-sync-mutation.d.ts +28 -0
  88. package/dist-server/service/user-provisioning/user-sync-mutation.js +111 -0
  89. package/dist-server/service/user-provisioning/user-sync-mutation.js.map +1 -0
  90. package/dist-server/service/webhook/webhook-management.d.ts +21 -0
  91. package/dist-server/service/webhook/webhook-management.js +134 -0
  92. package/dist-server/service/webhook/webhook-management.js.map +1 -0
  93. package/dist-server/tsconfig.tsbuildinfo +1 -0
  94. package/dist-server/types/dataset-labeling-types.d.ts +71 -0
  95. package/dist-server/types/dataset-labeling-types.js +259 -0
  96. package/dist-server/types/dataset-labeling-types.js.map +1 -0
  97. package/dist-server/types/label-studio-types.d.ts +128 -0
  98. package/dist-server/types/label-studio-types.js +494 -0
  99. package/dist-server/types/label-studio-types.js.map +1 -0
  100. package/dist-server/types/prediction-types.d.ts +39 -0
  101. package/dist-server/types/prediction-types.js +121 -0
  102. package/dist-server/types/prediction-types.js.map +1 -0
  103. package/dist-server/utils/annotation-exporter.d.ts +104 -0
  104. package/dist-server/utils/annotation-exporter.js +261 -0
  105. package/dist-server/utils/annotation-exporter.js.map +1 -0
  106. package/dist-server/utils/label-config-builder.d.ts +117 -0
  107. package/dist-server/utils/label-config-builder.js +286 -0
  108. package/dist-server/utils/label-config-builder.js.map +1 -0
  109. package/dist-server/utils/label-studio-api-client.d.ts +180 -0
  110. package/dist-server/utils/label-studio-api-client.js +401 -0
  111. package/dist-server/utils/label-studio-api-client.js.map +1 -0
  112. package/dist-server/utils/media-url-extractor.d.ts +45 -0
  113. package/dist-server/utils/media-url-extractor.js +152 -0
  114. package/dist-server/utils/media-url-extractor.js.map +1 -0
  115. package/dist-server/utils/task-transformer.d.ts +108 -0
  116. package/dist-server/utils/task-transformer.js +260 -0
  117. package/dist-server/utils/task-transformer.js.map +1 -0
  118. package/package.json +47 -0
  119. package/server/SERVER_STRUCTURE.md +351 -0
  120. package/server/controller/label-studio-role-mapper.ts +76 -0
  121. package/server/controller/user-provisioning-service.ts +340 -0
  122. package/server/index.ts +19 -0
  123. package/server/route/label-studio-sso.ts +194 -0
  124. package/server/route/webhook.ts +304 -0
  125. package/server/route.ts +35 -0
  126. package/server/service/ai-prediction-service.ts +239 -0
  127. package/server/service/dataset-labeling-integration.ts +590 -0
  128. package/server/service/external-data-source-service.ts +438 -0
  129. package/server/service/index.ts +24 -0
  130. package/server/service/label-studio-sso-service.ts +108 -0
  131. package/server/service/labeling-scenario-service.ts.deprecated +566 -0
  132. package/server/service/ml/ml-backend-service.ts +127 -0
  133. package/server/service/prediction/prediction-management.ts +281 -0
  134. package/server/service/project/project-management.ts +284 -0
  135. package/server/service/task/task-management.ts +363 -0
  136. package/server/service/user-provisioning/user-sync-mutation.ts +80 -0
  137. package/server/service/webhook/webhook-management.ts +109 -0
  138. package/server/tsconfig.json +11 -0
  139. package/server/types/dataset-labeling-types.ts +181 -0
  140. package/server/types/global.d.ts +23 -0
  141. package/server/types/label-studio-types.ts +346 -0
  142. package/server/types/prediction-types.ts +86 -0
  143. package/server/types/scenario-types.ts.deprecated +362 -0
  144. package/server/utils/annotation-exporter.ts +340 -0
  145. package/server/utils/label-config-builder.ts +340 -0
  146. package/server/utils/label-studio-api-client.ts +487 -0
  147. package/server/utils/media-url-extractor.ts +193 -0
  148. package/server/utils/task-transformer.ts +342 -0
  149. package/test-ai-prediction.js +268 -0
  150. package/test-dataset-integration.js +449 -0
  151. package/test-simple.js +89 -0
  152. package/things-factory.config.js +12 -0
@@ -0,0 +1,304 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ /**
3
+ * Label Studio Wrapper Component for Things Factory
4
+ *
5
+ * This component embeds Label Studio within Things Factory using an iframe.
6
+ * It uses subdomain-based cookie sharing for SSO authentication, eliminating
7
+ * the need for proxying.
8
+ *
9
+ * ## Key Features:
10
+ * - Direct Label Studio access (no proxy needed)
11
+ * - Automatic SSO cookie setup via shared domain
12
+ * - Hidden header for seamless integration
13
+ * - Floating Action Buttons (FAB) for Reload and Fullscreen
14
+ *
15
+ * ## Architecture:
16
+ * 1. Client calls /label-studio/sso/setup
17
+ * 2. Backend sets cookie with shared domain (e.g., .nubison.localhost)
18
+ * 3. Browser → Label Studio (direct, with cookie)
19
+ *
20
+ * ## Configuration Requirements:
21
+ * - Label Studio and Things Factory must use subdomain pattern (e.g., label.example.com, app.example.com)
22
+ * - Label Studio .env must include:
23
+ * - CSRF_TRUSTED_ORIGINS: Include Things Factory frontend domain
24
+ * - ALLOWED_HOSTS: Include subdomain patterns
25
+ * - JWT_SSO_* settings for SSO authentication
26
+ * - Things Factory config must include:
27
+ * - labelStudio.serverUrl: Label Studio URL
28
+ * - labelStudio.cookieDomain: Shared cookie domain (e.g., .example.com)
29
+ *
30
+ * ## Usage:
31
+ * ```html
32
+ * <label-studio-wrapper></label-studio-wrapper>
33
+ * ```
34
+ *
35
+ * The component automatically:
36
+ * 1. Calls SSO setup endpoint to establish authentication
37
+ * 2. Builds iframe URL pointing directly to Label Studio
38
+ * 3. Adds ?hideHeader=true to hide Label Studio's header
39
+ * 4. Provides FAB buttons for reload and fullscreen
40
+ */
41
+ import { css, html, LitElement } from 'lit';
42
+ import { customElement, state, query } from 'lit/decorators.js';
43
+ let LabelStudioWrapper = class LabelStudioWrapper extends LitElement {
44
+ constructor() {
45
+ super(...arguments);
46
+ this.loading = true;
47
+ this.error = null;
48
+ this.iframeUrl = '';
49
+ this.path = '';
50
+ this.iframeLoaded = false;
51
+ this.labelStudioUrl = '';
52
+ }
53
+ static { this.styles = css `
54
+ :host {
55
+ display: flex;
56
+ flex-direction: column;
57
+ height: 100%;
58
+ overflow: hidden;
59
+ }
60
+
61
+ .container {
62
+ flex: 1;
63
+ display: flex;
64
+ flex-direction: column;
65
+ overflow: hidden;
66
+ position: relative;
67
+ }
68
+
69
+ .iframe-container {
70
+ flex: 1;
71
+ position: relative;
72
+ overflow: hidden;
73
+ }
74
+
75
+ iframe {
76
+ width: 100%;
77
+ height: 100%;
78
+ border: none;
79
+ }
80
+
81
+ .fab-container {
82
+ position: absolute;
83
+ bottom: 24px;
84
+ left: 24px;
85
+ display: flex;
86
+ flex-direction: column;
87
+ gap: 12px;
88
+ z-index: 1000;
89
+ }
90
+
91
+ .fab-button {
92
+ width: 56px;
93
+ height: 56px;
94
+ border-radius: 16px;
95
+ background-color: var(--md-sys-color-primary-container);
96
+ color: var(--md-sys-color-on-primary-container);
97
+ border: none;
98
+ cursor: pointer;
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
103
+ transition: all 0.2s ease;
104
+ }
105
+
106
+ .fab-button:hover {
107
+ background-color: var(--md-sys-color-primary);
108
+ color: var(--md-sys-color-on-primary);
109
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
110
+ transform: translateY(-2px);
111
+ }
112
+
113
+ .fab-button md-icon {
114
+ font-size: 24px;
115
+ }
116
+
117
+ .loading {
118
+ display: flex;
119
+ justify-content: center;
120
+ align-items: center;
121
+ height: 100%;
122
+ font-size: 18px;
123
+ color: var(--md-sys-color-on-surface-variant);
124
+ }
125
+
126
+ .error {
127
+ display: flex;
128
+ justify-content: center;
129
+ align-items: center;
130
+ height: 100%;
131
+ color: var(--md-sys-color-error);
132
+ flex-direction: column;
133
+ gap: 10px;
134
+ }
135
+
136
+ md-filled-button {
137
+ --md-filled-button-container-color: var(--md-sys-color-primary);
138
+ }
139
+ `; }
140
+ async updated(changes) {
141
+ if (changes.has('path') && this.path) {
142
+ await this.buildIframeUrl();
143
+ setTimeout(() => {
144
+ if (!this.iframeLoaded && !this.error && !this.loading) {
145
+ this.error = 'Failed to load Label Studio. The server may be unavailable.';
146
+ }
147
+ }, 15000);
148
+ }
149
+ }
150
+ async buildIframeUrl() {
151
+ try {
152
+ this.loading = true;
153
+ this.error = null;
154
+ // Step 1: Get Label Studio configuration
155
+ console.log('[Label Studio Wrapper] Fetching Label Studio configuration...');
156
+ const configResponse = await fetch('/label-studio/sso/config', {
157
+ method: 'GET',
158
+ credentials: 'include'
159
+ });
160
+ if (!configResponse.ok) {
161
+ throw new Error(`Failed to fetch Label Studio config: ${configResponse.status}`);
162
+ }
163
+ const configData = await configResponse.json();
164
+ this.labelStudioUrl = configData.labelStudioUrl;
165
+ if (!this.labelStudioUrl || this.labelStudioUrl === 'not configured') {
166
+ throw new Error('Label Studio URL is not configured. Please check your settings.');
167
+ }
168
+ console.log('[Label Studio Wrapper] Label Studio URL:', this.labelStudioUrl);
169
+ console.log('[Label Studio Wrapper] Cookie domain:', configData.cookieDomain);
170
+ // Step 2: Setup SSO token before loading iframe
171
+ console.log('[Label Studio Wrapper] Setting up SSO token...');
172
+ const ssoResponse = await fetch('/label-studio/sso/setup', {
173
+ method: 'GET',
174
+ credentials: 'include' // Include cookies in request
175
+ });
176
+ if (!ssoResponse.ok) {
177
+ const errorData = await ssoResponse.json().catch(() => ({}));
178
+ throw new Error(errorData.message || `SSO setup failed: ${ssoResponse.status}`);
179
+ }
180
+ const ssoData = await ssoResponse.json();
181
+ console.log('[Label Studio Wrapper] SSO setup complete:', ssoData.message);
182
+ // Step 3: Build iframe URL pointing directly to Label Studio
183
+ // No proxy needed - cookie is shared via domain
184
+ let url = this.labelStudioUrl + this.path;
185
+ // Build query parameters
186
+ const params = new URLSearchParams();
187
+ // Hide header for seamless iframe embedding
188
+ params.set('hideHeader', 'true');
189
+ // Combine URL with parameters
190
+ const queryString = params.toString();
191
+ this.iframeUrl = queryString ? `${url}?${queryString}` : url;
192
+ console.log('[Label Studio Wrapper] iframe URL:', this.iframeUrl);
193
+ }
194
+ catch (err) {
195
+ console.error('[Label Studio Wrapper] Failed to build iframe URL:', err);
196
+ this.error = `Failed to initialize: ${err.message}`;
197
+ }
198
+ finally {
199
+ this.loading = false;
200
+ }
201
+ }
202
+ handleIframeLoad() {
203
+ console.log('Label Studio iframe loaded successfully');
204
+ this.iframeLoaded = true;
205
+ this.loading = false;
206
+ }
207
+ handleIframeError() {
208
+ console.error('Label Studio iframe failed to load');
209
+ this.error = 'Failed to load Label Studio iframe. Please check the server connection.';
210
+ this.loading = false;
211
+ }
212
+ handleReload() {
213
+ if (this.iframe) {
214
+ this.iframe.src = this.iframeUrl;
215
+ }
216
+ }
217
+ handleFullscreen() {
218
+ if (this.iframe) {
219
+ if (this.iframe.requestFullscreen) {
220
+ this.iframe.requestFullscreen();
221
+ }
222
+ }
223
+ }
224
+ render() {
225
+ if (this.loading) {
226
+ return html `
227
+ <div class="loading">
228
+ <md-circular-progress indeterminate></md-circular-progress>
229
+ <span>Loading Label Studio...</span>
230
+ </div>
231
+ `;
232
+ }
233
+ if (this.error) {
234
+ return html `
235
+ <div class="error">
236
+ <md-icon>error</md-icon>
237
+ <div>${this.error}</div>
238
+ <md-filled-button @click=${this.buildIframeUrl}>
239
+ <md-icon slot="icon">refresh</md-icon>
240
+ Retry
241
+ </md-filled-button>
242
+ </div>
243
+ `;
244
+ }
245
+ return html `
246
+ <div class="container">
247
+ <div class="iframe-container">
248
+ <iframe
249
+ src=${this.iframeUrl}
250
+ @load=${this.handleIframeLoad}
251
+ @error=${this.handleIframeError}
252
+ allow="fullscreen"
253
+ allow-same-origin
254
+ allow-scripts
255
+ allow-forms
256
+ allow-popups
257
+ allow-top-navigation
258
+ ></iframe>
259
+ </div>
260
+ <div class="fab-container">
261
+ <button class="fab-button" @click=${this.handleReload} title="Reload">
262
+ <md-icon>refresh</md-icon>
263
+ </button>
264
+ <button class="fab-button" @click=${this.handleFullscreen} title="Fullscreen">
265
+ <md-icon>fullscreen</md-icon>
266
+ </button>
267
+ </div>
268
+ </div>
269
+ `;
270
+ }
271
+ };
272
+ __decorate([
273
+ state(),
274
+ __metadata("design:type", Boolean)
275
+ ], LabelStudioWrapper.prototype, "loading", void 0);
276
+ __decorate([
277
+ state(),
278
+ __metadata("design:type", Object)
279
+ ], LabelStudioWrapper.prototype, "error", void 0);
280
+ __decorate([
281
+ state(),
282
+ __metadata("design:type", String)
283
+ ], LabelStudioWrapper.prototype, "iframeUrl", void 0);
284
+ __decorate([
285
+ state(),
286
+ __metadata("design:type", String)
287
+ ], LabelStudioWrapper.prototype, "path", void 0);
288
+ __decorate([
289
+ state(),
290
+ __metadata("design:type", Boolean)
291
+ ], LabelStudioWrapper.prototype, "iframeLoaded", void 0);
292
+ __decorate([
293
+ state(),
294
+ __metadata("design:type", String)
295
+ ], LabelStudioWrapper.prototype, "labelStudioUrl", void 0);
296
+ __decorate([
297
+ query('iframe'),
298
+ __metadata("design:type", HTMLIFrameElement)
299
+ ], LabelStudioWrapper.prototype, "iframe", void 0);
300
+ LabelStudioWrapper = __decorate([
301
+ customElement('label-studio-wrapper')
302
+ ], LabelStudioWrapper);
303
+ export { LabelStudioWrapper };
304
+ //# sourceMappingURL=label-studio-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-studio-wrapper.js","sourceRoot":"","sources":["../client/label-studio-wrapper.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Label Studio Wrapper Component for Things Factory\n *\n * This component embeds Label Studio within Things Factory using an iframe.\n * It uses subdomain-based cookie sharing for SSO authentication, eliminating\n * the need for proxying.\n *\n * ## Key Features:\n * - Direct Label Studio access (no proxy needed)\n * - Automatic SSO cookie setup via shared domain\n * - Hidden header for seamless integration\n * - Floating Action Buttons (FAB) for Reload and Fullscreen\n *\n * ## Architecture:\n * 1. Client calls /label-studio/sso/setup\n * 2. Backend sets cookie with shared domain (e.g., .nubison.localhost)\n * 3. Browser → Label Studio (direct, with cookie)\n *\n * ## Configuration Requirements:\n * - Label Studio and Things Factory must use subdomain pattern (e.g., label.example.com, app.example.com)\n * - Label Studio .env must include:\n * - CSRF_TRUSTED_ORIGINS: Include Things Factory frontend domain\n * - ALLOWED_HOSTS: Include subdomain patterns\n * - JWT_SSO_* settings for SSO authentication\n * - Things Factory config must include:\n * - labelStudio.serverUrl: Label Studio URL\n * - labelStudio.cookieDomain: Shared cookie domain (e.g., .example.com)\n *\n * ## Usage:\n * ```html\n * <label-studio-wrapper></label-studio-wrapper>\n * ```\n *\n * The component automatically:\n * 1. Calls SSO setup endpoint to establish authentication\n * 2. Builds iframe URL pointing directly to Label Studio\n * 3. Adds ?hideHeader=true to hide Label Studio's header\n * 4. Provides FAB buttons for reload and fullscreen\n */\nimport { css, html, LitElement } from 'lit'\nimport { customElement, state, query } from 'lit/decorators.js'\n\n@customElement('label-studio-wrapper')\nexport class LabelStudioWrapper extends LitElement {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .container {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n position: relative;\n }\n\n .iframe-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n }\n\n iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n .fab-container {\n position: absolute;\n bottom: 24px;\n left: 24px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n z-index: 1000;\n }\n\n .fab-button {\n width: 56px;\n height: 56px;\n border-radius: 16px;\n background-color: var(--md-sys-color-primary-container);\n color: var(--md-sys-color-on-primary-container);\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n transition: all 0.2s ease;\n }\n\n .fab-button:hover {\n background-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);\n transform: translateY(-2px);\n }\n\n .fab-button md-icon {\n font-size: 24px;\n }\n\n .loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n font-size: 18px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .error {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n color: var(--md-sys-color-error);\n flex-direction: column;\n gap: 10px;\n }\n\n md-filled-button {\n --md-filled-button-container-color: var(--md-sys-color-primary);\n }\n `\n\n @state() private loading: boolean = true\n @state() private error: string | null = null\n @state() private iframeUrl: string = ''\n @state() private path: string = ''\n @state() private iframeLoaded: boolean = false\n @state() private labelStudioUrl: string = ''\n @query('iframe') private iframe!: HTMLIFrameElement\n\n async updated(changes: any) {\n if (changes.has('path') && this.path) {\n await this.buildIframeUrl()\n\n setTimeout(() => {\n if (!this.iframeLoaded && !this.error && !this.loading) {\n this.error = 'Failed to load Label Studio. The server may be unavailable.'\n }\n }, 15000)\n }\n }\n\n async buildIframeUrl() {\n try {\n this.loading = true\n this.error = null\n\n // Step 1: Get Label Studio configuration\n console.log('[Label Studio Wrapper] Fetching Label Studio configuration...')\n\n const configResponse = await fetch('/label-studio/sso/config', {\n method: 'GET',\n credentials: 'include'\n })\n\n if (!configResponse.ok) {\n throw new Error(`Failed to fetch Label Studio config: ${configResponse.status}`)\n }\n\n const configData = await configResponse.json()\n this.labelStudioUrl = configData.labelStudioUrl\n\n if (!this.labelStudioUrl || this.labelStudioUrl === 'not configured') {\n throw new Error('Label Studio URL is not configured. Please check your settings.')\n }\n\n console.log('[Label Studio Wrapper] Label Studio URL:', this.labelStudioUrl)\n console.log('[Label Studio Wrapper] Cookie domain:', configData.cookieDomain)\n\n // Step 2: Setup SSO token before loading iframe\n console.log('[Label Studio Wrapper] Setting up SSO token...')\n\n const ssoResponse = await fetch('/label-studio/sso/setup', {\n method: 'GET',\n credentials: 'include' // Include cookies in request\n })\n\n if (!ssoResponse.ok) {\n const errorData = await ssoResponse.json().catch(() => ({}))\n throw new Error(errorData.message || `SSO setup failed: ${ssoResponse.status}`)\n }\n\n const ssoData = await ssoResponse.json()\n console.log('[Label Studio Wrapper] SSO setup complete:', ssoData.message)\n\n // Step 3: Build iframe URL pointing directly to Label Studio\n // No proxy needed - cookie is shared via domain\n let url = this.labelStudioUrl + this.path\n\n // Build query parameters\n const params = new URLSearchParams()\n\n // Hide header for seamless iframe embedding\n params.set('hideHeader', 'true')\n\n // Combine URL with parameters\n const queryString = params.toString()\n this.iframeUrl = queryString ? `${url}?${queryString}` : url\n\n console.log('[Label Studio Wrapper] iframe URL:', this.iframeUrl)\n } catch (err: any) {\n console.error('[Label Studio Wrapper] Failed to build iframe URL:', err)\n this.error = `Failed to initialize: ${err.message}`\n } finally {\n this.loading = false\n }\n }\n\n handleIframeLoad() {\n console.log('Label Studio iframe loaded successfully')\n this.iframeLoaded = true\n this.loading = false\n }\n\n handleIframeError() {\n console.error('Label Studio iframe failed to load')\n this.error = 'Failed to load Label Studio iframe. Please check the server connection.'\n this.loading = false\n }\n\n handleReload() {\n if (this.iframe) {\n this.iframe.src = this.iframeUrl\n }\n }\n\n handleFullscreen() {\n if (this.iframe) {\n if (this.iframe.requestFullscreen) {\n this.iframe.requestFullscreen()\n }\n }\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"loading\">\n <md-circular-progress indeterminate></md-circular-progress>\n <span>Loading Label Studio...</span>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"error\">\n <md-icon>error</md-icon>\n <div>${this.error}</div>\n <md-filled-button @click=${this.buildIframeUrl}>\n <md-icon slot=\"icon\">refresh</md-icon>\n Retry\n </md-filled-button>\n </div>\n `\n }\n\n return html`\n <div class=\"container\">\n <div class=\"iframe-container\">\n <iframe\n src=${this.iframeUrl}\n @load=${this.handleIframeLoad}\n @error=${this.handleIframeError}\n allow=\"fullscreen\"\n allow-same-origin\n allow-scripts\n allow-forms\n allow-popups\n allow-top-navigation\n ></iframe>\n </div>\n <div class=\"fab-container\">\n <button class=\"fab-button\" @click=${this.handleReload} title=\"Reload\">\n <md-icon>refresh</md-icon>\n </button>\n <button class=\"fab-button\" @click=${this.handleFullscreen} title=\"Fullscreen\">\n <md-icon>fullscreen</md-icon>\n </button>\n </div>\n </div>\n `\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export default function route(page: string): "label-studio-project-create" | "label-studio-project-list" | "label-studio-label" | undefined;
@@ -0,0 +1,14 @@
1
+ export default function route(page) {
2
+ switch (page) {
3
+ case 'label-studio-project-list':
4
+ import('./label-studio-project-list.js');
5
+ return page;
6
+ case 'label-studio-project-create':
7
+ import('./label-studio-project-create.js');
8
+ return page;
9
+ case 'label-studio-label':
10
+ import('./label-studio-label-page.js');
11
+ return page;
12
+ }
13
+ }
14
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,2BAA2B;YAC9B,MAAM,CAAC,gCAAgC,CAAC,CAAA;YACxC,OAAO,IAAI,CAAA;QAEb,KAAK,6BAA6B;YAChC,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,oBAAoB;YACvB,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page: string) {\n switch (page) {\n case 'label-studio-project-list':\n import('./label-studio-project-list.js')\n return page\n\n case 'label-studio-project-create':\n import('./label-studio-project-create.js')\n return page\n\n case 'label-studio-label':\n import('./label-studio-label-page.js')\n return page\n }\n}\n"]}