@things-factory/integration-label-studio 10.1.247 → 10.1.248

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 (99) hide show
  1. package/package.json +3 -3
  2. package/dist-client/bootstrap.d.ts +0 -1
  3. package/dist-client/bootstrap.js +0 -2
  4. package/dist-client/bootstrap.js.map +0 -1
  5. package/dist-client/index.d.ts +0 -1
  6. package/dist-client/index.js +0 -2
  7. package/dist-client/index.js.map +0 -1
  8. package/dist-client/label-studio-label-page.d.ts +0 -8
  9. package/dist-client/label-studio-label-page.js +0 -54
  10. package/dist-client/label-studio-label-page.js.map +0 -1
  11. package/dist-client/label-studio-project-create.d.ts +0 -16
  12. package/dist-client/label-studio-project-create.js +0 -237
  13. package/dist-client/label-studio-project-create.js.map +0 -1
  14. package/dist-client/label-studio-project-list.d.ts +0 -16
  15. package/dist-client/label-studio-project-list.js +0 -222
  16. package/dist-client/label-studio-project-list.js.map +0 -1
  17. package/dist-client/label-studio-wrapper.d.ts +0 -57
  18. package/dist-client/label-studio-wrapper.js +0 -304
  19. package/dist-client/label-studio-wrapper.js.map +0 -1
  20. package/dist-client/route.d.ts +0 -1
  21. package/dist-client/route.js +0 -14
  22. package/dist-client/route.js.map +0 -1
  23. package/dist-client/tsconfig.tsbuildinfo +0 -1
  24. package/dist-server/controller/label-studio-role-mapper.d.ts +0 -35
  25. package/dist-server/controller/label-studio-role-mapper.js +0 -65
  26. package/dist-server/controller/label-studio-role-mapper.js.map +0 -1
  27. package/dist-server/controller/user-provisioning-service.d.ts +0 -66
  28. package/dist-server/controller/user-provisioning-service.js +0 -264
  29. package/dist-server/controller/user-provisioning-service.js.map +0 -1
  30. package/dist-server/index.d.ts +0 -7
  31. package/dist-server/index.js +0 -19
  32. package/dist-server/index.js.map +0 -1
  33. package/dist-server/route/label-studio-sso.d.ts +0 -2
  34. package/dist-server/route/label-studio-sso.js +0 -156
  35. package/dist-server/route/label-studio-sso.js.map +0 -1
  36. package/dist-server/route/webhook.d.ts +0 -66
  37. package/dist-server/route/webhook.js +0 -249
  38. package/dist-server/route/webhook.js.map +0 -1
  39. package/dist-server/route.d.ts +0 -1
  40. package/dist-server/route.js +0 -21
  41. package/dist-server/route.js.map +0 -1
  42. package/dist-server/service/ai-prediction-service.d.ts +0 -27
  43. package/dist-server/service/ai-prediction-service.js +0 -222
  44. package/dist-server/service/ai-prediction-service.js.map +0 -1
  45. package/dist-server/service/dataset-labeling-integration.d.ts +0 -44
  46. package/dist-server/service/dataset-labeling-integration.js +0 -512
  47. package/dist-server/service/dataset-labeling-integration.js.map +0 -1
  48. package/dist-server/service/external-data-source-service.d.ts +0 -78
  49. package/dist-server/service/external-data-source-service.js +0 -415
  50. package/dist-server/service/external-data-source-service.js.map +0 -1
  51. package/dist-server/service/index.d.ts +0 -12
  52. package/dist-server/service/index.js +0 -27
  53. package/dist-server/service/index.js.map +0 -1
  54. package/dist-server/service/label-studio-sso-service.d.ts +0 -38
  55. package/dist-server/service/label-studio-sso-service.js +0 -98
  56. package/dist-server/service/label-studio-sso-service.js.map +0 -1
  57. package/dist-server/service/ml/ml-backend-service.d.ts +0 -23
  58. package/dist-server/service/ml/ml-backend-service.js +0 -153
  59. package/dist-server/service/ml/ml-backend-service.js.map +0 -1
  60. package/dist-server/service/prediction/prediction-management.d.ts +0 -32
  61. package/dist-server/service/prediction/prediction-management.js +0 -299
  62. package/dist-server/service/prediction/prediction-management.js.map +0 -1
  63. package/dist-server/service/project/project-management.d.ts +0 -36
  64. package/dist-server/service/project/project-management.js +0 -309
  65. package/dist-server/service/project/project-management.js.map +0 -1
  66. package/dist-server/service/task/task-management.d.ts +0 -42
  67. package/dist-server/service/task/task-management.js +0 -372
  68. package/dist-server/service/task/task-management.js.map +0 -1
  69. package/dist-server/service/user-provisioning/user-sync-mutation.d.ts +0 -28
  70. package/dist-server/service/user-provisioning/user-sync-mutation.js +0 -111
  71. package/dist-server/service/user-provisioning/user-sync-mutation.js.map +0 -1
  72. package/dist-server/service/webhook/webhook-management.d.ts +0 -21
  73. package/dist-server/service/webhook/webhook-management.js +0 -134
  74. package/dist-server/service/webhook/webhook-management.js.map +0 -1
  75. package/dist-server/tsconfig.tsbuildinfo +0 -1
  76. package/dist-server/types/dataset-labeling-types.d.ts +0 -71
  77. package/dist-server/types/dataset-labeling-types.js +0 -259
  78. package/dist-server/types/dataset-labeling-types.js.map +0 -1
  79. package/dist-server/types/label-studio-types.d.ts +0 -128
  80. package/dist-server/types/label-studio-types.js +0 -494
  81. package/dist-server/types/label-studio-types.js.map +0 -1
  82. package/dist-server/types/prediction-types.d.ts +0 -39
  83. package/dist-server/types/prediction-types.js +0 -121
  84. package/dist-server/types/prediction-types.js.map +0 -1
  85. package/dist-server/utils/annotation-exporter.d.ts +0 -104
  86. package/dist-server/utils/annotation-exporter.js +0 -261
  87. package/dist-server/utils/annotation-exporter.js.map +0 -1
  88. package/dist-server/utils/label-config-builder.d.ts +0 -117
  89. package/dist-server/utils/label-config-builder.js +0 -286
  90. package/dist-server/utils/label-config-builder.js.map +0 -1
  91. package/dist-server/utils/label-studio-api-client.d.ts +0 -180
  92. package/dist-server/utils/label-studio-api-client.js +0 -401
  93. package/dist-server/utils/label-studio-api-client.js.map +0 -1
  94. package/dist-server/utils/media-url-extractor.d.ts +0 -45
  95. package/dist-server/utils/media-url-extractor.js +0 -152
  96. package/dist-server/utils/media-url-extractor.js.map +0 -1
  97. package/dist-server/utils/task-transformer.d.ts +0 -108
  98. package/dist-server/utils/task-transformer.js +0 -260
  99. package/dist-server/utils/task-transformer.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/integration-label-studio",
