@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.
- package/CHANGELOG.md +85 -0
- package/EXTERNAL_DATA_SOURCING.md +484 -0
- package/IMPLEMENTATION_GUIDE.md +469 -0
- package/INTEGRATION.md +279 -0
- package/README.md +1014 -0
- package/SETUP_GUIDE.md +577 -0
- package/TEST_GUIDE.md +387 -0
- package/UI_CUSTOMIZATION.md +395 -0
- package/USER_SYNC_GUIDE.md +514 -0
- package/client/bootstrap.ts +1 -0
- package/client/index.ts +1 -0
- package/client/label-studio-label-page.ts +52 -0
- package/client/label-studio-project-create.ts +216 -0
- package/client/label-studio-project-list.ts +214 -0
- package/client/label-studio-wrapper.ts +294 -0
- package/client/route.ts +15 -0
- package/client/tsconfig.json +13 -0
- package/config/config.development.js +124 -0
- package/config/config.production.js +182 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +2 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/label-studio-label-page.d.ts +8 -0
- package/dist-client/label-studio-label-page.js +54 -0
- package/dist-client/label-studio-label-page.js.map +1 -0
- package/dist-client/label-studio-project-create.d.ts +16 -0
- package/dist-client/label-studio-project-create.js +235 -0
- package/dist-client/label-studio-project-create.js.map +1 -0
- package/dist-client/label-studio-project-list.d.ts +16 -0
- package/dist-client/label-studio-project-list.js +222 -0
- package/dist-client/label-studio-project-list.js.map +1 -0
- package/dist-client/label-studio-wrapper.d.ts +57 -0
- package/dist-client/label-studio-wrapper.js +304 -0
- package/dist-client/label-studio-wrapper.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +14 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controller/label-studio-role-mapper.d.ts +35 -0
- package/dist-server/controller/label-studio-role-mapper.js +65 -0
- package/dist-server/controller/label-studio-role-mapper.js.map +1 -0
- package/dist-server/controller/user-provisioning-service.d.ts +66 -0
- package/dist-server/controller/user-provisioning-service.js +264 -0
- package/dist-server/controller/user-provisioning-service.js.map +1 -0
- package/dist-server/index.d.ts +7 -0
- package/dist-server/index.js +19 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/route/label-studio-sso.d.ts +2 -0
- package/dist-server/route/label-studio-sso.js +156 -0
- package/dist-server/route/label-studio-sso.js.map +1 -0
- package/dist-server/route/webhook.d.ts +65 -0
- package/dist-server/route/webhook.js +248 -0
- package/dist-server/route/webhook.js.map +1 -0
- package/dist-server/route.d.ts +1 -0
- package/dist-server/route.js +21 -0
- package/dist-server/route.js.map +1 -0
- package/dist-server/service/ai-prediction-service.d.ts +27 -0
- package/dist-server/service/ai-prediction-service.js +222 -0
- package/dist-server/service/ai-prediction-service.js.map +1 -0
- package/dist-server/service/dataset-labeling-integration.d.ts +44 -0
- package/dist-server/service/dataset-labeling-integration.js +512 -0
- package/dist-server/service/dataset-labeling-integration.js.map +1 -0
- package/dist-server/service/external-data-source-service.d.ts +78 -0
- package/dist-server/service/external-data-source-service.js +415 -0
- package/dist-server/service/external-data-source-service.js.map +1 -0
- package/dist-server/service/index.d.ts +12 -0
- package/dist-server/service/index.js +27 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/label-studio-sso-service.d.ts +38 -0
- package/dist-server/service/label-studio-sso-service.js +98 -0
- package/dist-server/service/label-studio-sso-service.js.map +1 -0
- package/dist-server/service/ml/ml-backend-service.d.ts +23 -0
- package/dist-server/service/ml/ml-backend-service.js +153 -0
- package/dist-server/service/ml/ml-backend-service.js.map +1 -0
- package/dist-server/service/prediction/prediction-management.d.ts +32 -0
- package/dist-server/service/prediction/prediction-management.js +299 -0
- package/dist-server/service/prediction/prediction-management.js.map +1 -0
- package/dist-server/service/project/project-management.d.ts +36 -0
- package/dist-server/service/project/project-management.js +309 -0
- package/dist-server/service/project/project-management.js.map +1 -0
- package/dist-server/service/task/task-management.d.ts +42 -0
- package/dist-server/service/task/task-management.js +372 -0
- package/dist-server/service/task/task-management.js.map +1 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.d.ts +28 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.js +111 -0
- package/dist-server/service/user-provisioning/user-sync-mutation.js.map +1 -0
- package/dist-server/service/webhook/webhook-management.d.ts +21 -0
- package/dist-server/service/webhook/webhook-management.js +134 -0
- package/dist-server/service/webhook/webhook-management.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/dist-server/types/dataset-labeling-types.d.ts +71 -0
- package/dist-server/types/dataset-labeling-types.js +259 -0
- package/dist-server/types/dataset-labeling-types.js.map +1 -0
- package/dist-server/types/label-studio-types.d.ts +128 -0
- package/dist-server/types/label-studio-types.js +494 -0
- package/dist-server/types/label-studio-types.js.map +1 -0
- package/dist-server/types/prediction-types.d.ts +39 -0
- package/dist-server/types/prediction-types.js +121 -0
- package/dist-server/types/prediction-types.js.map +1 -0
- package/dist-server/utils/annotation-exporter.d.ts +104 -0
- package/dist-server/utils/annotation-exporter.js +261 -0
- package/dist-server/utils/annotation-exporter.js.map +1 -0
- package/dist-server/utils/label-config-builder.d.ts +117 -0
- package/dist-server/utils/label-config-builder.js +286 -0
- package/dist-server/utils/label-config-builder.js.map +1 -0
- package/dist-server/utils/label-studio-api-client.d.ts +180 -0
- package/dist-server/utils/label-studio-api-client.js +401 -0
- package/dist-server/utils/label-studio-api-client.js.map +1 -0
- package/dist-server/utils/media-url-extractor.d.ts +45 -0
- package/dist-server/utils/media-url-extractor.js +152 -0
- package/dist-server/utils/media-url-extractor.js.map +1 -0
- package/dist-server/utils/task-transformer.d.ts +108 -0
- package/dist-server/utils/task-transformer.js +260 -0
- package/dist-server/utils/task-transformer.js.map +1 -0
- package/package.json +47 -0
- package/server/SERVER_STRUCTURE.md +351 -0
- package/server/controller/label-studio-role-mapper.ts +76 -0
- package/server/controller/user-provisioning-service.ts +340 -0
- package/server/index.ts +19 -0
- package/server/route/label-studio-sso.ts +194 -0
- package/server/route/webhook.ts +304 -0
- package/server/route.ts +35 -0
- package/server/service/ai-prediction-service.ts +239 -0
- package/server/service/dataset-labeling-integration.ts +590 -0
- package/server/service/external-data-source-service.ts +438 -0
- package/server/service/index.ts +24 -0
- package/server/service/label-studio-sso-service.ts +108 -0
- package/server/service/labeling-scenario-service.ts.deprecated +566 -0
- package/server/service/ml/ml-backend-service.ts +127 -0
- package/server/service/prediction/prediction-management.ts +281 -0
- package/server/service/project/project-management.ts +284 -0
- package/server/service/task/task-management.ts +363 -0
- package/server/service/user-provisioning/user-sync-mutation.ts +80 -0
- package/server/service/webhook/webhook-management.ts +109 -0
- package/server/tsconfig.json +11 -0
- package/server/types/dataset-labeling-types.ts +181 -0
- package/server/types/global.d.ts +23 -0
- package/server/types/label-studio-types.ts +346 -0
- package/server/types/prediction-types.ts +86 -0
- package/server/types/scenario-types.ts.deprecated +362 -0
- package/server/utils/annotation-exporter.ts +340 -0
- package/server/utils/label-config-builder.ts +340 -0
- package/server/utils/label-studio-api-client.ts +487 -0
- package/server/utils/media-url-extractor.ts +193 -0
- package/server/utils/task-transformer.ts +342 -0
- package/test-ai-prediction.js +268 -0
- package/test-dataset-integration.js +449 -0
- package/test-simple.js +89 -0
- package/things-factory.config.js +12 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import './label-studio-wrapper.js';
|
|
3
|
+
import { css, html } from 'lit';
|
|
4
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
5
|
+
import { PageView } from '@things-factory/shell/client';
|
|
6
|
+
let LabelStudioLabelPage = class LabelStudioLabelPage extends PageView {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.projectId = '';
|
|
10
|
+
}
|
|
11
|
+
static { this.styles = css `
|
|
12
|
+
:host {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
height: 100%;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.label-studio-wrapper {
|
|
20
|
+
flex: 1;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.error {
|
|
27
|
+
padding: 20px;
|
|
28
|
+
color: var(--md-sys-color-error);
|
|
29
|
+
text-align: center;
|
|
30
|
+
}
|
|
31
|
+
`; }
|
|
32
|
+
async pageUpdated(changes, lifecycle, changedBefore) {
|
|
33
|
+
if (this.active && lifecycle.resourceId) {
|
|
34
|
+
this.projectId = lifecycle.resourceId;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
render() {
|
|
38
|
+
const projectId = this.projectId;
|
|
39
|
+
if (!projectId) {
|
|
40
|
+
return html `<div class="error">Error: No project ID provided</div>`;
|
|
41
|
+
}
|
|
42
|
+
const path = `/projects/${projectId}`;
|
|
43
|
+
return html ` <label-studio-wrapper .path=${path}></label-studio-wrapper> `;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
__decorate([
|
|
47
|
+
property({ type: String }),
|
|
48
|
+
__metadata("design:type", String)
|
|
49
|
+
], LabelStudioLabelPage.prototype, "projectId", void 0);
|
|
50
|
+
LabelStudioLabelPage = __decorate([
|
|
51
|
+
customElement('label-studio-label-page')
|
|
52
|
+
], LabelStudioLabelPage);
|
|
53
|
+
export { LabelStudioLabelPage };
|
|
54
|
+
//# sourceMappingURL=label-studio-label-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label-studio-label-page.js","sourceRoot":"","sources":["../client/label-studio-label-page.ts"],"names":[],"mappings":";AAAA,OAAO,2BAA2B,CAAA;AAElC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAc,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAS,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAGlE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAGhD,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,QAAQ;IAA3C;;QAuBuB,cAAS,GAAW,EAAE,CAAA;IAmBpD,CAAC;aAzCQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;GAoBlB,AApBY,CAoBZ;IAID,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,aAAa;QACjD,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;YACxC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,UAAU,CAAA;QACvC,CAAC;IACH,CAAC;IAED,MAAM;QACJ,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;QAEhC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,IAAI,CAAA,wDAAwD,CAAA;QACrE,CAAC;QAED,MAAM,IAAI,GAAG,aAAa,SAAS,EAAE,CAAA;QAErC,OAAO,IAAI,CAAA,gCAAgC,IAAI,2BAA2B,CAAA;IAC5E,CAAC;;AAlB2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;uDAAuB;AAvBvC,oBAAoB;IADhC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,oBAAoB,CA0ChC","sourcesContent":["import './label-studio-wrapper.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { gql } from 'graphql-tag'\nimport { client } from '@operato/graphql'\nimport { PageView } from '@things-factory/shell/client'\n\n@customElement('label-studio-label-page')\nexport class LabelStudioLabelPage extends PageView {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .label-studio-wrapper {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .error {\n padding: 20px;\n color: var(--md-sys-color-error);\n text-align: center;\n }\n `\n\n @property({ type: String }) projectId: string = ''\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (this.active && lifecycle.resourceId) {\n this.projectId = lifecycle.resourceId\n }\n }\n\n render() {\n const projectId = this.projectId\n\n if (!projectId) {\n return html`<div class=\"error\">Error: No project ID provided</div>`\n }\n\n const path = `/projects/${projectId}`\n\n return html` <label-studio-wrapper .path=${path}></label-studio-wrapper> `\n }\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import '@material/web/button/filled-button.js';
|
|
2
|
+
import '@material/web/button/text-button.js';
|
|
3
|
+
import '@material/web/textfield/filled-text-field.js';
|
|
4
|
+
import { PageView } from '@things-factory/shell/client';
|
|
5
|
+
export declare class LabelStudioProjectCreate extends PageView {
|
|
6
|
+
static styles: import("lit").CSSResult[];
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
labelConfig: string;
|
|
10
|
+
saving: boolean;
|
|
11
|
+
error: string;
|
|
12
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
13
|
+
useTemplate(type: string): void;
|
|
14
|
+
save(): Promise<void>;
|
|
15
|
+
cancel(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/button/filled-button.js';
|
|
3
|
+
import '@material/web/button/text-button.js';
|
|
4
|
+
import '@material/web/textfield/filled-text-field.js';
|
|
5
|
+
import { css, html } from 'lit';
|
|
6
|
+
import { customElement, state } from 'lit/decorators.js';
|
|
7
|
+
import { client, PageView, navigate } from '@things-factory/shell/client';
|
|
8
|
+
import gql from 'graphql-tag';
|
|
9
|
+
let LabelStudioProjectCreate = class LabelStudioProjectCreate extends PageView {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.title = '';
|
|
13
|
+
this.description = '';
|
|
14
|
+
this.labelConfig = '';
|
|
15
|
+
this.saving = false;
|
|
16
|
+
this.error = '';
|
|
17
|
+
}
|
|
18
|
+
static { this.styles = [
|
|
19
|
+
css `
|
|
20
|
+
:host {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
max-width: 800px;
|
|
25
|
+
margin: 0 auto;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
h2 {
|
|
29
|
+
margin: 0 0 20px 0;
|
|
30
|
+
color: var(--md-sys-color-on-surface);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.form-field {
|
|
34
|
+
margin-bottom: 20px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.form-field label {
|
|
38
|
+
display: block;
|
|
39
|
+
margin-bottom: 8px;
|
|
40
|
+
color: var(--md-sys-color-on-surface);
|
|
41
|
+
font-weight: 500;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
md-filled-text-field {
|
|
45
|
+
width: 100%;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
textarea {
|
|
49
|
+
width: 100%;
|
|
50
|
+
min-height: 200px;
|
|
51
|
+
padding: 12px;
|
|
52
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
53
|
+
border-radius: 4px;
|
|
54
|
+
font-family: monospace;
|
|
55
|
+
font-size: 14px;
|
|
56
|
+
resize: vertical;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.actions {
|
|
60
|
+
display: flex;
|
|
61
|
+
gap: 10px;
|
|
62
|
+
justify-content: flex-end;
|
|
63
|
+
margin-top: 20px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.template-examples {
|
|
67
|
+
margin-top: 20px;
|
|
68
|
+
padding: 15px;
|
|
69
|
+
background: var(--md-sys-color-surface-variant);
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.template-examples h4 {
|
|
74
|
+
margin: 0 0 10px 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.template-btn {
|
|
78
|
+
margin: 5px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.error {
|
|
82
|
+
color: var(--md-sys-color-error);
|
|
83
|
+
margin: 10px 0;
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
]; }
|
|
87
|
+
render() {
|
|
88
|
+
return html `
|
|
89
|
+
<h2>Create Label Studio Project</h2>
|
|
90
|
+
|
|
91
|
+
<div class="form-field">
|
|
92
|
+
<label>Project Title *</label>
|
|
93
|
+
<md-filled-text-field
|
|
94
|
+
.value=${this.title}
|
|
95
|
+
@input=${(e) => (this.title = e.target.value)}
|
|
96
|
+
placeholder="Enter project title"
|
|
97
|
+
></md-filled-text-field>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="form-field">
|
|
101
|
+
<label>Description</label>
|
|
102
|
+
<md-filled-text-field
|
|
103
|
+
.value=${this.description}
|
|
104
|
+
@input=${(e) => (this.description = e.target.value)}
|
|
105
|
+
placeholder="Enter project description"
|
|
106
|
+
></md-filled-text-field>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="form-field">
|
|
110
|
+
<label>Label Configuration (XML) *</label>
|
|
111
|
+
<textarea
|
|
112
|
+
.value=${this.labelConfig}
|
|
113
|
+
@input=${(e) => (this.labelConfig = e.target.value)}
|
|
114
|
+
placeholder='<View> <Text name="text" value="$text"/> <Choices name="label" toName="text"> <Choice value="Positive"/> <Choice value="Negative"/> </Choices> </View>'
|
|
115
|
+
></textarea>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div class="template-examples">
|
|
119
|
+
<h4>Quick Templates</h4>
|
|
120
|
+
<md-filled-button class="template-btn" @click=${() => this.useTemplate('text-classification')}>
|
|
121
|
+
Text Classification
|
|
122
|
+
</md-filled-button>
|
|
123
|
+
<md-filled-button class="template-btn" @click=${() => this.useTemplate('image-classification')}>
|
|
124
|
+
Image Classification
|
|
125
|
+
</md-filled-button>
|
|
126
|
+
<md-filled-button class="template-btn" @click=${() => this.useTemplate('ner')}>
|
|
127
|
+
Named Entity Recognition
|
|
128
|
+
</md-filled-button>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
${this.error ? html `<div class="error">${this.error}</div>` : ''}
|
|
132
|
+
|
|
133
|
+
<div class="actions">
|
|
134
|
+
<md-text-button @click=${this.cancel}>Cancel</md-text-button>
|
|
135
|
+
<md-filled-button @click=${this.save} ?disabled=${this.saving}>
|
|
136
|
+
${this.saving ? 'Creating...' : 'Create Project'}
|
|
137
|
+
</md-filled-button>
|
|
138
|
+
</div>
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
useTemplate(type) {
|
|
142
|
+
const templates = {
|
|
143
|
+
'text-classification': `<View>
|
|
144
|
+
<Text name="text" value="$text"/>
|
|
145
|
+
<Choices name="sentiment" toName="text" choice="single">
|
|
146
|
+
<Choice value="Positive"/>
|
|
147
|
+
<Choice value="Negative"/>
|
|
148
|
+
<Choice value="Neutral"/>
|
|
149
|
+
</Choices>
|
|
150
|
+
</View>`,
|
|
151
|
+
'image-classification': `<View>
|
|
152
|
+
<Image name="image" value="$image"/>
|
|
153
|
+
<Choices name="choice" toName="image" choice="single">
|
|
154
|
+
<Choice value="Cat"/>
|
|
155
|
+
<Choice value="Dog"/>
|
|
156
|
+
<Choice value="Bird"/>
|
|
157
|
+
</Choices>
|
|
158
|
+
</View>`,
|
|
159
|
+
ner: `<View>
|
|
160
|
+
<Text name="text" value="$text"/>
|
|
161
|
+
<Labels name="label" toName="text">
|
|
162
|
+
<Label value="Person" background="red"/>
|
|
163
|
+
<Label value="Organization" background="darkorange"/>
|
|
164
|
+
<Label value="Location" background="blue"/>
|
|
165
|
+
</Labels>
|
|
166
|
+
</View>`
|
|
167
|
+
};
|
|
168
|
+
this.labelConfig = templates[type] || '';
|
|
169
|
+
}
|
|
170
|
+
async save() {
|
|
171
|
+
if (!this.title || !this.labelConfig) {
|
|
172
|
+
this.error = 'Title and Label Configuration are required';
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
this.saving = true;
|
|
177
|
+
this.error = '';
|
|
178
|
+
const response = await client.mutate({
|
|
179
|
+
mutation: gql `
|
|
180
|
+
mutation CreateProject($input: CreateProjectInput!) {
|
|
181
|
+
createLabelStudioProject(input: $input) {
|
|
182
|
+
id
|
|
183
|
+
title
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
`,
|
|
187
|
+
variables: {
|
|
188
|
+
input: {
|
|
189
|
+
title: this.title,
|
|
190
|
+
description: this.description || undefined,
|
|
191
|
+
labelConfig: this.labelConfig
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
const project = response.data.createLabelStudioProject;
|
|
196
|
+
// Navigate to project list
|
|
197
|
+
navigate('label-studio-project-list');
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
console.error('Failed to create project:', error);
|
|
201
|
+
this.error = error.message || 'Failed to create project';
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
this.saving = false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
cancel() {
|
|
208
|
+
navigate('label-studio-project-list');
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
__decorate([
|
|
212
|
+
state(),
|
|
213
|
+
__metadata("design:type", String)
|
|
214
|
+
], LabelStudioProjectCreate.prototype, "title", void 0);
|
|
215
|
+
__decorate([
|
|
216
|
+
state(),
|
|
217
|
+
__metadata("design:type", String)
|
|
218
|
+
], LabelStudioProjectCreate.prototype, "description", void 0);
|
|
219
|
+
__decorate([
|
|
220
|
+
state(),
|
|
221
|
+
__metadata("design:type", String)
|
|
222
|
+
], LabelStudioProjectCreate.prototype, "labelConfig", void 0);
|
|
223
|
+
__decorate([
|
|
224
|
+
state(),
|
|
225
|
+
__metadata("design:type", Boolean)
|
|
226
|
+
], LabelStudioProjectCreate.prototype, "saving", void 0);
|
|
227
|
+
__decorate([
|
|
228
|
+
state(),
|
|
229
|
+
__metadata("design:type", String)
|
|
230
|
+
], LabelStudioProjectCreate.prototype, "error", void 0);
|
|
231
|
+
LabelStudioProjectCreate = __decorate([
|
|
232
|
+
customElement('label-studio-project-create')
|
|
233
|
+
], LabelStudioProjectCreate);
|
|
234
|
+
export { LabelStudioProjectCreate };
|
|
235
|
+
//# sourceMappingURL=label-studio-project-create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label-studio-project-create.js","sourceRoot":"","sources":["../client/label-studio-project-create.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/textfield/filled-text-field.js'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { client, PageView, navigate } from '@things-factory/shell/client'\nimport gql from 'graphql-tag'\n\n@customElement('label-studio-project-create')\nexport class LabelStudioProjectCreate extends PageView {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n padding: 20px;\n max-width: 800px;\n margin: 0 auto;\n }\n\n h2 {\n margin: 0 0 20px 0;\n color: var(--md-sys-color-on-surface);\n }\n\n .form-field {\n margin-bottom: 20px;\n }\n\n .form-field label {\n display: block;\n margin-bottom: 8px;\n color: var(--md-sys-color-on-surface);\n font-weight: 500;\n }\n\n md-filled-text-field {\n width: 100%;\n }\n\n textarea {\n width: 100%;\n min-height: 200px;\n padding: 12px;\n border: 1px solid var(--md-sys-color-outline);\n border-radius: 4px;\n font-family: monospace;\n font-size: 14px;\n resize: vertical;\n }\n\n .actions {\n display: flex;\n gap: 10px;\n justify-content: flex-end;\n margin-top: 20px;\n }\n\n .template-examples {\n margin-top: 20px;\n padding: 15px;\n background: var(--md-sys-color-surface-variant);\n border-radius: 8px;\n }\n\n .template-examples h4 {\n margin: 0 0 10px 0;\n }\n\n .template-btn {\n margin: 5px;\n }\n\n .error {\n color: var(--md-sys-color-error);\n margin: 10px 0;\n }\n `\n ]\n\n @state() title: string = ''\n @state() description: string = ''\n @state() labelConfig: string = ''\n @state() saving: boolean = false\n @state() error: string = ''\n\n render() {\n return html`\n <h2>Create Label Studio Project</h2>\n\n <div class=\"form-field\">\n <label>Project Title *</label>\n <md-filled-text-field\n .value=${this.title}\n @input=${(e: any) => (this.title = e.target.value)}\n placeholder=\"Enter project title\"\n ></md-filled-text-field>\n </div>\n\n <div class=\"form-field\">\n <label>Description</label>\n <md-filled-text-field\n .value=${this.description}\n @input=${(e: any) => (this.description = e.target.value)}\n placeholder=\"Enter project description\"\n ></md-filled-text-field>\n </div>\n\n <div class=\"form-field\">\n <label>Label Configuration (XML) *</label>\n <textarea\n .value=${this.labelConfig}\n @input=${(e: any) => (this.labelConfig = (e.target as HTMLTextAreaElement).value)}\n placeholder='<View> <Text name=\"text\" value=\"$text\"/> <Choices name=\"label\" toName=\"text\"> <Choice value=\"Positive\"/> <Choice value=\"Negative\"/> </Choices> </View>'\n ></textarea>\n </div>\n\n <div class=\"template-examples\">\n <h4>Quick Templates</h4>\n <md-filled-button class=\"template-btn\" @click=${() => this.useTemplate('text-classification')}>\n Text Classification\n </md-filled-button>\n <md-filled-button class=\"template-btn\" @click=${() => this.useTemplate('image-classification')}>\n Image Classification\n </md-filled-button>\n <md-filled-button class=\"template-btn\" @click=${() => this.useTemplate('ner')}>\n Named Entity Recognition\n </md-filled-button>\n </div>\n\n ${this.error ? html`<div class=\"error\">${this.error}</div>` : ''}\n\n <div class=\"actions\">\n <md-text-button @click=${this.cancel}>Cancel</md-text-button>\n <md-filled-button @click=${this.save} ?disabled=${this.saving}>\n ${this.saving ? 'Creating...' : 'Create Project'}\n </md-filled-button>\n </div>\n `\n }\n\n useTemplate(type: string) {\n const templates = {\n 'text-classification': `<View>\n <Text name=\"text\" value=\"$text\"/>\n <Choices name=\"sentiment\" toName=\"text\" choice=\"single\">\n <Choice value=\"Positive\"/>\n <Choice value=\"Negative\"/>\n <Choice value=\"Neutral\"/>\n </Choices>\n</View>`,\n 'image-classification': `<View>\n <Image name=\"image\" value=\"$image\"/>\n <Choices name=\"choice\" toName=\"image\" choice=\"single\">\n <Choice value=\"Cat\"/>\n <Choice value=\"Dog\"/>\n <Choice value=\"Bird\"/>\n </Choices>\n</View>`,\n ner: `<View>\n <Text name=\"text\" value=\"$text\"/>\n <Labels name=\"label\" toName=\"text\">\n <Label value=\"Person\" background=\"red\"/>\n <Label value=\"Organization\" background=\"darkorange\"/>\n <Label value=\"Location\" background=\"blue\"/>\n </Labels>\n</View>`\n }\n\n this.labelConfig = templates[type] || ''\n }\n\n async save() {\n if (!this.title || !this.labelConfig) {\n this.error = 'Title and Label Configuration are required'\n return\n }\n\n try {\n this.saving = true\n this.error = ''\n\n const response = await client.mutate({\n mutation: gql`\n mutation CreateProject($input: CreateProjectInput!) {\n createLabelStudioProject(input: $input) {\n id\n title\n }\n }\n `,\n variables: {\n input: {\n title: this.title,\n description: this.description || undefined,\n labelConfig: this.labelConfig\n }\n }\n })\n\n const project = response.data.createLabelStudioProject\n\n // Navigate to project list\n navigate('label-studio-project-list')\n } catch (error: any) {\n console.error('Failed to create project:', error)\n this.error = error.message || 'Failed to create project'\n } finally {\n this.saving = false\n }\n }\n\n cancel() {\n navigate('label-studio-project-list')\n }\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import '@material/web/button/filled-button.js';
|
|
2
|
+
import '@material/web/button/outlined-button.js';
|
|
3
|
+
import '@material/web/icon/icon.js';
|
|
4
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
5
|
+
import { PageView } from '@things-factory/shell/client';
|
|
6
|
+
export declare class LabelStudioProjectList extends PageView {
|
|
7
|
+
static styles: import("lit").CSSResult[];
|
|
8
|
+
projects: any[];
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: string;
|
|
11
|
+
pageUpdated(changes: any, lifecycle: any, changedBefore: any): Promise<void>;
|
|
12
|
+
loadProjects(): Promise<void>;
|
|
13
|
+
createProject(): void;
|
|
14
|
+
openProject(projectId: number): void;
|
|
15
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/button/filled-button.js';
|
|
3
|
+
import '@material/web/button/outlined-button.js';
|
|
4
|
+
import '@material/web/icon/icon.js';
|
|
5
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
6
|
+
import { css, html } from 'lit';
|
|
7
|
+
import { customElement, state } from 'lit/decorators.js';
|
|
8
|
+
import { client, PageView, navigate } from '@things-factory/shell/client';
|
|
9
|
+
import gql from 'graphql-tag';
|
|
10
|
+
let LabelStudioProjectList = class LabelStudioProjectList extends PageView {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.projects = [];
|
|
14
|
+
this.loading = true;
|
|
15
|
+
this.error = '';
|
|
16
|
+
}
|
|
17
|
+
static { this.styles = [
|
|
18
|
+
css `
|
|
19
|
+
:host {
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
height: 100%;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
background-color: var(--md-sys-color-surface);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.header {
|
|
28
|
+
display: flex;
|
|
29
|
+
justify-content: space-between;
|
|
30
|
+
align-items: center;
|
|
31
|
+
margin-bottom: 20px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.header h2 {
|
|
35
|
+
margin: 0;
|
|
36
|
+
color: var(--md-sys-color-on-surface);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.project-grid {
|
|
40
|
+
display: grid;
|
|
41
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
42
|
+
gap: 20px;
|
|
43
|
+
overflow-y: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.project-card {
|
|
47
|
+
background: var(--md-sys-color-surface-container);
|
|
48
|
+
border-radius: 12px;
|
|
49
|
+
padding: 20px;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
transition: all 0.3s ease;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.project-card:hover {
|
|
55
|
+
background: var(--md-sys-color-surface-container-high);
|
|
56
|
+
transform: translateY(-2px);
|
|
57
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.project-card h3 {
|
|
61
|
+
margin: 0 0 10px 0;
|
|
62
|
+
color: var(--md-sys-color-on-surface);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.project-card p {
|
|
66
|
+
margin: 5px 0;
|
|
67
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
68
|
+
font-size: 14px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.project-stats {
|
|
72
|
+
display: flex;
|
|
73
|
+
gap: 15px;
|
|
74
|
+
margin-top: 15px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.stat-item {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.stat-label {
|
|
83
|
+
font-size: 12px;
|
|
84
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.stat-value {
|
|
88
|
+
font-size: 20px;
|
|
89
|
+
font-weight: bold;
|
|
90
|
+
color: var(--md-sys-color-primary);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.progress-bar {
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 8px;
|
|
96
|
+
background: var(--md-sys-color-surface-variant);
|
|
97
|
+
border-radius: 4px;
|
|
98
|
+
margin-top: 10px;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.progress-fill {
|
|
103
|
+
height: 100%;
|
|
104
|
+
background: var(--md-sys-color-primary);
|
|
105
|
+
transition: width 0.3s ease;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.loading {
|
|
109
|
+
display: flex;
|
|
110
|
+
justify-content: center;
|
|
111
|
+
align-items: center;
|
|
112
|
+
height: 100%;
|
|
113
|
+
font-size: 18px;
|
|
114
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.error {
|
|
118
|
+
color: var(--md-sys-color-error);
|
|
119
|
+
padding: 20px;
|
|
120
|
+
text-align: center;
|
|
121
|
+
}
|
|
122
|
+
`
|
|
123
|
+
]; }
|
|
124
|
+
async pageUpdated(changes, lifecycle, changedBefore) {
|
|
125
|
+
if (this.active) {
|
|
126
|
+
this.loadProjects();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async loadProjects() {
|
|
130
|
+
try {
|
|
131
|
+
this.loading = true;
|
|
132
|
+
const response = await client.query({
|
|
133
|
+
query: gql `
|
|
134
|
+
query {
|
|
135
|
+
labelStudioProjects {
|
|
136
|
+
id
|
|
137
|
+
title
|
|
138
|
+
description
|
|
139
|
+
taskCount
|
|
140
|
+
completedTaskCount
|
|
141
|
+
completionRate
|
|
142
|
+
createdAt
|
|
143
|
+
updatedAt
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
`
|
|
147
|
+
});
|
|
148
|
+
this.projects = response.data.labelStudioProjects;
|
|
149
|
+
this.loading = false;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('Failed to load projects:', error);
|
|
153
|
+
this.error = error.message || 'Failed to load projects';
|
|
154
|
+
this.loading = false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
createProject() {
|
|
158
|
+
navigate('label-studio-project-create');
|
|
159
|
+
}
|
|
160
|
+
openProject(projectId) {
|
|
161
|
+
// Open Label Studio viewer with project
|
|
162
|
+
navigate(`label-studio-label/${projectId}`);
|
|
163
|
+
}
|
|
164
|
+
render() {
|
|
165
|
+
if (this.loading) {
|
|
166
|
+
return html `<div class="loading">Loading projects...</div>`;
|
|
167
|
+
}
|
|
168
|
+
if (this.error) {
|
|
169
|
+
return html `<div class="error">${this.error}</div>`;
|
|
170
|
+
}
|
|
171
|
+
return html `
|
|
172
|
+
<div class="header">
|
|
173
|
+
<h2>Label Studio Projects</h2>
|
|
174
|
+
<md-filled-button @click=${this.createProject}>
|
|
175
|
+
<md-icon slot="icon">add</md-icon>
|
|
176
|
+
New Project
|
|
177
|
+
</md-filled-button>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class="project-grid">
|
|
181
|
+
${this.projects.map(project => html `
|
|
182
|
+
<div class="project-card" @click=${() => this.openProject(project.id)}>
|
|
183
|
+
<h3>${project.title}</h3>
|
|
184
|
+
<p>${project.description || 'No description'}</p>
|
|
185
|
+
|
|
186
|
+
<div class="project-stats">
|
|
187
|
+
<div class="stat-item">
|
|
188
|
+
<span class="stat-label">Tasks</span>
|
|
189
|
+
<span class="stat-value">${project.taskCount}</span>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="stat-item">
|
|
192
|
+
<span class="stat-label">Completed</span>
|
|
193
|
+
<span class="stat-value">${project.completedTaskCount}</span>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<div class="progress-bar">
|
|
198
|
+
<div class="progress-fill" style="width: ${(project.completionRate * 100).toFixed(0)}%"></div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
`)}
|
|
202
|
+
</div>
|
|
203
|
+
`;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
__decorate([
|
|
207
|
+
state(),
|
|
208
|
+
__metadata("design:type", Array)
|
|
209
|
+
], LabelStudioProjectList.prototype, "projects", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
state(),
|
|
212
|
+
__metadata("design:type", Boolean)
|
|
213
|
+
], LabelStudioProjectList.prototype, "loading", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
state(),
|
|
216
|
+
__metadata("design:type", String)
|
|
217
|
+
], LabelStudioProjectList.prototype, "error", void 0);
|
|
218
|
+
LabelStudioProjectList = __decorate([
|
|
219
|
+
customElement('label-studio-project-list')
|
|
220
|
+
], LabelStudioProjectList);
|
|
221
|
+
export { LabelStudioProjectList };
|
|
222
|
+
//# sourceMappingURL=label-studio-project-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label-studio-project-list.js","sourceRoot":"","sources":["../client/label-studio-project-list.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/outlined-button.js'\nimport '@material/web/icon/icon.js'\nimport '@material/web/iconbutton/icon-button.js'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { client, PageView, navigate } from '@things-factory/shell/client'\nimport gql from 'graphql-tag'\n\n@customElement('label-studio-project-list')\nexport class LabelStudioProjectList extends PageView {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n padding: 20px;\n background-color: var(--md-sys-color-surface);\n }\n\n .header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n }\n\n .header h2 {\n margin: 0;\n color: var(--md-sys-color-on-surface);\n }\n\n .project-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 20px;\n overflow-y: auto;\n }\n\n .project-card {\n background: var(--md-sys-color-surface-container);\n border-radius: 12px;\n padding: 20px;\n cursor: pointer;\n transition: all 0.3s ease;\n }\n\n .project-card:hover {\n background: var(--md-sys-color-surface-container-high);\n transform: translateY(-2px);\n box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n }\n\n .project-card h3 {\n margin: 0 0 10px 0;\n color: var(--md-sys-color-on-surface);\n }\n\n .project-card p {\n margin: 5px 0;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 14px;\n }\n\n .project-stats {\n display: flex;\n gap: 15px;\n margin-top: 15px;\n }\n\n .stat-item {\n display: flex;\n flex-direction: column;\n }\n\n .stat-label {\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .stat-value {\n font-size: 20px;\n font-weight: bold;\n color: var(--md-sys-color-primary);\n }\n\n .progress-bar {\n width: 100%;\n height: 8px;\n background: var(--md-sys-color-surface-variant);\n border-radius: 4px;\n margin-top: 10px;\n overflow: hidden;\n }\n\n .progress-fill {\n height: 100%;\n background: var(--md-sys-color-primary);\n transition: width 0.3s ease;\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 color: var(--md-sys-color-error);\n padding: 20px;\n text-align: center;\n }\n `\n ]\n\n @state() projects: any[] = []\n @state() loading: boolean = true\n @state() error: string = ''\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (this.active) {\n this.loadProjects()\n }\n }\n\n async loadProjects() {\n try {\n this.loading = true\n\n const response = await client.query({\n query: gql`\n query {\n labelStudioProjects {\n id\n title\n description\n taskCount\n completedTaskCount\n completionRate\n createdAt\n updatedAt\n }\n }\n `\n })\n\n this.projects = response.data.labelStudioProjects\n this.loading = false\n } catch (error: any) {\n console.error('Failed to load projects:', error)\n this.error = error.message || 'Failed to load projects'\n this.loading = false\n }\n }\n\n createProject() {\n navigate('label-studio-project-create')\n }\n\n openProject(projectId: number) {\n // Open Label Studio viewer with project\n navigate(`label-studio-label/${projectId}`)\n }\n\n render() {\n if (this.loading) {\n return html`<div class=\"loading\">Loading projects...</div>`\n }\n\n if (this.error) {\n return html`<div class=\"error\">${this.error}</div>`\n }\n\n return html`\n <div class=\"header\">\n <h2>Label Studio Projects</h2>\n <md-filled-button @click=${this.createProject}>\n <md-icon slot=\"icon\">add</md-icon>\n New Project\n </md-filled-button>\n </div>\n\n <div class=\"project-grid\">\n ${this.projects.map(\n project => html`\n <div class=\"project-card\" @click=${() => this.openProject(project.id)}>\n <h3>${project.title}</h3>\n <p>${project.description || 'No description'}</p>\n\n <div class=\"project-stats\">\n <div class=\"stat-item\">\n <span class=\"stat-label\">Tasks</span>\n <span class=\"stat-value\">${project.taskCount}</span>\n </div>\n <div class=\"stat-item\">\n <span class=\"stat-label\">Completed</span>\n <span class=\"stat-value\">${project.completedTaskCount}</span>\n </div>\n </div>\n\n <div class=\"progress-bar\">\n <div class=\"progress-fill\" style=\"width: ${(project.completionRate * 100).toFixed(0)}%\"></div>\n </div>\n </div>\n `\n )}\n </div>\n `\n }\n}\n"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Label Studio Wrapper Component for Things Factory
|
|
3
|
+
*
|
|
4
|
+
* This component embeds Label Studio within Things Factory using an iframe.
|
|
5
|
+
* It uses subdomain-based cookie sharing for SSO authentication, eliminating
|
|
6
|
+
* the need for proxying.
|
|
7
|
+
*
|
|
8
|
+
* ## Key Features:
|
|
9
|
+
* - Direct Label Studio access (no proxy needed)
|
|
10
|
+
* - Automatic SSO cookie setup via shared domain
|
|
11
|
+
* - Hidden header for seamless integration
|
|
12
|
+
* - Floating Action Buttons (FAB) for Reload and Fullscreen
|
|
13
|
+
*
|
|
14
|
+
* ## Architecture:
|
|
15
|
+
* 1. Client calls /label-studio/sso/setup
|
|
16
|
+
* 2. Backend sets cookie with shared domain (e.g., .nubison.localhost)
|
|
17
|
+
* 3. Browser → Label Studio (direct, with cookie)
|
|
18
|
+
*
|
|
19
|
+
* ## Configuration Requirements:
|
|
20
|
+
* - Label Studio and Things Factory must use subdomain pattern (e.g., label.example.com, app.example.com)
|
|
21
|
+
* - Label Studio .env must include:
|
|
22
|
+
* - CSRF_TRUSTED_ORIGINS: Include Things Factory frontend domain
|
|
23
|
+
* - ALLOWED_HOSTS: Include subdomain patterns
|
|
24
|
+
* - JWT_SSO_* settings for SSO authentication
|
|
25
|
+
* - Things Factory config must include:
|
|
26
|
+
* - labelStudio.serverUrl: Label Studio URL
|
|
27
|
+
* - labelStudio.cookieDomain: Shared cookie domain (e.g., .example.com)
|
|
28
|
+
*
|
|
29
|
+
* ## Usage:
|
|
30
|
+
* ```html
|
|
31
|
+
* <label-studio-wrapper></label-studio-wrapper>
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* The component automatically:
|
|
35
|
+
* 1. Calls SSO setup endpoint to establish authentication
|
|
36
|
+
* 2. Builds iframe URL pointing directly to Label Studio
|
|
37
|
+
* 3. Adds ?hideHeader=true to hide Label Studio's header
|
|
38
|
+
* 4. Provides FAB buttons for reload and fullscreen
|
|
39
|
+
*/
|
|
40
|
+
import { LitElement } from 'lit';
|
|
41
|
+
export declare class LabelStudioWrapper extends LitElement {
|
|
42
|
+
static styles: import("lit").CSSResult;
|
|
43
|
+
private loading;
|
|
44
|
+
private error;
|
|
45
|
+
private iframeUrl;
|
|
46
|
+
private path;
|
|
47
|
+
private iframeLoaded;
|
|
48
|
+
private labelStudioUrl;
|
|
49
|
+
private iframe;
|
|
50
|
+
updated(changes: any): Promise<void>;
|
|
51
|
+
buildIframeUrl(): Promise<void>;
|
|
52
|
+
handleIframeLoad(): void;
|
|
53
|
+
handleIframeError(): void;
|
|
54
|
+
handleReload(): void;
|
|
55
|
+
handleFullscreen(): void;
|
|
56
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
57
|
+
}
|