3
- "version": "10.1.247",
3
+ "version": "10.1.248",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -30,7 +30,7 @@
30
30
  "@operato/layout": "^10.0.0",
31
31
  "@things-factory/ai-inference": "^10.1.149",
32
32
  "@things-factory/auth-base": "^10.1.247",
33
- "@things-factory/dataset": "^10.1.247",
33
+ "@things-factory/dataset": "^10.1.248",
34
34
  "@things-factory/env": "^10.1.149",
35
35
  "@things-factory/menu-base": "^10.1.247",
36
36
  "@things-factory/shell": "^10.1.247"
@@ -43,5 +43,5 @@
43
43
  "sso",
44
44
  "data-labeling"
45
45
  ],
46
- "gitHead": "3c672029b2ef4a09f3370d37e5d225392856a602"
46
+ "gitHead": "ca390a4b791d6e8630388167d3da6031034b5a5e"
47
47
  }
@@ -1 +0,0 @@
1
- export default function bootstrap(): void;
@@ -1,2 +0,0 @@
1
- export default function bootstrap() { }
2
- //# sourceMappingURL=bootstrap.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../client/bootstrap.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,SAAS,KAAI,CAAC","sourcesContent":["export default function bootstrap() {}\n"]}
@@ -1 +0,0 @@
1
- export * from './label-studio-wrapper.js';
@@ -1,2 +0,0 @@
1
- export * from './label-studio-wrapper.js';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA","sourcesContent":["export * from './label-studio-wrapper.js'\n"]}
@@ -1,8 +0,0 @@
1
- import './label-studio-wrapper.js';
2
- import { PageView } from '@things-factory/shell/client';
3
- export declare class LabelStudioLabelPage extends PageView {
4
- static styles: import("lit").CSSResult;
5
- projectId: string;
6
- pageUpdated(changes: any, lifecycle: any, changedBefore: any): Promise<void>;
7
- render(): import("lit-html").TemplateResult<1>;
8
- }
@@ -1,54 +0,0 @@
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
@@ -1 +0,0 @@
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"]}
@@ -1,16 +0,0 @@
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
- }
@@ -1,237 +0,0 @@
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
- background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));
50
- color: var(--input-field-color, var(--md-sys-color-on-surface));
51
- width: 100%;
52
- min-height: 200px;
53
- padding: 12px;
54
- border: 1px solid var(--md-sys-color-outline);
55
- border-radius: 4px;
56
- font-family: monospace;
57
- font-size: 14px;
58
- resize: vertical;
59
- }
60
-
61
- .actions {
62
- display: flex;
63
- gap: 10px;
64
- justify-content: flex-end;
65
- margin-top: 20px;
66
- }
67
-
68
- .template-examples {
69
- margin-top: 20px;
70
- padding: 15px;
71
- background: var(--md-sys-color-surface-variant);
72
- border-radius: 8px;
73
- }
74
-
75
- .template-examples h4 {
76
- margin: 0 0 10px 0;
77
- }
78
-
79
- .template-btn {
80
- margin: 5px;
81
- }
82
-
83
- .error {
84
- color: var(--md-sys-color-error);
85
- margin: 10px 0;
86
- }
87
- `
88
- ]; }
89
- render() {
90
- return html `
91
- <h2>Create Label Studio Project</h2>
92
-
93
- <div class="form-field">
94
- <label>Project Title *</label>
95
- <md-filled-text-field
96
- .value=${this.title}
97
- @input=${(e) => (this.title = e.target.value)}
98
- placeholder="Enter project title"
99
- ></md-filled-text-field>
100
- </div>
101
-
102
- <div class="form-field">
103
- <label>Description</label>
104
- <md-filled-text-field
105
- .value=${this.description}
106
- @input=${(e) => (this.description = e.target.value)}
107
- placeholder="Enter project description"
108
- ></md-filled-text-field>
109
- </div>
110
-
111
- <div class="form-field">
112
- <label>Label Configuration (XML) *</label>
113
- <textarea
114
- .value=${this.labelConfig}
115
- @input=${(e) => (this.labelConfig = e.target.value)}
116
- placeholder='<View>&#10; <Text name="text" value="$text"/>&#10; <Choices name="label" toName="text">&#10; <Choice value="Positive"/>&#10; <Choice value="Negative"/>&#10; </Choices>&#10;</View>'
117
- ></textarea>
118
- </div>
119
-
120
- <div class="template-examples">
121
- <h4>Quick Templates</h4>
122
- <md-filled-button class="template-btn" @click=${() => this.useTemplate('text-classification')}>
123
- Text Classification
124
- </md-filled-button>
125
- <md-filled-button class="template-btn" @click=${() => this.useTemplate('image-classification')}>
126
- Image Classification
127
- </md-filled-button>
128
- <md-filled-button class="template-btn" @click=${() => this.useTemplate('ner')}>
129
- Named Entity Recognition
130
- </md-filled-button>
131
- </div>
132
-
133
- ${this.error ? html `<div class="error">${this.error}</div>` : ''}
134
-
135
- <div class="actions">
136
- <md-text-button @click=${this.cancel}>Cancel</md-text-button>
137
- <md-filled-button @click=${this.save} ?disabled=${this.saving}>
138
- ${this.saving ? 'Creating...' : 'Create Project'}
139
- </md-filled-button>
140
- </div>
141
- `;
142
- }
143
- useTemplate(type) {
144
- const templates = {
145
- 'text-classification': `<View>
146
- <Text name="text" value="$text"/>
147
- <Choices name="sentiment" toName="text" choice="single">
148
- <Choice value="Positive"/>
149
- <Choice value="Negative"/>
150
- <Choice value="Neutral"/>
151
- </Choices>
152
- </View>`,
153
- 'image-classification': `<View>
154
- <Image name="image" value="$image"/>
155
- <Choices name="choice" toName="image" choice="single">
156
- <Choice value="Cat"/>
157
- <Choice value="Dog"/>
158
- <Choice value="Bird"/>
159
- </Choices>
160
- </View>`,
161
- ner: `<View>
162
- <Text name="text" value="$text"/>
163
- <Labels name="label" toName="text">
164
- <Label value="Person" background="red"/>
165
- <Label value="Organization" background="darkorange"/>
166
- <Label value="Location" background="blue"/>
167
- </Labels>
168
- </View>`
169
- };
170
- this.labelConfig = templates[type] || '';
171
- }
172
- async save() {
173
- if (!this.title || !this.labelConfig) {
174
- this.error = 'Title and Label Configuration are required';
175
- return;
176
- }
177
- try {
178
- this.saving = true;
179
- this.error = '';
180
- const response = await client.mutate({
181
- mutation: gql `
182
- mutation CreateProject($input: CreateProjectInput!) {
183
- createLabelStudioProject(input: $input) {
184
- id
185
- title
186
- }
187
- }
188
- `,
189
- variables: {
190
- input: {
191
- title: this.title,
192
- description: this.description || undefined,
193
- labelConfig: this.labelConfig
194
- }
195
- }
196
- });
197
- const project = response.data.createLabelStudioProject;
198
- // Navigate to project list
199
- navigate('label-studio-project-list');
200
- }
201
- catch (error) {
202
- console.error('Failed to create project:', error);
203
- this.error = error.message || 'Failed to create project';
204
- }
205
- finally {
206
- this.saving = false;
207
- }
208
- }
209
- cancel() {
210
- navigate('label-studio-project-list');
211
- }
212
- };
213
- __decorate([
214
- state(),
215
- __metadata("design:type", String)
216
- ], LabelStudioProjectCreate.prototype, "title", void 0);
217
- __decorate([
218
- state(),
219
- __metadata("design:type", String)
220
- ], LabelStudioProjectCreate.prototype, "description", void 0);
221
- __decorate([
222
- state(),
223
- __metadata("design:type", String)
224
- ], LabelStudioProjectCreate.prototype, "labelConfig", void 0);
225
- __decorate([
226
- state(),
227
- __metadata("design:type", Boolean)
228
- ], LabelStudioProjectCreate.prototype, "saving", void 0);
229
- __decorate([
230
- state(),
231
- __metadata("design:type", String)
232
- ], LabelStudioProjectCreate.prototype, "error", void 0);
233
- LabelStudioProjectCreate = __decorate([
234
- customElement('label-studio-project-create')
235
- ], LabelStudioProjectCreate);
236
- export { LabelStudioProjectCreate };
237
- //# sourceMappingURL=label-studio-project-create.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"label-studio-project-create.js","sourceRoot":"","sources":["../client/label-studio-project-create.ts"],"names":[],"mappings":";AAAA,OAAO,uCAAuC,CAAA;AAC9C,OAAO,qCAAqC,CAAA;AAC5C,OAAO,8CAA8C,CAAA;AACrD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACzE,OAAO,GAAG,MAAM,aAAa,CAAA;AAGtB,IAAM,wBAAwB,GAA9B,MAAM,wBAAyB,SAAQ,QAAQ;IAA/C;;QAyEI,UAAK,GAAW,EAAE,CAAA;QAClB,gBAAW,GAAW,EAAE,CAAA;QACxB,gBAAW,GAAW,EAAE,CAAA;QACxB,WAAM,GAAY,KAAK,CAAA;QACvB,UAAK,GAAW,EAAE,CAAA;IAmI7B,CAAC;aA/MQ,WAAM,GAAG;QACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoEF;KACF,AAtEY,CAsEZ;IAQD,MAAM;QACJ,OAAO,IAAI,CAAA;;;;;;mBAMI,IAAI,CAAC,KAAK;mBACV,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;mBAQzC,IAAI,CAAC,WAAW;mBAChB,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;;;;;;;mBAQ/C,IAAI,CAAC,WAAW;mBAChB,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,GAAI,CAAC,CAAC,MAA8B,CAAC,KAAK,CAAC;;;;;;;wDAOnC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC;;;wDAG7C,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC;;;wDAG9C,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;;;;;QAK7E,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE;;;iCAGrC,IAAI,CAAC,MAAM;mCACT,IAAI,CAAC,IAAI,cAAc,IAAI,CAAC,MAAM;YACzD,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,gBAAgB;;;KAGrD,CAAA;IACH,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,MAAM,SAAS,GAAG;YAChB,qBAAqB,EAAE;;;;;;;QAOrB;YACF,sBAAsB,EAAE;;;;;;;QAOtB;YACF,GAAG,EAAE;;;;;;;QAOH;SACH,CAAA;QAED,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;IAC1C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,GAAG,4CAA4C,CAAA;YACzD,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;YAClB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;YAEf,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;gBACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;SAOZ;gBACD,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,SAAS;wBAC1C,WAAW,EAAE,IAAI,CAAC,WAAW;qBAC9B;iBACF;aACF,CAAC,CAAA;YAEF,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAA;YAEtD,2BAA2B;YAC3B,QAAQ,CAAC,2BAA2B,CAAC,CAAA;QACvC,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAA;YACjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,IAAI,0BAA0B,CAAA;QAC1D,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,MAAM,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;IAED,MAAM;QACJ,QAAQ,CAAC,2BAA2B,CAAC,CAAA;IACvC,CAAC;;AAtIQ;IAAR,KAAK,EAAE;;uDAAmB;AAClB;IAAR,KAAK,EAAE;;6DAAyB;AACxB;IAAR,KAAK,EAAE;;6DAAyB;AACxB;IAAR,KAAK,EAAE;;wDAAwB;AACvB;IAAR,KAAK,EAAE;;uDAAmB;AA7EhB,wBAAwB;IADpC,aAAa,CAAC,6BAA6B,CAAC;GAChC,wBAAwB,CAgNpC","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 background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\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>&#10; <Text name=\"text\" value=\"$text\"/>&#10; <Choices name=\"label\" toName=\"text\">&#10; <Choice value=\"Positive\"/>&#10; <Choice value=\"Negative\"/>&#10; </Choices>&#10;</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"]}
@@ -1,16 +0,0 @@
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
- }
@@ -1,222 +0,0 @@
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
@@ -1 +0,0 @@
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"]}