├── .babelrc ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .env.defaults ├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── actions │ ├── download-artifact │ │ └── action.yml │ └── upload-artifact │ │ └── action.yml ├── autolabeler.yml ├── pr-title-checker-config.json ├── pull_request_template.md └── workflows │ ├── build_bundle.yml │ ├── cancel_cicd_pipeline.yml │ ├── cicd_pipeline.yml │ ├── e2e_tests.yml │ ├── eslint.yml │ ├── fun_tests.yml │ ├── git-command.yml │ ├── help-command.yml │ ├── jira-command.yml │ ├── npm_audit.skip │ ├── pr-labeler.yml │ ├── release-set-version.yml │ ├── slash-command-dispatch.yml │ ├── sync-pr-ls.yml │ ├── sync-pr-lse.yml │ ├── tests_coverage.yml │ └── unit_tests.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LSF.init.md ├── README.md ├── __mocks__ └── global.ts ├── codecov.yml ├── dev-server.js ├── e2e ├── .eslintrc.js ├── codecept.conf.js ├── coverage-to-istanbul.js ├── examples │ ├── audio-paragraphs.js │ ├── audio-regions.js │ ├── classification.js │ ├── data │ │ └── sample-sin.json │ ├── image-bboxes.js │ ├── image-ellipses.js │ ├── image-keypoints.js │ ├── image-polygons.js │ ├── index.js │ ├── ner-url.js │ ├── nested.js │ ├── text-html.js │ ├── text-paragraphs.js │ ├── timeseries-url-indexed.js │ └── utils.js ├── fragments │ ├── AtAudioView.js │ ├── AtDateTime.ts │ ├── AtDetails.js │ ├── AtImageView.js │ ├── AtLabels.js │ ├── AtOutliner.js │ ├── AtPanels.js │ ├── AtParagraphs.js │ ├── AtRichText.js │ ├── AtSettings.js │ ├── AtSidebar.js │ ├── AtTableView.js │ ├── AtTaxonomy.js │ ├── AtTextAreaView.js │ ├── AtTimeSeries.js │ ├── AtTopbar.js │ ├── AtVideoView.js │ ├── ErrorsCollector.js │ ├── LabelStudio.js │ ├── Labels.js │ ├── Modals.js │ ├── Regions.js │ └── Tools.js ├── helpers │ ├── Annotations.ts │ ├── Assertion.js │ ├── DateTime.ts │ ├── MouseActions.js │ ├── PlaywrightAddon.js │ └── Selection.js ├── jsconfig.json ├── package.json ├── plugins │ ├── errorsCollector.js │ ├── featureFlags.js │ ├── istanbulСoverage.js │ └── stepLogsModifier.js ├── setup │ └── feature-flags.js ├── steps.d.ts ├── steps_file.js ├── tests │ ├── audio │ │ ├── audio-controls.test.js │ │ ├── audio-errors.test.js │ │ ├── audio-regions.test.js │ │ └── audio-webaudio-decoder.test.js │ ├── date-time.test.js │ ├── empty-labels.test.js │ ├── helpers.js │ ├── history.test.js │ ├── image-labels.test.js │ ├── image-list.test.js │ ├── image.gestures.test.js │ ├── image.magic-wand.test.js │ ├── image.selected-region.test.js │ ├── image.shapes.test.js │ ├── image.test.js │ ├── image.transformer.test.js │ ├── image.zoom-rotate.test.js │ ├── maxUsage.test.js │ ├── ner-text.test.js │ ├── ner.test.js │ ├── nested-choices.test.js │ ├── nested.test.js │ ├── ocr.test.js │ ├── outliner.test.js │ ├── paragraphs-filter.test.js │ ├── readonly-tests │ │ ├── readonly-annotations │ │ │ ├── audio-annotation-readonly.test.js │ │ │ ├── classification-annotation-readonly.test.js │ │ │ ├── image-annotation-readonly.test.js │ │ │ ├── ner-annotation-readonly.test.js │ │ │ └── timeseries-annotation-readonly.test.js │ │ └── readonly-results │ │ │ ├── audio-results-readonly.test.js │ │ │ ├── classification-results-readonly.test.js │ │ │ ├── image-results-readonly.test.js │ │ │ ├── ner-results-readonly.test.js │ │ │ └── timeseries-results-readonly.test.js │ ├── regression-tests │ │ ├── annotation-button.test.js │ │ ├── brush-relations.test.js │ │ ├── dynamic-choices.test.js │ │ ├── hotkey.test.js │ │ ├── image-ctrl-drawing.test.js │ │ ├── image-draw-undo.test.js │ │ ├── image-width.test.js │ │ ├── image-zoom-position.test.js │ │ ├── image-zoom-transform.test.js │ │ ├── image.regions-select.test.js │ │ ├── multiple-same-named-tools.test.js │ │ ├── numpad-hotkeys.test.js │ │ ├── outliner-regions-dnd.test.js │ │ ├── preselected-choices.test.js │ │ ├── richtext.test.js │ │ ├── video-meta.test.js │ │ ├── video-snapshot.test.js │ │ ├── video-timeline-seek-indicator.test.js │ │ ├── video-unmount.test.js │ │ ├── wrong-results-order.test.js │ │ └── zoomed-image-displaying.test.js │ ├── repeater-paginate-scroll.test.js │ ├── required.test.js │ ├── rich-text │ │ ├── rich-text-basic-functional.test.js │ │ ├── rich-text-edge-cases.test.js │ │ ├── rich-text-perfomance.test.js │ │ ├── rich-text-regions-displaying.test.js │ │ └── rich-text-regions-interactions.test.js │ ├── shortcuts.test.js │ ├── smart-tools.history.test.js │ ├── smoke.test.js │ ├── sync │ │ ├── audio-paragraphs.test.js │ │ ├── audio-video-paragraphs.test.js │ │ └── multiple-audio.test.js │ ├── table.test.js │ ├── taxonomy.test.js │ ├── text-area.test.js │ ├── textarea.skip-duplicates.test.js │ ├── timeseries.test.js │ ├── toggle-visibility.test.js │ ├── unfinished-polygons.test.js │ └── visual-tags.test.js ├── tsconfig.json ├── utils │ ├── asserts.js │ └── feature-flags.js └── yarn.lock ├── examples ├── audio_classification │ ├── START.md │ ├── annotations │ │ ├── 0.json │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── audio_regions │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── audio_video_paragraphs │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── classification_mixed │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── datetime │ ├── config.xml │ └── index.js ├── dialogue_analysis │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── html_document │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_bbox │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_bbox_large │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_classification │ └── config.xml ├── image_ellipses │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_keypoints │ ├── START.md │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_list │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.ts │ └── tasks.json ├── image_list_large │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.ts │ └── tasks.json ├── image_list_perregion │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.ts │ └── tasks.json ├── image_magic_wand │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── gcp_cors_config.json │ ├── index.js │ └── tasks.json ├── image_multilabel │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_ocr │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_polygons │ ├── START.md │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_segmentation │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── image_tools │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── named_entity │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── nested_choices │ ├── annotations │ │ └── 0.json │ ├── complicated.js │ ├── config-complicated.xml │ ├── config.xml │ ├── index.js │ └── tasks.json ├── pairwise │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── phrases │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── ranker │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── ranker_buckets │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── references │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── repeater │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── required │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── rich_text_html │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── rich_text_plain │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── rich_text_plain_remote │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── sentiment_analysis │ ├── START.md │ ├── annotations │ │ ├── 0.json │ │ ├── 1.json │ │ ├── 2.json │ │ └── 3.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── table │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── table_csv │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── taxonomy │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── taxonomy_large │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── taxonomy_large_inline │ ├── annotations │ │ └── 0.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── timeseries │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── sample-sin.json ├── timeseries_single │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ ├── sample-sin.json │ └── tasks.json ├── transcribe_audio │ ├── START.md │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── video │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── video_audio │ ├── annotations │ │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json └── video_bboxes │ ├── annotations │ └── 1.json │ ├── config.xml │ ├── index.js │ └── tasks.json ├── images ├── heartex_icon_opossum_green.svg ├── heartex_icon_opossum_green@2x.png ├── heartex_icon_opossum_orang.svg ├── heartex_icon_opossum_orang@2x.png ├── label-studio-examples.gif ├── logo.png ├── ls_github.png ├── ls_github_hq.png ├── magicwand_example.gif ├── nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg ├── opossum_looking.png ├── opossum_looking.svg └── screenshots │ ├── audio_classification.png │ ├── audio_regions.png │ ├── audio_transcription.png │ ├── chatbot_analysis.png │ ├── html_document.png │ ├── image_bbox.png │ ├── image_keypoints.png │ ├── image_magic_wand.png │ ├── image_polygons.png │ ├── named_entity.png │ ├── ranker.png │ └── text_classification.png ├── jest.config.js ├── jest.setup.js ├── nyc.config.js ├── package.json ├── public ├── favicon.ico ├── files │ ├── barradeen-emotional.mp3 │ ├── images │ │ └── DSC03121.jpg │ ├── opossum_intro.webm │ ├── opossum_snow.mp4 │ └── video.mp4 ├── images │ ├── 3nowhite.svg │ ├── GitHub-Mark-64px.png │ ├── astro-visuals.jpg │ ├── logo.png │ ├── ls_logo.png │ └── ls_logo.svg ├── index.html ├── manifest.json └── styles │ └── main.css ├── scripts ├── copy.sh ├── create-docs.js ├── postinstall.sh └── publish.sh ├── src ├── Component.js ├── LabelStudio.tsx ├── assets │ ├── icons │ │ ├── annotation.svg │ │ ├── annotation │ │ │ ├── draft_created.svg │ │ │ ├── draft_created2.svg │ │ │ ├── entity_created.svg │ │ │ ├── ground_truth.svg │ │ │ ├── imported.svg │ │ │ ├── index.ts │ │ │ ├── prediction.svg │ │ │ ├── propagated.svg │ │ │ ├── removed.svg │ │ │ ├── skipped.svg │ │ │ ├── skipped2.svg │ │ │ ├── thumbs_down.svg │ │ │ └── thumbs_up.svg │ │ ├── arrow-left-small.svg │ │ ├── arrow-left.svg │ │ ├── arrow-right-small.svg │ │ ├── arrow-right.svg │ │ ├── ban.svg │ │ ├── check-bold.svg │ │ ├── check.svg │ │ ├── chevron.svg │ │ ├── close.svg │ │ ├── collapse-small.svg │ │ ├── collapse.svg │ │ ├── comment-check.svg │ │ ├── comment-red.svg │ │ ├── comment-resolved.svg │ │ ├── comment-unresolved.svg │ │ ├── copy.svg │ │ ├── cross-bold.svg │ │ ├── cross.svg │ │ ├── cursor.svg │ │ ├── date.svg │ │ ├── delete.svg │ │ ├── details.svg │ │ ├── duplicate.svg │ │ ├── ellipsis.svg │ │ ├── expand-alt.svg │ │ ├── expand-small.svg │ │ ├── expand.svg │ │ ├── fast.svg │ │ ├── filter.svg │ │ ├── grid.svg │ │ ├── hamburger.svg │ │ ├── help.svg │ │ ├── index.tsx │ │ ├── info-outline.svg │ │ ├── info.svg │ │ ├── invisible.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── locked.svg │ │ ├── menu.svg │ │ ├── minus.svg │ │ ├── ocr.svg │ │ ├── outliner │ │ │ ├── collapse.svg │ │ │ ├── drag.svg │ │ │ ├── expand.svg │ │ │ ├── eye_closed.svg │ │ │ ├── eye_opened.svg │ │ │ ├── index.ts │ │ │ └── info.svg │ │ ├── parent-link.svg │ │ ├── pause.svg │ │ ├── play.svg │ │ ├── player │ │ │ ├── pause.svg │ │ │ ├── play.svg │ │ │ └── step.svg │ │ ├── plus-alt.svg │ │ ├── plus-circle.svg │ │ ├── plus.svg │ │ ├── png │ │ │ ├── check.png │ │ │ └── cross.png │ │ ├── properties │ │ │ └── angle.svg │ │ ├── redo.svg │ │ ├── relations │ │ │ ├── bi.svg │ │ │ ├── left.svg │ │ │ └── right.svg │ │ ├── remove.svg │ │ ├── rotate.svg │ │ ├── send.svg │ │ ├── settings-alt.svg │ │ ├── settings.svg │ │ ├── slow.svg │ │ ├── sort-down-new.svg │ │ ├── sort-down.svg │ │ ├── sort-up-new.svg │ │ ├── sort-up.svg │ │ ├── sparks.svg │ │ ├── speed.svg │ │ ├── star-outline.svg │ │ ├── star.svg │ │ ├── svg.d.ts │ │ ├── tag-alt.svg │ │ ├── tag.svg │ │ ├── text.svg │ │ ├── timeline │ │ │ ├── backward.svg │ │ │ ├── chevron_left.svg │ │ │ ├── chevron_right.svg │ │ │ ├── collapse.svg │ │ │ ├── config.svg │ │ │ ├── cross.svg │ │ │ ├── expand.svg │ │ │ ├── eye_closed.svg │ │ │ ├── eye_opened.svg │ │ │ ├── fastforward.svg │ │ │ ├── forward.svg │ │ │ ├── fullscreen.svg │ │ │ ├── fullscreen_exit.svg │ │ │ ├── index.ts │ │ │ ├── info.svg │ │ │ ├── interpolation_add.svg │ │ │ ├── interpolation_disabled.svg │ │ │ ├── interpolation_remove.svg │ │ │ ├── keypoint_add.svg │ │ │ ├── keypoint_delete.svg │ │ │ ├── keypoint_disabled.svg │ │ │ ├── next_step.svg │ │ │ ├── pause.svg │ │ │ ├── play.svg │ │ │ ├── prev_step.svg │ │ │ ├── replay.svg │ │ │ ├── rewind.svg │ │ │ ├── sound.svg │ │ │ └── sound_muted.svg │ │ ├── tools │ │ │ ├── brightness-tool.svg │ │ │ ├── brush-tool-smart.svg │ │ │ ├── brush-tool.svg │ │ │ ├── circle-tool-smart.svg │ │ │ ├── circle-tool.svg │ │ │ ├── contrast-tool.svg │ │ │ ├── eraser-tool.svg │ │ │ ├── expand-tool.svg │ │ │ ├── hand-tool.svg │ │ │ ├── keypoints-tool-smart.svg │ │ │ ├── keypoints-tool.svg │ │ │ ├── magic-wand-tool.svg │ │ │ ├── magnify-tool.svg │ │ │ ├── minify-tool.svg │ │ │ ├── move-tool.svg │ │ │ ├── polygon-tool-smart.svg │ │ │ ├── polygon-tool.svg │ │ │ ├── rectangle-3point-tool-smart.svg │ │ │ ├── rectangle-3point-tool.svg │ │ │ ├── rectangle-tool-smart.svg │ │ │ ├── rectangle-tool.svg │ │ │ ├── rotate-left-tool.svg │ │ │ └── rotate-right-tool.svg │ │ ├── trash-rect.svg │ │ ├── trash.svg │ │ ├── tree │ │ │ ├── arrow.svg │ │ │ └── index.ts │ │ ├── undo.svg │ │ ├── unlocked.svg │ │ ├── view-all.svg │ │ ├── visible.svg │ │ ├── volume-full.svg │ │ ├── volume-half.svg │ │ ├── volume-mute.svg │ │ └── warning.svg │ └── styles │ │ ├── _functions.scss │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── global.scss ├── common │ ├── Button │ │ ├── Button.styl │ │ └── Button.tsx │ ├── Dropdown │ │ ├── Dropdown.styl │ │ ├── Dropdown.ts │ │ ├── DropdownComponent.tsx │ │ ├── DropdownContext.tsx │ │ └── DropdownTrigger.tsx │ ├── Icon │ │ ├── Icon.js │ │ └── Icon.styl │ ├── Input │ │ ├── Input.js │ │ └── Input.styl │ ├── Label │ │ ├── Label.js │ │ └── Label.styl │ ├── Menu │ │ ├── Menu.js │ │ ├── Menu.styl │ │ ├── MenuContext.js │ │ └── MenuItem.js │ ├── Modal │ │ ├── Modal.js │ │ ├── Modal.styl │ │ └── ModalPopup.js │ ├── Oneof │ │ └── Oneof.js │ ├── Pagination │ │ ├── Pagination.styl │ │ └── Pagination.tsx │ ├── RadioGroup │ │ ├── RadioGroup.js │ │ └── RadioGroup.styl │ ├── Range │ │ ├── Range.styl │ │ └── Range.tsx │ ├── Select │ │ ├── Select.styl │ │ └── Select.tsx │ ├── Space │ │ ├── Space.styl │ │ └── Space.tsx │ ├── Tag │ │ ├── Tag.styl │ │ └── Tag.tsx │ ├── TextArea │ │ ├── TextArea.styl │ │ └── TextArea.tsx │ ├── TimeAgo │ │ └── TimeAgo.tsx │ ├── Toggle │ │ ├── Toggle.js │ │ └── Toggle.styl │ ├── Tooltip │ │ ├── Tooltip.styl │ │ └── Tooltip.tsx │ ├── Userpic │ │ ├── Userpic.styl │ │ └── Userpic.tsx │ └── Utils │ │ ├── mergeRefs.ts │ │ ├── useMounted.ts │ │ ├── useValueTracker.ts │ │ └── useWindowSize.ts ├── components │ ├── AnnotationTab │ │ ├── AnnotationTab.js │ │ ├── CommentsSection.styl │ │ ├── DynamicPreannotationsControl.js │ │ ├── DynamicPreannotationsControl.styl │ │ ├── DynamicPreannotationsToggle.js │ │ └── DynamicPreannotationsToggle.styl │ ├── AnnotationTabs │ │ ├── AnnotationTabs.js │ │ └── AnnotationTabs.styl │ ├── Annotations │ │ ├── Annotations.js │ │ └── Annotations.module.scss │ ├── AnnotationsCarousel │ │ ├── AnnotationButton.styl │ │ ├── AnnotationButton.tsx │ │ ├── AnnotationsCarousel.styl │ │ ├── AnnotationsCarousel.tsx │ │ └── __tests__ │ │ │ ├── AnnotationButton.test.tsx │ │ │ ├── AnnotationsCarousel.test.tsx │ │ │ └── sampleData.js │ ├── App │ │ ├── Annotation.js │ │ ├── App.js │ │ ├── App.styl │ │ ├── Grid.js │ │ ├── Grid.module.scss │ │ └── Grid.styl │ ├── BottomBar │ │ ├── Actions.js │ │ ├── BottomBar.js │ │ ├── BottomBar.styl │ │ ├── Controls.js │ │ ├── Controls.styl │ │ ├── CurrentTask.js │ │ ├── CurrentTask.styl │ │ ├── HistoryActions.js │ │ └── HistoryActions.styl │ ├── Choice │ │ ├── Choice.js │ │ └── Choice.module.scss │ ├── Comments │ │ ├── CommentForm.styl │ │ ├── CommentForm.tsx │ │ ├── CommentFormBase.tsx │ │ ├── CommentItem.styl │ │ ├── CommentItem.tsx │ │ ├── Comments.styl │ │ ├── Comments.tsx │ │ └── CommentsList.tsx │ ├── Controls │ │ ├── Controls.js │ │ └── Controls.module.scss │ ├── CurrentEntity │ │ ├── AnnotationHistory.styl │ │ ├── AnnotationHistory.tsx │ │ ├── Controls.js │ │ ├── Controls.styl │ │ ├── CurrentEntity.js │ │ ├── CurrentEntity.styl │ │ ├── GroundTruth.js │ │ ├── GroundTruth.styl │ │ ├── HistoryActions.js │ │ └── HistoryActions.styl │ ├── Debug.js │ ├── Dialog │ │ ├── Dialog.js │ │ └── Dialog.module.scss │ ├── DraftPanel │ │ ├── DraftPanel.js │ │ └── DraftPanel.styl │ ├── Entities │ │ ├── Entities.js │ │ ├── Entities.module.scss │ │ ├── Entities.scss │ │ ├── Entities.styl │ │ ├── GroupMenu.js │ │ ├── LabelItem.js │ │ ├── LabelItem.styl │ │ ├── LabelList.js │ │ ├── RegionItem.js │ │ ├── RegionItem.styl │ │ ├── RegionTree.js │ │ ├── SortMenu.js │ │ └── SortMenu.styl │ ├── Entity │ │ ├── Entity.js │ │ ├── Entity.module.scss │ │ └── Entity.styl │ ├── ErrorMessage │ │ ├── ErrorMessage.js │ │ └── ErrorMessage.module.scss │ ├── Filter │ │ ├── Filter.styl │ │ ├── Filter.tsx │ │ ├── FilterDropdown.tsx │ │ ├── FilterInput.tsx │ │ ├── FilterInterfaces.tsx │ │ ├── FilterRow.styl │ │ ├── FilterRow.tsx │ │ ├── __tests__ │ │ │ ├── Filter.test.tsx │ │ │ ├── FilterRow.test.tsx │ │ │ └── filter-utils.test.tsx │ │ ├── filter-util.ts │ │ └── types │ │ │ ├── Boolean.js │ │ │ ├── Common.js │ │ │ ├── Number.js │ │ │ ├── String.js │ │ │ └── index.js │ ├── Hint │ │ ├── Hint.module.scss │ │ ├── Hint.styl │ │ ├── Hint.test.js │ │ ├── Hint.tsx │ │ └── __snapshots__ │ │ │ └── Hint.test.js.snap │ ├── HtxTextBox │ │ ├── HtxTextBox.js │ │ └── HtxTextBox.module.scss │ ├── ImageGrid │ │ └── ImageGrid.js │ ├── ImageTransformer │ │ ├── ImageTransformer.js │ │ ├── LSTransformer.js │ │ └── LSTransformerOld.js │ ├── ImageView │ │ ├── Image.js │ │ ├── Image.styl │ │ ├── ImageView.js │ │ ├── ImageView.module.scss │ │ ├── ImageViewContext.ts │ │ ├── LabelOnRegion.js │ │ └── SuggestionControls.js │ ├── Infomodal │ │ └── Infomodal.js │ ├── InstructionsModal │ │ ├── InstructionsModal.tsx │ │ └── __tests__ │ │ │ └── InstructionsModal.test.tsx │ ├── Label │ │ ├── Label.js │ │ └── Label.styl │ ├── NewTaxonomy │ │ ├── NewTaxonomy.styl │ │ ├── NewTaxonomy.tsx │ │ ├── TaxonomySearch.styl │ │ └── TaxonomySearch.tsx │ ├── Node │ │ ├── Node.styl │ │ ├── Node.tsx │ │ └── NodeView.ts │ ├── Panel │ │ ├── Panel.js │ │ └── Panel.module.scss │ ├── Ranker │ │ ├── Column.tsx │ │ ├── Item.tsx │ │ ├── Ranker.module.scss │ │ ├── Ranker.tsx │ │ ├── StrictModeDroppable.tsx │ │ └── createData.ts │ ├── Relations │ │ ├── Relations.js │ │ ├── Relations.module.scss │ │ └── Relations.styl │ ├── RelationsOverlay │ │ ├── BoundingBox.js │ │ ├── Geometry.js │ │ ├── NodesConnector.js │ │ ├── RelationShape.js │ │ ├── RelationsOverlay.js │ │ └── watchers │ │ │ ├── DOMWatcher.js │ │ │ ├── EllipseWatcher.js │ │ │ ├── PolygonWatcher.js │ │ │ ├── PropertyWatcher.js │ │ │ └── index.js │ ├── Segment │ │ ├── Segment.js │ │ └── Segment.module.scss │ ├── Settings │ │ ├── Settings.js │ │ ├── Settings.styl │ │ └── TagSettings │ │ │ ├── SettingsRenderer.tsx │ │ │ ├── Types.ts │ │ │ ├── VideoSettings.tsx │ │ │ └── index.ts │ ├── SidePanels │ │ ├── Components │ │ │ ├── LockButton.tsx │ │ │ └── RegionControlButton.tsx │ │ ├── DetailsPanel │ │ │ ├── DetailsPanel.styl │ │ │ ├── DetailsPanel.tsx │ │ │ ├── RegionDetails.styl │ │ │ ├── RegionDetails.tsx │ │ │ ├── RegionEditor.styl │ │ │ ├── RegionEditor.tsx │ │ │ ├── RegionItem.tsx │ │ │ ├── RegionLabels.tsx │ │ │ ├── Relations.styl │ │ │ └── Relations.tsx │ │ ├── OutlinerPanel │ │ │ ├── OutlinerPanel.styl │ │ │ ├── OutlinerPanel.tsx │ │ │ ├── OutlinerTree.tsx │ │ │ ├── TreeView.styl │ │ │ ├── ViewControls.styl │ │ │ └── ViewControls.tsx │ │ ├── PanelBase.styl │ │ ├── PanelBase.tsx │ │ ├── SidePanels.styl │ │ ├── SidePanels.tsx │ │ ├── SidePanelsContext.ts │ │ ├── TabPanels │ │ │ ├── PanelTabsBase.styl │ │ │ ├── PanelTabsBase.tsx │ │ │ ├── SideTabsPanels.tsx │ │ │ ├── Tabs.styl │ │ │ ├── Tabs.tsx │ │ │ ├── __tests__ │ │ │ │ └── utils.test.tsx │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── __tests__ │ │ │ └── PanelBase.test.tsx │ │ └── constants.ts │ ├── SidebarTabs │ │ ├── SidebarTabs.js │ │ └── SidebarTabs.styl │ ├── SimpleBadge │ │ ├── SimpleBadge.js │ │ └── SimpleBadge.module.scss │ ├── Tags │ │ └── Object.tsx │ ├── Taxonomy │ │ ├── Taxonomy.module.scss │ │ └── Taxonomy.tsx │ ├── TextHighlight │ │ ├── EmojiNode.js │ │ ├── Node.js │ │ ├── Range.js │ │ ├── TextHighlight.js │ │ ├── TextHighlight.module.scss │ │ └── UrlNode.js │ ├── TextNode │ │ └── TextNode.js │ ├── TimeDurationControl │ │ ├── TimeBox.styl │ │ ├── TimeBox.tsx │ │ ├── TimeDurationControl.styl │ │ └── TimeDurationControl.tsx │ ├── Timeline │ │ ├── Context.ts │ │ ├── Controls.styl │ │ ├── Controls.tsx │ │ ├── Controls │ │ │ ├── AudioControl.styl │ │ │ ├── AudioControl.tsx │ │ │ ├── ConfigControl.styl │ │ │ ├── ConfigControl.tsx │ │ │ ├── Info.styl │ │ │ ├── Info.tsx │ │ │ ├── Slider.styl │ │ │ └── Slider.tsx │ │ ├── Plugins │ │ │ └── Timeline.tsx │ │ ├── Seeker.styl │ │ ├── Seeker.tsx │ │ ├── SideControls │ │ │ ├── FramesControl.styl │ │ │ ├── FramesControl.tsx │ │ │ ├── VolumeControl.tsx │ │ │ └── index.ts │ │ ├── Timeline.styl │ │ ├── Timeline.tsx │ │ ├── Types.ts │ │ └── Views │ │ │ ├── Frames │ │ │ ├── Controls.tsx │ │ │ ├── Frames.styl │ │ │ ├── Frames.tsx │ │ │ ├── Keypoints.styl │ │ │ ├── Keypoints.tsx │ │ │ ├── Minimap.styl │ │ │ ├── Minimap.tsx │ │ │ ├── Utils.ts │ │ │ └── index.ts │ │ │ ├── Wave │ │ │ ├── Utils.ts │ │ │ ├── Wave.styl │ │ │ ├── Wave.tsx │ │ │ └── index.ts │ │ │ └── index.ts │ ├── Toolbar │ │ ├── FlyoutMenu.js │ │ ├── FlyoutMenu.styl │ │ ├── Tool.js │ │ ├── Tool.styl │ │ ├── Toolbar.js │ │ ├── Toolbar.styl │ │ └── ToolbarContext.js │ ├── Tools │ │ ├── Basic.js │ │ ├── Slider.js │ │ ├── SliderDropDown.js │ │ └── Styles.module.scss │ ├── TopBar │ │ ├── Actions.js │ │ ├── Annotations.js │ │ ├── Annotations.styl │ │ ├── Controls.js │ │ ├── Controls.styl │ │ ├── CurrentTask.js │ │ ├── CurrentTask.styl │ │ ├── HistoryActions.js │ │ ├── HistoryActions.styl │ │ ├── TopBar.js │ │ └── TopBar.styl │ ├── TreeStructure │ │ └── TreeStructure.tsx │ ├── TreeValidation │ │ └── TreeValidation.js │ ├── VideoCanvas │ │ ├── VideoCanvas.styl │ │ ├── VideoCanvas.tsx │ │ ├── VideoConstants.ts │ │ ├── VirtualCanvas.tsx │ │ ├── VirtualVideo.tsx │ │ └── __tests__ │ │ │ └── VirtualVideo.test.tsx │ └── Waveform │ │ ├── Waveform.js │ │ └── Waveform.module.scss ├── configureStore.js ├── core │ ├── Constants.ts │ ├── CustomTypes.ts │ ├── DataValidator │ │ ├── ConfigValidator.js │ │ └── index.js │ ├── External.js │ ├── Helpers.ts │ ├── Hotkey.ts │ ├── Registry.ts │ ├── TimeTraveller.js │ ├── Tree.tsx │ ├── Types.js │ ├── __tests__ │ │ ├── ConfigValidator.test.js │ │ └── repeater.test.js │ ├── feature-flags │ │ ├── flags.json │ │ └── index.ts │ └── settings │ │ ├── editorsettings.js │ │ ├── keymap.json │ │ ├── types.ts │ │ └── videosettings.ts ├── defaultOptions.js ├── env │ ├── development.js │ └── production.js ├── hooks │ ├── useDrag.ts │ ├── useFullscreen.ts │ ├── useHotkey.ts │ ├── useLocalStorageState.ts │ ├── useMedia.ts │ ├── useMemoizedHandlers.ts │ ├── useRegionColor.ts │ ├── useRegionsCopyPaste.ts │ └── useToggle.ts ├── index.js ├── lib │ └── AudioUltra │ │ ├── Common │ │ ├── Cacheable.ts │ │ ├── Color.ts │ │ ├── Destructable.ts │ │ ├── Events.ts │ │ ├── Style.ts │ │ ├── Utils.ts │ │ └── Worker │ │ │ └── index.ts │ │ ├── Controls │ │ ├── Html5Player.ts │ │ ├── Player.ts │ │ └── WebAudioPlayer.ts │ │ ├── Cursor │ │ └── Cursor.ts │ │ ├── Media │ │ ├── AudioDecoder.ts │ │ ├── AudioDecoderPool.ts │ │ ├── BaseAudioDecoder.ts │ │ ├── MediaLoader.ts │ │ ├── SplitChannel.ts │ │ ├── SplitChannelWorker.ts │ │ ├── WaveformAudio.ts │ │ └── WebAudioDecoder.ts │ │ ├── Regions │ │ ├── Region.ts │ │ ├── Regions.ts │ │ └── Segment.ts │ │ ├── Timeline │ │ └── Timeline.ts │ │ ├── Tooltip │ │ └── Tooltip.ts │ │ ├── Visual │ │ ├── Layer.ts │ │ ├── LayerGroup.ts │ │ ├── Loader.ts │ │ ├── PlayHead.ts │ │ └── Visualizer.ts │ │ ├── Waveform.ts │ │ ├── index.ts │ │ └── react │ │ └── index.ts ├── mixins │ ├── AnnotationMixin.js │ ├── AreaMixin.js │ ├── DrawingTool.js │ ├── DynamicChildrenMixin.js │ ├── HighlightMixin.js │ ├── IsReadyMixin.js │ ├── KonvaRegion.js │ ├── LabelMixin.js │ ├── LeadTime.ts │ ├── Normalization.ts │ ├── PerItem.js │ ├── PerRegion.js │ ├── PerRegionModes.ts │ ├── PersistentState.js │ ├── ProcessAttrs.js │ ├── ReadOnlyMixin.js │ ├── Regions.js │ ├── Required.js │ ├── SelectedChoiceMixin.js │ ├── SelectedModel.js │ ├── SeparatedControlMixin.js │ ├── SharedChoiceStore │ │ ├── extender.js │ │ ├── mixin.js │ │ └── model.js │ ├── SpanText.js │ ├── Syncable.ts │ ├── TagParentMixin.js │ ├── Tool.js │ ├── ToolManagerMixin.js │ └── Visibility.js ├── react-app-env.d.ts ├── regions │ ├── AliveRegion.tsx │ ├── Area.js │ ├── AudioRegion.js │ ├── AudioRegion │ │ ├── AudioRegionModel.js │ │ └── AudioUltraRegionModel.js │ ├── BrushRegion.js │ ├── EditableRegion.js │ ├── EllipseRegion.js │ ├── HyperTextRegion.js │ ├── HyperTextRegion │ │ └── HyperTextRegion.module.scss │ ├── ImageRegion.js │ ├── KeyPointRegion.js │ ├── ParagraphsRegion.js │ ├── PolygonPoint.js │ ├── PolygonRegion.js │ ├── RectRegion.js │ ├── RegionWrapper.js │ ├── Result.js │ ├── RichTextRegion.js │ ├── Test.js │ ├── TextAreaRegion.js │ ├── TextAreaRegion │ │ └── TextAreaRegion.module.scss │ ├── TextRegion.js │ ├── TextRegion │ │ └── TextRegion.module.scss │ ├── TimeSeriesRegion.js │ ├── VideoRectangleRegion.js │ ├── VideoRegion.js │ └── index.js ├── serviceWorker.js ├── setupTests.js ├── stores │ ├── Annotation │ │ ├── Annotation.js │ │ ├── HistoryItem.js │ │ └── store.js │ ├── AppStore.js │ ├── Comment │ │ ├── Comment.js │ │ └── CommentStore.js │ ├── ProjectStore.js │ ├── RegionStore.js │ ├── RelationStore.js │ ├── SettingsStore.js │ ├── TaskStore.js │ ├── UserLabels.ts │ ├── UserStore.js │ └── __tests__ │ │ └── TaskStore.test.js ├── styles │ └── global.module.scss ├── tags │ ├── TagBase.js │ ├── control │ │ ├── Base.js │ │ ├── Brush.js │ │ ├── BrushLabels.js │ │ ├── Choice.js │ │ ├── Choice │ │ │ └── Choice.styl │ │ ├── Choices.js │ │ ├── Choices │ │ │ └── Choises.styl │ │ ├── ClassificationBase.js │ │ ├── DateTime.js │ │ ├── Ellipse.js │ │ ├── EllipseLabels.js │ │ ├── HyperTextLabels.js │ │ ├── KeyPoint.js │ │ ├── KeyPointLabels.js │ │ ├── Label.js │ │ ├── Labels │ │ │ ├── Labels.js │ │ │ └── Labels.styl │ │ ├── MagicWand.js │ │ ├── Number.js │ │ ├── Pairwise.js │ │ ├── ParagraphLabels.js │ │ ├── Polygon.js │ │ ├── PolygonLabels.js │ │ ├── Ranker.js │ │ ├── Rating.js │ │ ├── Rectangle.js │ │ ├── RectangleLabels.js │ │ ├── Relation.js │ │ ├── Relations.js │ │ ├── Shortcut.js │ │ ├── Taxonomy │ │ │ ├── Taxonomy.js │ │ │ └── Taxonomy.styl │ │ ├── TextArea │ │ │ ├── TextArea.js │ │ │ ├── TextArea.styl │ │ │ └── TextAreaRegionView.js │ │ ├── TimeSeriesLabels.js │ │ ├── VideoRectangle.js │ │ ├── __tests__ │ │ │ └── Ranker.test.ts │ │ └── index.js │ ├── object │ │ ├── Audio.js │ │ ├── Audio │ │ │ └── Controls.js │ │ ├── AudioNext │ │ │ ├── constants.ts │ │ │ ├── index.js │ │ │ ├── model.js │ │ │ ├── view.tsx │ │ │ └── view_old.js │ │ ├── AudioPlus │ │ │ └── AudioPlus.module.scss │ │ ├── AudioUltra │ │ │ ├── constants.ts │ │ │ ├── model.js │ │ │ ├── view.styl │ │ │ └── view.tsx │ │ ├── Base.js │ │ ├── HyperText.js │ │ ├── Image │ │ │ ├── DrawingRegion.js │ │ │ ├── Image.js │ │ │ ├── ImageEntity.js │ │ │ ├── ImageEntityMixin.js │ │ │ ├── ImageSelection.js │ │ │ ├── ImageSelectionPoint.js │ │ │ └── index.js │ │ ├── List.js │ │ ├── MultiItemObjectBase.js │ │ ├── PagedView.js │ │ ├── Paragraphs │ │ │ ├── AuthorFilter.js │ │ │ ├── HtxParagraphs.js │ │ │ ├── Paragraphs.module.scss │ │ │ ├── Phrases.js │ │ │ ├── __tests__ │ │ │ │ ├── Phrases.test.tsx │ │ │ │ └── model.test.ts │ │ │ ├── index.js │ │ │ └── model.js │ │ ├── RichText │ │ │ ├── RichText.styl │ │ │ ├── domManager.ts │ │ │ ├── index.js │ │ │ ├── model.js │ │ │ └── view.js │ │ ├── Table.js │ │ ├── Text.js │ │ ├── Text │ │ │ └── Text.module.scss │ │ ├── TimeSeries.js │ │ ├── TimeSeries │ │ │ ├── Channel.js │ │ │ ├── helpers.js │ │ │ └── symbols.js │ │ ├── Video │ │ │ ├── HtxVideo.js │ │ │ ├── Rectangle.tsx │ │ │ ├── TransformTools.ts │ │ │ ├── Video.js │ │ │ ├── Video.styl │ │ │ ├── VideoRegions.js │ │ │ ├── index.js │ │ │ ├── tools.ts │ │ │ └── types.ts │ │ └── index.js │ └── visual │ │ ├── Collapse.js │ │ ├── Dialog.js │ │ ├── Filter.js │ │ ├── Header.js │ │ ├── Repeater.js │ │ ├── Style.js │ │ ├── View.js │ │ ├── __tests__ │ │ └── Header.test.jsx │ │ └── index.js ├── themes │ └── default │ │ ├── colors.styl │ │ ├── scrollbar.styl │ │ └── tools │ │ ├── scrollbar.styl │ │ └── waiting.styl ├── tools │ ├── Base.js │ ├── Brightness.js │ ├── Brush.js │ ├── Contrast.js │ ├── Ellipse.js │ ├── Erase.js │ ├── KeyPoint.js │ ├── LiveWire.js │ ├── MagicWand.js │ ├── Manager.js │ ├── Polygon.js │ ├── Rect.js │ ├── Rotate.js │ ├── Selection.js │ ├── Tools.module.scss │ ├── Zoom.js │ └── index.js └── utils │ ├── FileLoader.ts │ ├── InputMask.ts │ ├── __tests__ │ ├── colors.test.js │ ├── data.test.js │ ├── date.test.js │ ├── debounce.test.js │ ├── html.test.ts │ ├── props.test.js │ ├── styles.test.js │ ├── unique.test.js │ └── utilities.test.js │ ├── bboxCoords.js │ ├── bem.ts │ ├── canvas.js │ ├── colors.js │ ├── data.js │ ├── date.js │ ├── debounce.js │ ├── dom.ts │ ├── events.ts │ ├── feature-flags.ts │ ├── hooks.js │ ├── html.js │ ├── image.js │ ├── index.js │ ├── livewire.js │ ├── magic-wand.js │ ├── messages.js │ ├── namedColors.ts │ ├── props.ts │ ├── reactCleaner.js │ ├── resize-observer.ts │ ├── scissors.js │ ├── selection-tools.js │ ├── styles.js │ ├── transition.ts │ ├── unique.ts │ └── utilities.ts ├── tests └── functional │ ├── .gitignore │ ├── cypress.config.js │ ├── cypress │ ├── parallel-weights.json │ └── support │ │ └── e2e.ts │ ├── data │ ├── audio │ │ └── audio_paragraphs.ts │ ├── auto_annotation │ │ └── auto_accept.ts │ ├── control_tags │ │ ├── choice.ts │ │ ├── number.ts │ │ ├── per-item.ts │ │ ├── taxonomy.ts │ │ └── textarea.ts │ ├── core │ │ └── hotkeys.ts │ ├── image_segmentation │ │ ├── crosshair.ts │ │ ├── image_position.ts │ │ ├── layers.ts │ │ └── tools │ │ │ ├── brush.ts │ │ │ ├── magic-wand.ts │ │ │ ├── rect.ts │ │ │ ├── rect3point.ts │ │ │ ├── selection-tool.ts │ │ │ └── zoom.ts │ ├── lmm │ │ └── accept_suggestions.ts │ ├── outliner │ │ └── hide-all.ts │ ├── relations │ │ └── basic.ts │ └── video_segmentation │ │ └── regions.ts │ ├── feature-flags.ts │ ├── multi-reporter-config.json │ ├── output │ └── snapshots │ │ ├── Audio -- Renders audio with merged channels by default.snap.png │ │ ├── Audio -- Renders separate audio channels with splitchannels=true.snap.png │ │ ├── Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png │ │ ├── Audio Paragraphs Sync -- Highlights the correct Audio segment whenever it is played or seeked.snap.png │ │ └── audio │ │ └── audio_paragraphs.cy.ts │ │ ├── Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png │ │ ├── HighlightAfterFinishedPlayback.snap.png │ │ └── HighlightOnFirstSeek.snap.png │ ├── package.json │ ├── specs │ ├── audio │ │ ├── audio.cy.ts │ │ └── audio_paragraphs.cy.ts │ ├── auto_annotation │ │ └── auto_accept.cy.ts │ ├── control_tags │ │ ├── choice.cy.ts │ │ ├── classification │ │ │ └── per-item.cy.ts │ │ ├── number.cy.ts │ │ ├── taxonomy.cy.ts │ │ └── textarea.cy.ts │ ├── core │ │ ├── feature_flags.cy.ts │ │ ├── hotkeys.cy.ts │ │ └── label_studio.cy.ts │ ├── image_segmentation │ │ ├── basic.cy.ts │ │ ├── crosshair.cy.ts │ │ ├── image_position.cy.ts │ │ ├── image_regions.cy.ts │ │ ├── layers.cy.ts │ │ └── tools │ │ │ ├── brush.cy.ts │ │ │ ├── magic-wand.cy.ts │ │ │ ├── rect.cy.ts │ │ │ ├── rect3point.cy.ts │ │ │ ├── selection-tool.cy.ts │ │ │ └── zoom.cy.ts │ ├── llm │ │ └── accept_suggestions.cy.ts │ ├── object_tags │ │ └── text.cy.ts │ ├── outliner │ │ ├── filter.cy.ts │ │ ├── hide-all.cy.ts │ │ └── region_tree.cy.ts │ ├── relations │ │ ├── basic.cy.ts │ │ └── image_rectangle_regions.cy.ts │ └── video_segmentation │ │ └── regions.cy.ts │ ├── tsconfig.json │ └── yarn.lock ├── tsconfig.jest.json ├── tsconfig.json ├── types ├── Global.d.ts ├── Keymap.d.ts ├── React.d.ts ├── Regions.d.ts └── shallow-equal.d.ts ├── webpack.config-builder.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-react", { 4 | "runtime": "automatic" 5 | }], 6 | "@babel/preset-typescript", 7 | ["@babel/preset-env", { 8 | "targets": { 9 | "browsers": ["last 2 Chrome versions"], 10 | "node": "current" 11 | } 12 | }] 13 | ], 14 | "plugins": [ 15 | ["babel-plugin-import", { "libraryName": "antd" }], 16 | "@babel/plugin-proposal-class-properties", 17 | "@babel/plugin-proposal-private-methods", 18 | "@babel/plugin-proposal-optional-chaining", 19 | "@babel/plugin-proposal-nullish-coalescing-operator" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.env.defaults: -------------------------------------------------------------------------------- 1 | CSS_PREFIX=lsf- 2 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | CSS_PREFIX=[prefix for your css classes with trailing dash] 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.* 2 | *.styl 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | process: true, 4 | module: true, 5 | require: true, 6 | DISABLE_DEFAULT_LSF_INIT: true, 7 | __dirname: true, 8 | }, 9 | extends: ['plugin:@heartexlabs/frontend/recommended'], 10 | }; 11 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github/ @farioas 2 | -------------------------------------------------------------------------------- /.github/pr-title-checker-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "LABEL": { 3 | "name": "title needs formatting", 4 | "color": "EEEEEE" 5 | }, 6 | "CHECKS": { 7 | "prefixes": [ 8 | "fix: ", 9 | "feat: ", 10 | "docs: ", 11 | "chore: ", 12 | "ci: ", 13 | "perf: ", 14 | "refactor: ", 15 | "style: ", 16 | "test: " 17 | ], 18 | "ignoreLabels": [ 19 | "skip-changelog", 20 | "skip-ci" 21 | ] 22 | }, 23 | "MESSAGES": { 24 | "success": "PR title is valid", 25 | "failure": "PR title is invalid", 26 | "notice": "Please read the doc: [Release versioning strategy](https://heartex.atlassian.net/l/c/brYSL9qf)" 27 | } 28 | } -------------------------------------------------------------------------------- /.github/workflows/cancel_cicd_pipeline.yml: -------------------------------------------------------------------------------- 1 | name: "Cancel PR CI/CD pipeline" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - closed 7 | - converted_to_draft 8 | - locked 9 | branches: 10 | - master 11 | - 'lse-release/**' 12 | - 'ls-release/**' 13 | 14 | concurrency: 15 | group: CI/CD Pipeline-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.ref_name }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | cancel: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: hmarr/debug-action@v3.0.0 23 | - run: echo CI/CD Pipeline-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.ref_name }} 24 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | //npm.pkg.github.com/:_authToken=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 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | build/ 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "jsxBracketSameLine": false, 6 | "bracketSpacing": true, 7 | "semi": true 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | branches: 4 | only: 5 | - master 6 | - /^release\/.+$/ 7 | - /^major\/.+$/ 8 | 9 | node_js: 10 | - node 11 | 12 | cache: 13 | directories: 14 | - node_modules 15 | 16 | script: 17 | - npm test 18 | - unset CI 19 | - npm run build-bundle 20 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Label Studio 2 | 3 | First off, thanks for taking the time to contribute! 4 | 5 | This part of the documentation gives you a basic overview of how to help with the development of our label studio. 6 | 7 | ## Reporting Bugs 8 | 9 | ## Pull Requests 10 | 11 | ## Code of conduct 12 | 13 | We value input from each member of the community, however we urge you to abide by [code of conduct](https://github.com/heartexlabs/label-studio/blob/master/CODE_OF_CONDUCT.md). 14 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | fixes: 2 | - "/home/runner/work/label-studio-frontend/label-studio-frontend/::" 3 | -------------------------------------------------------------------------------- /dev-server.js: -------------------------------------------------------------------------------- 1 | const WebpackDevServer = require("webpack-dev-server"); 2 | const webpack = require("webpack"); 3 | 4 | const config = require("./webpack.config-builder")({ 5 | withDevServer: true, 6 | }); 7 | 8 | config.entry.main.unshift( 9 | `webpack-dev-server/client?http://localhost:${config.devServer.port}/`, 10 | `webpack/hot/dev-server`, 11 | ); 12 | 13 | const compiler = webpack(config); 14 | const server = new WebpackDevServer(config.devServer, compiler); 15 | 16 | server.startCallback(() => { 17 | console.log(`dev server listening on port ${config.devServer.port}`); 18 | }); 19 | -------------------------------------------------------------------------------- /e2e/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | 'codeceptjs', 4 | ], 5 | globals: { 6 | Htx: true, 7 | Feature: true, 8 | Scenario: true, 9 | Data: true, 10 | DataTable: true, 11 | Before: true, 12 | locate: true, 13 | actor: true, 14 | inject: true, 15 | session: true, 16 | pause: true, 17 | within: true, 18 | }, 19 | rules: { 20 | 'codeceptjs/no-exclusive-tests': 'error', 21 | 'codeceptjs/no-skipped-tests': 'warn', 22 | 'codeceptjs/no-pause-in-scenario': 'error', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /e2e/examples/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Utils: require('./utils'), 3 | examples: [ 4 | require('./audio-regions'), 5 | require('./image-bboxes'), 6 | require('./image-ellipses'), 7 | require('./image-keypoints'), 8 | require('./image-polygons'), 9 | require('./ner-url'), 10 | require('./nested'), 11 | require('./text-html'), 12 | require('./text-paragraphs'), 13 | require('./timeseries-url-indexed'), 14 | ], 15 | }; 16 | -------------------------------------------------------------------------------- /e2e/fragments/AtTableView.js: -------------------------------------------------------------------------------- 1 | const { I } = inject(); 2 | const assert = require('assert'); 3 | 4 | 5 | module.exports = { 6 | _tableRowSelectors: [ 7 | '.ant-table-wrapper .ant-table-tbody .ant-table-row:nth-child(1) .ant-table-cell:nth-child(1)', 8 | '.ant-table-wrapper .ant-table-tbody .ant-table-row:nth-child(2) .ant-table-cell:nth-child(1)', 9 | '.ant-table-wrapper .ant-table-tbody .ant-table-row:nth-child(3) .ant-table-cell:nth-child(1)', 10 | ], 11 | 12 | async seeKeys(value) { 13 | for (let i = 0; i < this._tableRowSelectors.length; i++) { 14 | const error = await I.grabTextFrom(this._tableRowSelectors[i]); 15 | 16 | assert.equal(error, value[i]); 17 | } 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /e2e/fragments/AtTextAreaView.js: -------------------------------------------------------------------------------- 1 | /* global inject */ 2 | const { I } = inject(); 3 | 4 | module.exports = { 5 | _inputSelector: '.ant-form-horizontal .ant-form-item .ant-form-item-control .ant-form-item-control-input .ant-form-item-control-input-content input', 6 | 7 | addNewTextTag(value) { 8 | I.fillField(this._inputSelector, value); 9 | I.pressKeyDown('Enter'); 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /e2e/fragments/Labels.js: -------------------------------------------------------------------------------- 1 | const { I } = inject(); 2 | 3 | module.exports = { 4 | selectWithHotkey(labelHotkey) { 5 | I.pressKey(`${labelHotkey}`); 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /e2e/fragments/Modals.js: -------------------------------------------------------------------------------- 1 | const { I } = inject(); 2 | 3 | module.exports = { 4 | seeWarning(text) { 5 | I.seeElement('.ant-modal'); 6 | I.see('Warning'); 7 | I.see(text); 8 | I.see('OK'); 9 | }, 10 | dontSeeWarning(text) { 11 | I.dontSeeElement('.ant-modal'); 12 | I.dontSee('Warning'); 13 | I.dontSee(text); 14 | }, 15 | closeWarning() { 16 | I.click('OK'); 17 | I.waitToHide('.ant-modal'); 18 | }, 19 | }; -------------------------------------------------------------------------------- /e2e/fragments/Regions.js: -------------------------------------------------------------------------------- 1 | const AtSidebar = require('./AtSidebar'); 2 | 3 | const { I } = inject(); 4 | 5 | module.exports = { 6 | unselectWithHotkey() { 7 | // wait is necessary for "Select region after creation" cases because 8 | // there's delay between region creation and ability to unselect a region 9 | I.wait(0.2); 10 | I.pressKey(['u']); 11 | AtSidebar.dontSeeSelectedRegion(); 12 | }, 13 | 14 | undoLastActionWithHotkey() { 15 | I.pressKey(['CommandOrControl', 'z']); 16 | }, 17 | 18 | redoLastAction() { 19 | I.pressKey(['CommandOrControl', 'Shift' ,'z']); 20 | }, 21 | 22 | //Image tools 23 | selectMoveTool() { 24 | I.pressKey('v'); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /e2e/fragments/Tools.js: -------------------------------------------------------------------------------- 1 | const { I } = inject(); 2 | 3 | module.exports = { 4 | async getElementPosition(elementSelector) { 5 | const pos = await I.executeScript((selector) => { 6 | const elem = document.querySelector(selector); 7 | const pos = elem?.getBoundingClientRect(); 8 | 9 | return pos ? { 10 | x: pos.x, 11 | y: pos.y, 12 | width: pos.width, 13 | height: pos.height, 14 | } : null; 15 | }, elementSelector); 16 | 17 | return pos; 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /e2e/helpers/DateTime.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple date formatter 3 | * @param {string} value date in ISO format 4 | * @param {string} format combinations of y, m, d (e.g. 'ymd') 5 | * @returns {string} formatted date 6 | */ 7 | export const formatDateValue = (value: string, format: string) => { 8 | const [y, m, d] = value.split('-'); 9 | let text = ''; 10 | 11 | for (const char of format) { 12 | if (char === 'y') text += y; 13 | if (char === 'm') text += m; 14 | if (char === 'd') text += d; 15 | } 16 | return text; 17 | }; 18 | -------------------------------------------------------------------------------- /e2e/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /e2e/steps.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-interface */ 2 | /// 3 | type steps_file = typeof import('./steps_file.js'); 4 | 5 | declare namespace CodeceptJS { 6 | interface SupportObject { I: CodeceptJS.I } 7 | interface CallbackOrder { [0]: CodeceptJS.I } 8 | interface Methods extends CodeceptJS.Puppeteer {} 9 | interface I extends ReturnType {} 10 | namespace Translation { 11 | interface Actions {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /e2e/tests/regression-tests/video-unmount.test.js: -------------------------------------------------------------------------------- 1 | Feature('Video unmount').tag('@regress'); 2 | 3 | Scenario('Reiniting Label Studio should not left unexpected null and video tags in DOM', async ({ I, LabelStudio }) => { 4 | I.amOnPage('/'); 5 | for (let i = 0; i < 60; i++) { 6 | LabelStudio.init({ 7 | config: ` 8 | 9 | `, 12 | data: { video: '/files/opossum_intro.webm' }, 13 | }); 14 | 15 | I.wait(i * i / 1000000); 16 | } 17 | I.dontSeeElementInDOM({ xpath: '//body/video[position()=2]' }); 18 | I.dontSee('null'); 19 | }).config({ waitForAction: 0 }); 20 | -------------------------------------------------------------------------------- /e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "ts-node": { 3 | "files": true 4 | }, 5 | "compilerOptions": { 6 | "target": "es2018", 7 | "lib": ["es2018", "DOM"], 8 | "esModuleInterop": true, 9 | "module": "commonjs", 10 | "strictNullChecks": true, 11 | "types": ["codeceptjs", "node"], 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /e2e/utils/asserts.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const Helpers = require('../tests/helpers'); 3 | 4 | function deepEqualWithTolerance(actual, expected, fractionDigits = 2, message) { 5 | assert.deepStrictEqual( 6 | Helpers.convertToFixed(actual, fractionDigits), 7 | Helpers.convertToFixed(expected, fractionDigits), 8 | message, 9 | ); 10 | } 11 | function notDeepEqualWithTolerance(actual, expected, fractionDigits = 2, message) { 12 | assert.notDeepStrictEqual( 13 | Helpers.convertToFixed(actual, fractionDigits), 14 | Helpers.convertToFixed(expected, fractionDigits), 15 | message, 16 | ); 17 | } 18 | 19 | module.exports = { 20 | deepEqualWithTolerance, 21 | notDeepEqualWithTolerance, 22 | }; 23 | -------------------------------------------------------------------------------- /examples/audio_classification/annotations/0.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": [ 3 | { 4 | "result": [ 5 | { 6 | "from_name": "label", 7 | "id": "wvDEy63Wgs", 8 | "to_name": "audio", 9 | "type": "choices", 10 | "value": { 11 | "choices": [ 12 | "Politics" 13 | ] 14 | } 15 | } 16 | ] 17 | } 18 | ], 19 | "data": { 20 | "url": "https://app.heartex.com/static/samples/game.wav" 21 | }, 22 | "id": 0, 23 | "task_path": "../examples/audio_classification/tasks.json" 24 | } 25 | -------------------------------------------------------------------------------- /examples/audio_classification/config.xml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /examples/audio_classification/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/0.json'; 4 | 5 | export const AudioClassification = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/audio_regions/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const AudioRegions = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/audio_video_paragraphs/START.md: -------------------------------------------------------------------------------- 1 | # Audio Video Paragraph 2 | 3 | # Install 4 | 5 | ## Linux & Ubuntu guide 6 | 7 | Install python and virtualenv 8 | 9 | ```bash 10 | # install python and virtualenv 11 | apt install python3.6 12 | pip3 install virtualenv 13 | 14 | # setup python virtual environment 15 | virtualenv -p python3 env3 16 | source env3/bin/activate 17 | 18 | # install requirements 19 | cd backend 20 | pip install -r requirements.txt 21 | ``` 22 | 23 | # Start 24 | 25 | Listen to the audio transcriptions, watch the video, and classify 26 | 27 | ```bash 28 | python server.py -c config.json -l ../examples/audio_video_paragraph/config.xml -i ../examples/audio_video_paragraph/tasks.json -o output 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/audio_video_paragraphs/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | -------------------------------------------------------------------------------- /examples/audio_video_paragraphs/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const AudioVideoParagraph = { config, tasks, annotation }; 6 | 7 | -------------------------------------------------------------------------------- /examples/classification_mixed/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ClassificationMixed = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/classification_mixed/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data": { 4 | "image": "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg" 5 | }, 6 | "predictions": [] 7 | }, 8 | { 9 | "data": { 10 | "image": "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/history-in-hd-e5eDHbmHprg-unsplash.jpg" 11 | } 12 | }, 13 | { 14 | "data": { 15 | "image": "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/soroush-karimi-crjPrExvShc-unsplash.jpg" 16 | } 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /examples/dialogue_analysis/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | -------------------------------------------------------------------------------- /examples/dialogue_analysis/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const DialogueAnalysis = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/html_document/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/html_document/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const HTMLDocument = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_bbox/START.md: -------------------------------------------------------------------------------- 1 | 2 | # Image object detection 3 | 4 | ![Image Object Detection](/images/screenshots/image_bbox.png "Image Object Detection") 5 | 6 | # Install 7 | 8 | ## Linux & Ubuntu guide 9 | 10 | Install python and virtualenv 11 | 12 | ```bash 13 | # install python and virtualenv 14 | apt install python3.6 15 | pip3 install virtualenv 16 | 17 | # setup python virtual environment 18 | virtualenv -p python3 env3 19 | source env3/bin/activate 20 | 21 | # install requirements 22 | cd backend 23 | pip install -r requirements.txt 24 | ``` 25 | 26 | # Start 27 | 28 | Image bounding box labeling 29 | 30 | ```bash 31 | python server.py -c config.json -l ../examples/image_bbox/config.xml -i ../examples/image_bbox/tasks.json -o output 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/image_bbox/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/image_bbox/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageBbox = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_bbox_large/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /examples/image_bbox_large/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageBboxLarge = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_classification/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/image_ellipses/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/image_ellipses/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageEllipselabels = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_keypoints/START.md: -------------------------------------------------------------------------------- 1 | 2 | # Image KeyPoints 3 | 4 | ![Image KeyPoints](/images/screenshots/image_keypoints.png "Image KeyPoints") 5 | 6 | # Install 7 | 8 | ## Linux & Ubuntu guide 9 | 10 | Install python and virtualenv 11 | 12 | ```bash 13 | # install python and virtualenv 14 | apt install python3.6 15 | pip3 install virtualenv 16 | 17 | # setup python virtual environment 18 | virtualenv -p python3 env3 19 | source env3/bin/activate 20 | 21 | # install requirements 22 | cd backend 23 | pip install -r requirements.txt 24 | ``` 25 | 26 | # Start 27 | 28 | Key points for the images 29 | 30 | ```bash 31 | python server.py -c config.json -l ../examples/image_keypoints/config.xml -i ../examples/image_keypoints/tasks.json -o output 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/image_keypoints/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/image_keypoints/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/0.json'; 4 | 5 | export const ImageKeyPoint = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_list/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/image_list/index.ts: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageList = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_list_large/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/image_list_large/index.ts: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageListLarge = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_list_perregion/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/image_list_perregion/index.ts: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageListPerregion = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_magic_wand/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/image_magic_wand/gcp_cors_config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "origin": ["*"], 4 | "responseHeader": ["*"], 5 | "method": ["GET"], 6 | "maxAgeSeconds": 3600 7 | } 8 | ] -------------------------------------------------------------------------------- /examples/image_magic_wand/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageMagicWand = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_multilabel/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/image_multilabel/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const ImageMultilabel = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/image_ocr/START.md: -------------------------------------------------------------------------------- 1 | 2 | # Image object detection 3 | 4 | ![Image Object Detection](/images/screenshots/image_bbox.png "Image Object Detection") 5 | 6 | # Install 7 | 8 | ## Linux & Ubuntu guide 9 | 10 | Install python and virtualenv 11 | 12 | ```bash 13 | # install python and virtualenv 14 | apt install python3.6 15 | pip3 install virtualenv 16 | 17 | # setup python virtual environment 18 | virtualenv -p python3 env3 19 | source env3/bin/activate 20 | 21 | # install requirements 22 | cd backend 23 | pip install -r requirements.txt 24 | ``` 25 | 26 | # Start 27 | 28 | Image bounding box labeling 29 | 30 | ```bash 31 | python server.py -c config.json -l ../examples/image_bbox/config.xml -i ../examples/image_bbox/tasks.json -o output 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/image_ocr/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 5 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /examples/transcribe_audio/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const TranscribeAudio = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/video/annotations/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": [ 3 | { 4 | "id": "1001", 5 | "result": [] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/video/config.xml: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 |
11 | -------------------------------------------------------------------------------- /examples/video/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const VideoClassification = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/video/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data": { 4 | "video": "https://app.heartex.ai/static/samples/opossum_snow.mp4" 5 | } 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /examples/video_audio/annotations/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "annotations": [ 3 | { 4 | "id": "1001", 5 | "result": [] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /examples/video_audio/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /examples/video_audio/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const VideoAudio = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/video_audio/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data": { 4 | "video": "https://app.heartex.ai/static/samples/opossum_snow.mp4" 5 | } 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /examples/video_bboxes/config.xml: -------------------------------------------------------------------------------- 1 | 2 |
Label the video:
3 |
12 | -------------------------------------------------------------------------------- /examples/video_bboxes/index.js: -------------------------------------------------------------------------------- 1 | import config from './config.xml'; 2 | import tasks from './tasks.json'; 3 | import annotation from './annotations/1.json'; 4 | 5 | export const VideoRectangles = { config, tasks, annotation }; 6 | -------------------------------------------------------------------------------- /examples/video_bboxes/tasks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "data": { 4 | "video": "https://app.heartex.ai/static/samples/opossum_snow.mp4" 5 | } 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /images/heartex_icon_opossum_green@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/heartex_icon_opossum_green@2x.png -------------------------------------------------------------------------------- /images/heartex_icon_opossum_orang@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/heartex_icon_opossum_orang@2x.png -------------------------------------------------------------------------------- /images/label-studio-examples.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/label-studio-examples.gif -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/logo.png -------------------------------------------------------------------------------- /images/ls_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/ls_github.png -------------------------------------------------------------------------------- /images/ls_github_hq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/ls_github_hq.png -------------------------------------------------------------------------------- /images/magicwand_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/magicwand_example.gif -------------------------------------------------------------------------------- /images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg -------------------------------------------------------------------------------- /images/opossum_looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/opossum_looking.png -------------------------------------------------------------------------------- /images/screenshots/audio_classification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/audio_classification.png -------------------------------------------------------------------------------- /images/screenshots/audio_regions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/audio_regions.png -------------------------------------------------------------------------------- /images/screenshots/audio_transcription.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/audio_transcription.png -------------------------------------------------------------------------------- /images/screenshots/chatbot_analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/chatbot_analysis.png -------------------------------------------------------------------------------- /images/screenshots/html_document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/html_document.png -------------------------------------------------------------------------------- /images/screenshots/image_bbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/image_bbox.png -------------------------------------------------------------------------------- /images/screenshots/image_keypoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/image_keypoints.png -------------------------------------------------------------------------------- /images/screenshots/image_magic_wand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/image_magic_wand.png -------------------------------------------------------------------------------- /images/screenshots/image_polygons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/image_polygons.png -------------------------------------------------------------------------------- /images/screenshots/named_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/named_entity.png -------------------------------------------------------------------------------- /images/screenshots/ranker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/ranker.png -------------------------------------------------------------------------------- /images/screenshots/text_classification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/images/screenshots/text_classification.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/favicon.ico -------------------------------------------------------------------------------- /public/files/barradeen-emotional.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/files/barradeen-emotional.mp3 -------------------------------------------------------------------------------- /public/files/images/DSC03121.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/files/images/DSC03121.jpg -------------------------------------------------------------------------------- /public/files/opossum_intro.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/files/opossum_intro.webm -------------------------------------------------------------------------------- /public/files/opossum_snow.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/files/opossum_snow.mp4 -------------------------------------------------------------------------------- /public/files/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/files/video.mp4 -------------------------------------------------------------------------------- /public/images/GitHub-Mark-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/images/GitHub-Mark-64px.png -------------------------------------------------------------------------------- /public/images/astro-visuals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/images/astro-visuals.jpg -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/images/logo.png -------------------------------------------------------------------------------- /public/images/ls_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/public/images/ls_logo.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Label Studio", 3 | "name": "Label Studio Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /scripts/copy.sh: -------------------------------------------------------------------------------- 1 | mkdir -p src/examples 2 | cp -r examples/* src/examples/ 3 | -------------------------------------------------------------------------------- /scripts/postinstall.sh: -------------------------------------------------------------------------------- 1 | cp ./node_modules/@martel/audio-file-decoder/decode-audio.wasm ./node_modules/@martel/audio-file-decoder/dist/decode-audio.wasm 2 | -------------------------------------------------------------------------------- /src/assets/icons/annotation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/icons/annotation/draft_created.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/icons/annotation/entity_created.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/icons/annotation/propagated.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/annotation/removed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/annotation/skipped.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-left-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-right-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/ban.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/assets/icons/check-bold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/chevron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/collapse-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/comment-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/comment-red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/comment-unresolved.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/cursor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/date.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/duplicate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/ellipsis.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/expand-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/expand-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/hamburger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/info-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/invisible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/icons/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/ocr.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/outliner/collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/outliner/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/outliner/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactComponent as IconOutlinerCollapse } from './collapse.svg'; 2 | export { ReactComponent as IconOutlinerExpand } from './expand.svg'; 3 | export { ReactComponent as IconOutlinerDrag } from './drag.svg'; 4 | export { ReactComponent as IconInfo } from './info.svg'; 5 | export { ReactComponent as IconOutlinerEyeOpened } from './eye_opened.svg'; 6 | export { ReactComponent as IconOutlinerEyeClosed } from './eye_closed.svg'; 7 | -------------------------------------------------------------------------------- /src/assets/icons/outliner/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/player/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/player/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/player/step.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/plus-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/plus-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/icons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/png/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/src/assets/icons/png/check.png -------------------------------------------------------------------------------- /src/assets/icons/png/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/src/assets/icons/png/cross.png -------------------------------------------------------------------------------- /src/assets/icons/properties/angle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/redo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/send.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/settings-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/sort-down-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/sort-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/sort-up-new.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/sort-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/star-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/svg.d.ts: -------------------------------------------------------------------------------- 1 | type SvgrComponent = React.StatelessComponent> 2 | 3 | declare module '*.svg' { 4 | const value: SvgrComponent; 5 | 6 | export default value; 7 | export const ReactComponent = value; 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/icons/tag.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/chevron_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/chevron_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/eye_opened.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/fastforward.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/keypoint_delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/keypoint_disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/rewind.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/sound.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/timeline/sound_muted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/tools/eraser-tool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/assets/icons/tools/magnify-tool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/icons/tools/minify-tool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/trash-rect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/tree/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/tree/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactComponent as IconArrow } from './arrow.svg'; 2 | -------------------------------------------------------------------------------- /src/assets/icons/undo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/icons/view-all.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/visible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/icons/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/assets/styles/_functions.scss: -------------------------------------------------------------------------------- 1 | @function get-color($color, $shade: "base", $map: $colors) { 2 | // check color exists 3 | @if (map-has-key($map, $color)) { 4 | $value: map-get($map, unquote($color)); 5 | // check if color or map 6 | @if type-of($value) == color { 7 | // return color 8 | @return $value; 9 | } 10 | // check shade of color exists 11 | @if (map-has-key($value, $shade)) { 12 | // return shade of color 13 | @return map-get($value, $shade); 14 | } 15 | } 16 | // else do nothing 17 | @return null; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin respond($breakpoint) { 2 | @if ($breakpoint == "phone") { 3 | @media (max-width: 760px) { 4 | @content; 5 | } 6 | } @else if ($breakpoint == "tablet") { 7 | @media (max-width: 1200px) { 8 | @content; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | /** Colors **/ 2 | $colors: ( 3 | default: #000000, 4 | error: #f5222d, 5 | ); 6 | 7 | $test-color: red; 8 | -------------------------------------------------------------------------------- /src/common/Dropdown/Dropdown.ts: -------------------------------------------------------------------------------- 1 | import { Dropdown } from './DropdownComponent'; 2 | import { DropdownTrigger } from './DropdownTrigger'; 3 | 4 | const DD = Object.assign(Dropdown, { Trigger: DropdownTrigger }); 5 | 6 | export { DD as Dropdown }; 7 | -------------------------------------------------------------------------------- /src/common/Dropdown/DropdownContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { MutableRefObject, RefObject } from 'react'; 2 | import { DropdownRef } from './DropdownComponent'; 3 | 4 | export interface DropdownContextValue { 5 | triggerRef: MutableRefObject; 6 | dropdown: RefObject; 7 | minIndex: number; 8 | hasTarget(target: HTMLElement): boolean; 9 | addChild(child: DropdownContextValue): void; 10 | removeChild(child: DropdownContextValue): void; 11 | open(): void; 12 | close(): void; 13 | } 14 | 15 | export const DropdownContext = React.createContext(null); 16 | -------------------------------------------------------------------------------- /src/common/Icon/Icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Block } from '../../utils/bem'; 3 | import './Icon.styl'; 4 | 5 | export const Icon = React.forwardRef(({ icon, ...props }, ref) => { 6 | return ( 7 | 8 | {React.createElement(icon, props)} 9 | 10 | ); 11 | }); 12 | -------------------------------------------------------------------------------- /src/common/Icon/Icon.styl: -------------------------------------------------------------------------------- 1 | .icon 2 | display inline-block 3 | white-space nowrap 4 | line-height 0 5 | -------------------------------------------------------------------------------- /src/common/Menu/MenuContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const MenuContext = React.createContext(); 4 | -------------------------------------------------------------------------------- /src/common/Oneof/Oneof.js: -------------------------------------------------------------------------------- 1 | import { Children } from 'react'; 2 | import { cloneElement, useMemo } from 'react'; 3 | 4 | export const Oneof = ({ value, children, className }) => { 5 | const childList = Children.toArray(children); 6 | 7 | const selectedChild = useMemo(() => { 8 | return childList.find(c => c.props.case === value) || null; 9 | }, [childList, value]); 10 | 11 | return selectedChild 12 | ? cloneElement(selectedChild, { 13 | ...selectedChild.props, 14 | className: [className, selectedChild.props.className].join(' '), 15 | }) 16 | : null; 17 | }; 18 | -------------------------------------------------------------------------------- /src/common/Userpic/Userpic.styl: -------------------------------------------------------------------------------- 1 | .userpic 2 | width 28px 3 | height 28px 4 | display flex 5 | overflow hidden 6 | position relative 7 | align-items center 8 | border-radius 13.5px 9 | justify-content center 10 | background #f5f5f5 11 | user-select none 12 | box-shadow inset 0px 0px 0px 1px rgba(0, 0, 0, 0.1) 13 | 14 | img 15 | opacity 0 16 | width 100% 17 | height 100% 18 | font-size 12px 19 | line-height 22px 20 | object-fit cover 21 | position absolute 22 | 23 | &__username 24 | display block 25 | font-size 12px 26 | line-height 22px 27 | font-weight bold 28 | font-family Roboto 29 | opacity 0.4 30 | -------------------------------------------------------------------------------- /src/common/Utils/mergeRefs.ts: -------------------------------------------------------------------------------- 1 | import { MutableRefObject } from 'react'; 2 | 3 | export default function mergeRefs(...inputRefs: (MutableRefObject|undefined|null)[]) { 4 | const filteredInputRefs = inputRefs.filter(Boolean) as MutableRefObject[]; 5 | 6 | if (filteredInputRefs.length <= 1) { 7 | return filteredInputRefs[0]; 8 | } 9 | 10 | return (ref: any) => { 11 | filteredInputRefs.forEach((inputRef: MutableRefObject|((ref: MutableRefObject) => void)) => { 12 | if (typeof inputRef === 'function') { 13 | inputRef(ref); 14 | } else { 15 | inputRef.current = ref; 16 | } 17 | }); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/common/Utils/useMounted.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | /** 4 | * Protects async tasks from causing memory leaks in other effects/callbacks. 5 | * Wrap any set states within a component with 6 | * 7 | * if (mounted.current) { ... } 8 | */ 9 | export const useMounted = () => { 10 | const mounted = useRef(true); 11 | 12 | useEffect(() => { 13 | mounted.current = true; 14 | return () => { 15 | mounted.current = false; 16 | }; 17 | }, []); 18 | 19 | return mounted; 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /src/common/Utils/useValueTracker.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useMemo, useState } from 'react'; 2 | 3 | export const useValueTracker = ( 4 | value?: T | undefined, 5 | defaultValue?: T | undefined, 6 | ): [T | string, (value: T) => void] => { 7 | const initialValue = useMemo(() => { 8 | return value ?? defaultValue ?? ''; 9 | }, [value, defaultValue]); 10 | 11 | const [finalValue, setValue] = useState(initialValue); 12 | 13 | useEffect(() => { 14 | setValue(initialValue); 15 | }, [initialValue]); 16 | 17 | return [finalValue, (value: T) => setValue(value)]; 18 | }; 19 | -------------------------------------------------------------------------------- /src/common/Utils/useWindowSize.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useWindowSize = () => { 4 | const [windowSize, setWindowSize] = useState({ 5 | width: window.innerWidth, 6 | height: window.innerWidth, 7 | }); 8 | 9 | useEffect(() => { 10 | const onResize = () => { 11 | setWindowSize({ 12 | width: window.innerWidth, 13 | height: window.innerWidth, 14 | }); 15 | }; 16 | 17 | window.addEventListener('resize', onResize); 18 | 19 | return () => window.removeEventListener('resize', onResize); 20 | }, []); 21 | 22 | return windowSize; 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/AnnotationTab/CommentsSection.styl: -------------------------------------------------------------------------------- 1 | .comments-section 2 | border-top 1px solid rgba(0,0,0,0.1) 3 | 4 | &__header 5 | display flex 6 | height 46px 7 | justify-content space-between 8 | padding 12px 15px 9 | align-items center 10 | font-weight 500 11 | font-size 16px 12 | line-height 22px 13 | 14 | &__title 15 | flex 1 16 | -------------------------------------------------------------------------------- /src/components/AnnotationTab/DynamicPreannotationsControl.styl: -------------------------------------------------------------------------------- 1 | .dynamic-preannotations-control 2 | top 10px 3 | left 50% 4 | position absolute 5 | background-color #fff 6 | padding 10px 7 | box-shadow 0px 0px 0px 1px rgba(0, 0, 0, 0.05), 0px 5px 10px rgba(0, 0, 0, 0.1) 8 | border-radius 7px 9 | transform translate3d(-50%, 0, 0) 10 | -------------------------------------------------------------------------------- /src/components/App/Annotation.js: -------------------------------------------------------------------------------- 1 | import Tree from '../../core/Tree'; 2 | import { isAlive } from 'mobx-state-tree'; 3 | import { useLayoutEffect } from 'react'; 4 | 5 | export function Annotation({ annotation, root }) { 6 | useLayoutEffect(() => { 7 | return () => { 8 | if (annotation && isAlive(annotation)) { 9 | annotation.resetReady(); 10 | } 11 | }; 12 | }, [annotation.pk, annotation.id]); 13 | return root ? Tree.renderItem(root, annotation) : null; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/App/Grid.styl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/src/components/App/Grid.styl -------------------------------------------------------------------------------- /src/components/BottomBar/HistoryActions.styl: -------------------------------------------------------------------------------- 1 | .history-buttons 2 | display flex 3 | gap 8px 4 | margin-left: 8px 5 | 6 | &__action 7 | width 36px 8 | height 36px 9 | border none 10 | padding 0 !important 11 | background none !important 12 | 13 | &:disabled 14 | opacity 0.6 15 | 16 | svg 17 | display block 18 | 19 | &_delete 20 | color #DD0000 21 | -------------------------------------------------------------------------------- /src/components/Choice/Choice.module.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/src/components/Choice/Choice.module.scss -------------------------------------------------------------------------------- /src/components/Comments/Comments.styl: -------------------------------------------------------------------------------- 1 | .comments 2 | --comments-padding 0 16px 8px 3 | --comments-spacing 8px 4 | 5 | padding var(--comments-padding) 6 | 7 | & > * + * { 8 | margin-top var(--comments-spacing) 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Comments/CommentsList.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { observer } from 'mobx-react'; 3 | import { Block } from '../../utils/bem'; 4 | import { CommentItem } from './CommentItem'; 5 | 6 | export const CommentsList: FC<{ commentStore: any }> = observer(({ commentStore }) => { 7 | 8 | return ( 9 | 10 | {commentStore.comments.map((comment: any) => ( 11 | 12 | ))} 13 | 14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/CurrentEntity/Controls.styl: -------------------------------------------------------------------------------- 1 | .controls 2 | height 32px 3 | display grid 4 | grid-auto-columns 1fr 5 | grid-auto-flow column 6 | grid-column-gap 12px 7 | justify-content flex-end 8 | 9 | &__tooltip-wrapper 10 | button 11 | width 100% 12 | 13 | &__skipped-info 14 | display flex 15 | svg 16 | margin 0 8px 0 4px 17 | -------------------------------------------------------------------------------- /src/components/CurrentEntity/GroundTruth.styl: -------------------------------------------------------------------------------- 1 | .ground-truth 2 | &_size 3 | &_md 4 | &, ~/__toggle, ~/__indicator 5 | width: 32px 6 | height: 32px 7 | 8 | &_disabled 9 | pointer-events: none 10 | &__toggle 11 | padding 0 12 | 13 | &__indicator 14 | color rgba(#000, 0.4) 15 | 16 | &_dark 17 | color #000 18 | 19 | &_active 20 | color #ffbb1a 21 | 22 | path 23 | fill-opacity 1 24 | stroke-opacity 1 25 | -------------------------------------------------------------------------------- /src/components/CurrentEntity/HistoryActions.styl: -------------------------------------------------------------------------------- 1 | .history-buttons 2 | display flex 3 | 4 | &__action 5 | width 36px 6 | height 36px 7 | border none 8 | padding 0 !important 9 | background none !important 10 | 11 | &:disabled 12 | opacity 0.6 13 | 14 | svg 15 | display block 16 | 17 | &_delete 18 | color #DD0000 19 | -------------------------------------------------------------------------------- /src/components/Dialog/Dialog.module.scss: -------------------------------------------------------------------------------- 1 | .block { 2 | position: relative; 3 | border: 1px solid #f2f3f4; 4 | background-color: #f8f9f9; 5 | border-radius: 5px; 6 | padding: 7px 20px; 7 | margin: 10px 0; 8 | display: flex; 9 | flex-flow: column; 10 | 11 | &:last-of-type { 12 | margin-bottom: 20px; 13 | } 14 | 15 | &_selected { 16 | border: 2px solid #ff4d4f; 17 | } 18 | } 19 | 20 | .name { 21 | font-weight: bold; 22 | } 23 | 24 | .tag { 25 | margin-top: 10px; 26 | } 27 | 28 | .date { 29 | font-style: italic; 30 | font-size: 0.8rem; 31 | } 32 | -------------------------------------------------------------------------------- /src/components/DraftPanel/DraftPanel.styl: -------------------------------------------------------------------------------- 1 | .draft-panel 2 | margin 8px 0 3 | &__toggle 4 | padding 0 5 | border 0 6 | vertical-align -0.5px 7 | height auto 8 | color $accent_color 9 | background none 10 | padding 0 11 | cursor pointer 12 | &:hover 13 | opacity 0.8 14 | -------------------------------------------------------------------------------- /src/components/Entities/LabelItem.styl: -------------------------------------------------------------------------------- 1 | .list-item 2 | --color #666 3 | padding 0 8px 0 0 4 | 5 | &__title 6 | display flex 7 | flex-flow row nowrap 8 | align-items center 9 | 10 | &__counter 11 | color var(--color) 12 | margin-left 12px 13 | 14 | &__visibility 15 | padding: 0 !important 16 | width: 24px 17 | padding: 24px -------------------------------------------------------------------------------- /src/components/Entities/SortMenu.styl: -------------------------------------------------------------------------------- 1 | .sort-menu 2 | &__option-inner 3 | width: 135px 4 | display: flex 5 | justify-content: space-between 6 | &__title 7 | display flex 8 | align-items center 9 | &__icon 10 | display flex 11 | align-items center 12 | justify-content: center; 13 | width: 24px 14 | height: 24px 15 | margin-right: 4px 16 | 17 | &__icon > * 18 | margin: 0 !important -------------------------------------------------------------------------------- /src/components/Entity/Entity.module.scss: -------------------------------------------------------------------------------- 1 | .block { 2 | display: flex; 3 | justify-content: flex-start; 4 | align-items: flex-start; 5 | flex-wrap: wrap; 6 | margin-top: 1em; 7 | } 8 | 9 | .button { 10 | margin-bottom: 10px; 11 | margin-right: 10px; 12 | } 13 | 14 | .labels { 15 | word-break: break-word; 16 | } 17 | 18 | .tag { 19 | margin-bottom: 5px; 20 | white-space: normal !important; 21 | } 22 | 23 | .statesblk > span { 24 | display: block; 25 | } 26 | 27 | .statesblk > div { 28 | margin-bottom: 0; 29 | } 30 | 31 | .row { 32 | display: flex; 33 | white-space: pre-wrap; 34 | margin-bottom: 12px; 35 | } 36 | 37 | .long { 38 | white-space: nowrap; 39 | overflow: hidden; 40 | text-overflow: ellipsis; 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Entity/Entity.styl: -------------------------------------------------------------------------------- 1 | .entity 2 | padding 0 15px 3 | margin-top 12px 4 | 5 | &__info 6 | margin-bottom 12px 7 | 8 | &__warning 9 | box-sizing border-box 10 | display flex 11 | flex-direction row 12 | align-items center 13 | padding 8px 14 | gap 8px 15 | background rgba(255, 183, 122, 0.16) 16 | border 1px solid rgba(137, 128, 152, 0.16) 17 | border-radius 4px 18 | margin 16px 0 0 19 | flex none 20 | order 0 21 | flex-grow 1 22 | svg 23 | width 20px 24 | height 17px 25 | fill var(--incomplete-warning-color, #FA8C16) 26 | -------------------------------------------------------------------------------- /src/components/ErrorMessage/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './ErrorMessage.module.scss'; 3 | import { sanitizeHtml } from '../../utils/html'; 4 | 5 | export const ErrorMessage = ({ error }) => { 6 | if (typeof error === 'string') { 7 | return
; 8 | } 9 | const body = error instanceof Error ? error.message : error; 10 | 11 | return
{body}
; 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/ErrorMessage/ErrorMessage.module.scss: -------------------------------------------------------------------------------- 1 | .error { 2 | margin: 16px 0; 3 | padding: 10px 15px; 4 | display: block; 5 | border-radius: 3px; 6 | color: rgb(119, 27, 4); 7 | border: 1px solid rgb(230, 138, 110); 8 | background-color: rgb(255, 193, 174); 9 | white-space: normal; 10 | 11 | & + & { 12 | margin: 0 0 16px; // in case we are in flex container, where margins don't collapse 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Filter/Filter.styl: -------------------------------------------------------------------------------- 1 | .filter 2 | padding 10px 3 | 4 | &__empty 5 | margin-bottom 10px 6 | font-size 14px 7 | color #585858 8 | width 220px 9 | 10 | .filter-button 11 | display flex 12 | height 24px 13 | padding 0 6px 0 2px 14 | cursor pointer 15 | align-items center 16 | border-radius 4px 17 | 18 | &:active 19 | &_active 20 | background rgba(65, 60, 74, 0.08) 21 | 22 | &__icon 23 | align-items center 24 | display flex 25 | 26 | &__filter-length 27 | font-size 11px 28 | font-weight 500 29 | text-align center 30 | color #030852 31 | width 15px 32 | height 20px 33 | line-height 21px 34 | border-radius 2px 35 | background #d6e4ff 36 | margin-left 3px -------------------------------------------------------------------------------- /src/components/Filter/FilterInterfaces.tsx: -------------------------------------------------------------------------------- 1 | export enum Logic { 2 | and = 'And', 3 | or = 'Or', 4 | } 5 | 6 | export interface FilterInterface { 7 | availableFilters: AvailableFiltersInterface[]; 8 | onChange: (filter: any) => void; 9 | filterData: any; 10 | 11 | animated?: boolean; 12 | } 13 | 14 | export interface FilterListInterface { 15 | field?: string | string[] | undefined; 16 | operation?: string | string[] | undefined; 17 | value?: any; 18 | path?: string; 19 | logic?: 'and' | 'or'; 20 | } 21 | 22 | export interface AvailableFiltersInterface { 23 | label: string; 24 | path: string; 25 | type: 'Boolean' | 'Common' | 'Number' | 'String' | string; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/Filter/FilterRow.styl: -------------------------------------------------------------------------------- 1 | .filter-row 2 | display flex 3 | flex-direction row 4 | align-items center 5 | margin-bottom 8px 6 | 7 | &__title-row 8 | width 60px 9 | text-align right 10 | 11 | &__delete 12 | cursor pointer 13 | height 16px 14 | 15 | &__column 16 | display flex 17 | margin-right 8px 18 | 19 | input 20 | height 24px !important -------------------------------------------------------------------------------- /src/components/Filter/types/Boolean.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FilterDropdown } from '../FilterDropdown'; 3 | import { observer } from 'mobx-react'; 4 | 5 | 6 | const BaseInput = observer((props) => ( 7 | { 9 | props.onChange(!value); 10 | }} 11 | items={[ 12 | { label: 'true', key: true }, 13 | { label: 'false', key: false }, 14 | ]} 15 | /> 16 | )); 17 | 18 | export const BooleanFilter = [ 19 | { 20 | key: 'equal', 21 | label: 'is', 22 | valueType: 'single', 23 | input: BaseInput, 24 | }, 25 | ]; 26 | -------------------------------------------------------------------------------- /src/components/Filter/types/Common.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FilterDropdown } from '../FilterDropdown'; 3 | import { observer } from 'mobx-react'; 4 | 5 | 6 | const BaseInput = observer((props) => ( 7 | props.onChange(value)} 9 | items={[ 10 | { label: 'yes' }, 11 | { label: 'no' }, 12 | ]} 13 | /> 14 | )); 15 | 16 | export const Common = [ 17 | { 18 | key: 'empty', 19 | label: 'is empty', 20 | input: BaseInput, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /src/components/Filter/types/index.js: -------------------------------------------------------------------------------- 1 | export { BooleanFilter as Boolean } from './Boolean'; 2 | export { Common } from './Common'; 3 | export { NumberFilter as Number } from './Number'; 4 | export { StringFilter as Image, StringFilter as String } from './String'; 5 | -------------------------------------------------------------------------------- /src/components/Hint/Hint.module.scss: -------------------------------------------------------------------------------- 1 | .main { 2 | font-size: 9px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Hint/Hint.styl: -------------------------------------------------------------------------------- 1 | .hint 2 | font-size 9px 3 | -------------------------------------------------------------------------------- /src/components/Hint/Hint.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect */ 2 | import React from 'react'; 3 | import Enzyme, { shallow } from 'enzyme'; 4 | import { shallowToJson } from 'enzyme-to-json'; 5 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; 6 | 7 | Enzyme.configure({ adapter: new Adapter() }); 8 | 9 | import Hint from './Hint'; 10 | 11 | describe('Hint', () => { 12 | it('Should render correctly', () => { 13 | const component = ( 14 | 15 | Test 16 | 17 | ); 18 | 19 | const output = shallow(component); 20 | 21 | expect(shallowToJson(output)).toMatchSnapshot(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/components/Hint/Hint.tsx: -------------------------------------------------------------------------------- 1 | import { CSSProperties, FC } from 'react'; 2 | import { Block } from '../../utils/bem'; 3 | 4 | import './Hint.styl'; 5 | 6 | interface HintProps { 7 | copy?: string; 8 | style?: CSSProperties; 9 | className?: string; 10 | } 11 | 12 | /** 13 | * Hint Component 14 | */ 15 | const Hint: FC = (props) => { 16 | return ( 17 | 24 | {props.children} 25 | 26 | ); 27 | }; 28 | 29 | export default Hint; 30 | -------------------------------------------------------------------------------- /src/components/Hint/__snapshots__/Hint.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Hint Should render correctly 1`] = ` 4 | 15 | Test 16 | 17 | `; 18 | -------------------------------------------------------------------------------- /src/components/HtxTextBox/HtxTextBox.module.scss: -------------------------------------------------------------------------------- 1 | .input { 2 | width: 100%; 3 | padding: 0.4em 1em; 4 | display: block; 5 | } 6 | 7 | .editing { 8 | padding: 0; 9 | position: relative; 10 | 11 | .enter { 12 | pointer-events: all; 13 | } 14 | } 15 | 16 | .delete { 17 | color: #1890ff; 18 | padding-top: 0.5em; 19 | padding-left: 1em; 20 | line-height: 1.8em; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/ImageView/Image.styl: -------------------------------------------------------------------------------- 1 | .image 2 | top 0 3 | position absolute 4 | overflow hidden 5 | 6 | .image-progress 7 | padding 14px 16px 8 | display flex 9 | justify-content center 10 | background #fff 11 | border-radius 4px 12 | box-shadow 0 0 0 0.5px rgba(0,0,0,0.2) 13 | margin 16px 0.5px 14 | flex-direction column 15 | color #777 16 | font-weight bold 17 | 18 | &__bar 19 | width 200px 20 | -------------------------------------------------------------------------------- /src/components/ImageView/ImageViewContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const ImageViewContext = createContext<{suggestion: boolean}>({ suggestion: false }); 4 | 5 | export const ImageViewProvider = ImageViewContext.Provider; 6 | -------------------------------------------------------------------------------- /src/components/NewTaxonomy/NewTaxonomy.styl: -------------------------------------------------------------------------------- 1 | :global(.htx-taxonomy-item-color) 2 | padding 4px 4px 3 | border-radius 2px 4 | -------------------------------------------------------------------------------- /src/components/Node/NodeView.ts: -------------------------------------------------------------------------------- 1 | interface NodeViewProps { 2 | name: string; 3 | icon: any; 4 | altIcon?: any; 5 | getContent?: (node: any) => JSX.Element | null; 6 | fullContent?: (node: any) => JSX.Element | null; 7 | } 8 | 9 | export const NodeView = ({ 10 | name, 11 | icon, 12 | altIcon = null, 13 | getContent = () => null, 14 | fullContent = () => null, 15 | }: NodeViewProps) => { 16 | if (altIcon instanceof Function) { 17 | [getContent, altIcon] = [altIcon, null]; 18 | } 19 | 20 | return { name, icon, altIcon, getContent, fullContent }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/Relations/Relations.styl: -------------------------------------------------------------------------------- 1 | .relations 2 | &__header 3 | display flex 4 | height 46px 5 | justify-content space-between 6 | padding 12px 15px 7 | align-items center 8 | font-weight 500 9 | font-size 16px 10 | line-height 22px 11 | 12 | &__title 13 | flex 1 14 | 15 | &__content 16 | padding 0 15px 17 | -------------------------------------------------------------------------------- /src/components/RelationsOverlay/watchers/DOMWatcher.js: -------------------------------------------------------------------------------- 1 | export class DOMWatcher { 2 | constructor(root, element, callback) { 3 | this.root = root; 4 | this.element = element.getRegionElement(); 5 | this.callback = callback; 6 | 7 | this.handleUpdate(); 8 | } 9 | 10 | handleResize() { 11 | window.addEventListener('resize', this.onUpdate); 12 | } 13 | 14 | handleUpdate() { 15 | this.observer = new MutationObserver(this.onUpdate); 16 | 17 | this.observer.observe(this.element, { attributes: true }); 18 | } 19 | 20 | onUpdate = () => { 21 | this.callback(); 22 | }; 23 | 24 | destroy() { 25 | window.removeEventListener('resize', this.onUpdate); 26 | this.observer.disconnect(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/RelationsOverlay/watchers/EllipseWatcher.js: -------------------------------------------------------------------------------- 1 | import { observe } from 'mobx'; 2 | import { debounce } from '../../../utils/debounce'; 3 | 4 | export class EllipseWatcher { 5 | constructor(root, element, callback) { 6 | this.root = root; 7 | this.element = element; 8 | this.callback = callback; 9 | 10 | this.handleUpdate(); 11 | } 12 | 13 | handleUpdate() { 14 | this.disposers = ['x', 'y', 'radiusX', 'radiusY', 'rotation'].map(property => { 15 | return observe(this.element, property, this.onUpdate, true); 16 | }); 17 | } 18 | 19 | onUpdate = debounce(() => { 20 | this.callback(); 21 | }, 10); 22 | 23 | destroy() { 24 | this.disposers.forEach(dispose => dispose()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/RelationsOverlay/watchers/PolygonWatcher.js: -------------------------------------------------------------------------------- 1 | import { observe } from 'mobx'; 2 | import { debounce } from '../../../utils/debounce'; 3 | 4 | export class PolygonWatcher { 5 | constructor(root, element, callback) { 6 | this.root = root; 7 | this.element = element; 8 | this.callback = callback; 9 | 10 | this.handleUpdate(); 11 | } 12 | 13 | handleUpdate() { 14 | this.disposers = ['x', 'y', 'width', 'height'].map(property => { 15 | return observe(this.element, property, this.onUpdate, true); 16 | }); 17 | } 18 | 19 | onUpdate = debounce(() => { 20 | this.callback(); 21 | }, 10); 22 | 23 | destroy() { 24 | this.disposers.forEach(dispose => dispose()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/RelationsOverlay/watchers/index.js: -------------------------------------------------------------------------------- 1 | export { DOMWatcher } from './DOMWatcher'; 2 | export { createPropertyWatcher } from './PropertyWatcher'; 3 | -------------------------------------------------------------------------------- /src/components/Segment/Segment.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | 4 | import styles from './Segment.module.scss'; 5 | 6 | /** 7 | * Segment Component 8 | */ 9 | export default class Segment extends React.Component { 10 | componentDidMount() { 11 | const { annotation } = this.props; 12 | 13 | if (annotation) annotation.updateObjects(); 14 | } 15 | 16 | render() { 17 | let cn = styles.block; 18 | 19 | if (this.props.className) cn = cn + ' ' + this.props.className; 20 | 21 | return
{this.props.children}
; 22 | } 23 | } 24 | 25 | Segment.propTypes = { 26 | children: PropTypes.array.isRequired, 27 | }; 28 | -------------------------------------------------------------------------------- /src/components/Segment/Segment.module.scss: -------------------------------------------------------------------------------- 1 | @import "../../assets/styles/global.scss"; 2 | 3 | .block { 4 | position: relative; 5 | width: 100%; 6 | padding: 1em 1em; 7 | border-radius: 0.28571429rem; 8 | margin-bottom: 1em; 9 | 10 | @include respond("phone") { 11 | width: 100%; 12 | margin-right: 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Settings/TagSettings/Types.ts: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | 3 | export interface Settings> extends FC<{store: any} & T> { 4 | tagName: string; 5 | title: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Settings/TagSettings/VideoSettings.tsx: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react-lite'; 2 | import VideoProperties from '../../../core/settings/videosettings'; 3 | import { SettingsRenderer } from './SettingsRenderer'; 4 | import { Settings } from './Types'; 5 | 6 | const VideoSettingsPure: Settings = ({ store }) => { 7 | return ( 8 | 12 | ); 13 | }; 14 | 15 | VideoSettingsPure.displayName = 'VideoSettings'; 16 | VideoSettingsPure.tagName = 'Video'; 17 | VideoSettingsPure.title = 'Video'; 18 | 19 | export const VideoSettings = observer(VideoSettingsPure); 20 | -------------------------------------------------------------------------------- /src/components/Settings/TagSettings/index.ts: -------------------------------------------------------------------------------- 1 | export { VideoSettings } from './VideoSettings'; 2 | -------------------------------------------------------------------------------- /src/components/SidePanels/Components/RegionControlButton.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { Button, ButtonProps } from '../../../common/Button/Button'; 3 | 4 | export const RegionControlButton: FC = ({ children, onClick, ...props }) => { 5 | return ( 6 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/SidePanels/OutlinerPanel/OutlinerPanel.styl: -------------------------------------------------------------------------------- 1 | .outliner 2 | --view-controls-height 40px 3 | 4 | &.ff_hide_all_regions 5 | --view-controls-height 32px 6 | 7 | &.ff_outliner_optim 8 | height: 100% 9 | 10 | &__empty 11 | padding 16px 12 | font-size 14px 13 | color rgba(#000, 0.6) 14 | 15 | .ff_outliner_optim &-tree 16 | height calc(100% - var(--view-controls-height)) 17 | 18 | .filters-info 19 | text-align center 20 | margin 22px 0 30px 0 21 | 22 | &-title 23 | font-weight 500 24 | font-size 12px 25 | color #1f1f1f 26 | 27 | &-description 28 | font-weight 400 29 | font-size 12px 30 | color #898098 -------------------------------------------------------------------------------- /src/components/SidePanels/SidePanelsContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | interface SidePanelsContextProps { 4 | locked: boolean; 5 | } 6 | 7 | export const SidePanelsContext = createContext({ 8 | locked: false, 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/SidePanels/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_PANEL_WIDTH = 320; 2 | export const DEFAULT_PANEL_HEIGHT = 300; 3 | export const DEFAULT_PANEL_MAX_WIDTH = 500; 4 | export const DEFAULT_PANEL_MAX_HEIGHT = 500; 5 | export const DEFAULT_PANEL_MIN_HEIGHT = 55; 6 | export const PANEL_HEADER_HEIGHT = 24; 7 | export const PANEL_HEADER_HEIGHT_PADDED = PANEL_HEADER_HEIGHT + 2; 8 | -------------------------------------------------------------------------------- /src/components/SidebarTabs/SidebarTabs.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Block, Elem } from '../../utils/bem'; 3 | import './SidebarTabs.styl'; 4 | 5 | // @todo there was an idea of switchable tabs, but they were not used, 6 | // @todo so implementation was removed and the whole part of interface 7 | // @todo is waiting to be removed in favor of new UI (see FF_DEV_3873) 8 | export const SidebarTabs = ({ children }) => { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/components/SimpleBadge/SimpleBadge.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './SimpleBadge.module.scss'; 3 | 4 | export const SimpleBadge = ({ number, className, ...props }) => ( 5 |
6 | {number} 7 |
8 | ); 9 | -------------------------------------------------------------------------------- /src/components/SimpleBadge/SimpleBadge.module.scss: -------------------------------------------------------------------------------- 1 | $SIZE: 20px; 2 | 3 | .badge { 4 | height: $SIZE; 5 | min-width: $SIZE; 6 | border-radius: $SIZE / 2; 7 | line-height: $SIZE; 8 | padding: 0 6px; 9 | flex-shrink: 0; 10 | font-size: 12px; 11 | font-weight: normal; 12 | text-align: center; 13 | white-space: nowrap; 14 | background: grey; 15 | color: white; 16 | box-shadow: 0 0 0 1px #fff; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/TextHighlight/Range.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Class for text data with 4 params: 3 | * start -> int: the index of the character where the range start. 4 | * end -> int: the index of the character where the range stop. 5 | * text -> string: the highlighted text. 6 | * data -> object: extra data (the props of the highlight component) 7 | */ 8 | export default class Range { 9 | constructor(start, end, text, data = {}) { 10 | this.start = start; 11 | this.end = end; 12 | this.text = text; 13 | this.data = data; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/components/TextHighlight/TextHighlight.module.scss: -------------------------------------------------------------------------------- 1 | .block { 2 | border: 1px solid #e8e8e8; 3 | background: rgba(0, 0, 0, 0.01); 4 | margin-bottom: 0.5em; 5 | border-radius: 3px; 6 | padding: 20px 10px; 7 | word-break: break-word; 8 | } 9 | -------------------------------------------------------------------------------- /src/components/TimeDurationControl/TimeBox.styl: -------------------------------------------------------------------------------- 1 | .time-box 2 | width 100px 3 | height 24px 4 | border-radius 4px 0 0 4px 5 | border 1px solid #89809829 6 | font-size 14px 7 | 8 | &_sidepanel 9 | width 100% 10 | 11 | &_inverted 12 | border-left none 13 | border-radius 0 4px 4px 0 14 | 15 | &__input-time 16 | border none 17 | padding 0 18 | outline none 19 | width 100% 20 | height 100% 21 | line-height 22px 22 | background none 23 | text-align center 24 | 25 | &__displayed-time 26 | width 100% 27 | height 100% 28 | line-height 22px 29 | text-align center 30 | 31 | span 32 | color #B6B7B6 33 | -------------------------------------------------------------------------------- /src/components/TimeDurationControl/TimeDurationControl.styl: -------------------------------------------------------------------------------- 1 | .timer-duration-control 2 | display flex -------------------------------------------------------------------------------- /src/components/Timeline/Context.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | import { TimelineContextValue } from './Types'; 3 | 4 | export const TimelineContext = createContext({ 5 | position: 0, 6 | length: 0, 7 | regions: [], 8 | step: 10, 9 | playing: false, 10 | settings: {}, 11 | visibleWidth: 0, 12 | seekOffset: 0, 13 | data: undefined, 14 | }); 15 | 16 | export const TimelineContextProvider = TimelineContext.Provider; 17 | -------------------------------------------------------------------------------- /src/components/Timeline/Controls/AudioControl.styl: -------------------------------------------------------------------------------- 1 | .audio-control 2 | position relative 3 | 4 | &__modal 5 | left 0 6 | top 36px 7 | position absolute 8 | width 232px 9 | background #fff 10 | border-radius 4px 11 | padding 22px 0 0 12 | box-shadow 0 4px 10px 0 #0000001F 13 | z-index 10 14 | 15 | &__range 16 | margin 0 20px 17 | width calc(100% - 40px) 18 | padding 0 19 | 20 | &__mute 21 | margin-top 10px 22 | padding 2px 0 23 | border-top 1px solid #8980981F 24 | font-size 16px 25 | cursor pointer 26 | 27 | &__mute-button 28 | padding 8px 16px 29 | 30 | &:hover 31 | background #00000014 -------------------------------------------------------------------------------- /src/components/Timeline/Controls/ConfigControl.styl: -------------------------------------------------------------------------------- 1 | .audio-config 2 | position relative 3 | 4 | &__modal 5 | left 0 6 | top 36px 7 | position absolute 8 | width 232px 9 | background #fff 10 | border-radius 4px 11 | padding 22px 0 0 12 | box-shadow 0 4px 10px 0 #0000001F 13 | z-index 10 14 | 15 | &__range 16 | margin 0 20px 17 | width calc(100% - 40px) 18 | padding 0 19 | 20 | &__buttons 21 | margin-top 10px 22 | padding 2px 0 23 | border-top 1px solid #8980981F 24 | font-size 16px 25 | cursor pointer 26 | 27 | &__menu-button 28 | padding 8px 16px 29 | cursor pointer 30 | 31 | &:hover 32 | background #00000014 33 | -------------------------------------------------------------------------------- /src/components/Timeline/Controls/Info.styl: -------------------------------------------------------------------------------- 1 | .control-info 2 | position relative 3 | margin-left 7px 4 | height 14px 5 | width 14px 6 | 7 | &__tooltip 8 | transition opacity .2s ease-out 9 | background #262626 10 | padding 4px 16px 11 | width 220px 12 | height auto 13 | border-radius 4px 14 | color #FAFAFA 15 | font-size 16px 16 | position: absolute; 17 | left: 50%; 18 | transform: translateX(-50%); 19 | visibility hidden 20 | opacity 0 21 | z-index 2 22 | 23 | &:hover &__tooltip 24 | visibility visible 25 | opacity 1 -------------------------------------------------------------------------------- /src/components/Timeline/Controls/Info.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Block, Elem } from '../../../utils/bem'; 3 | 4 | import './Info.styl'; 5 | import { IconInfoConfig } from '../../../assets/icons/timeline'; 6 | 7 | export interface InfoProps { 8 | text:string; 9 | } 10 | 11 | export const Info: FC = ({ 12 | text, 13 | }) => { 14 | 15 | return ( 16 | 17 | 18 | 21 | {text} 22 | 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/Timeline/SideControls/FramesControl.styl: -------------------------------------------------------------------------------- 1 | .frames-control 2 | height 36px 3 | min-width 115px 4 | display flex 5 | align-items center 6 | justify-content center 7 | background rgba(#000, 0.05) 8 | border-radius 4px 9 | 10 | font-weight 500 11 | font-size 16px 12 | line-height 19px 13 | color #000 14 | padding 0 8px 15 | 16 | span 17 | opacity 0.4 18 | padding-left 5px 19 | font-weight 400 20 | 21 | input 22 | width 100% 23 | height 100% 24 | border none 25 | background none 26 | padding 0 27 | margin 0 28 | font-size 16px 29 | line-height 19px 30 | text-align center 31 | -------------------------------------------------------------------------------- /src/components/Timeline/SideControls/index.ts: -------------------------------------------------------------------------------- 1 | export { FramesControl } from './FramesControl'; 2 | export { AudioVolumeControl } from './VolumeControl'; 3 | -------------------------------------------------------------------------------- /src/components/Timeline/Timeline.styl: -------------------------------------------------------------------------------- 1 | .timeline 2 | user-select none 3 | background-color #fff 4 | font-family "Roboto Condensed", Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 5 | 6 | &__topbar 7 | min-height 48px 8 | padding 6px 9 | display grid 10 | grid-row-gap 8px 11 | align-items center 12 | grid-auto-rows min-content 13 | grid-template-rows 1fr 14 | border-top 1px solid rgba(#000, 0.1) 15 | border-bottom 1px solid rgba(#000, 0.1) 16 | 17 | .audio 18 | min-width 425px -------------------------------------------------------------------------------- /src/components/Timeline/Views/Frames/Minimap.styl: -------------------------------------------------------------------------------- 1 | .minimap 2 | width 100% 3 | height 100% 4 | display flex 5 | align-items stretch 6 | flex-direction column 7 | justify-content center 8 | 9 | &__region 10 | height 2px 11 | width 100% 12 | padding 1px 0 13 | overflow hidden 14 | position relative 15 | box-sizing content-box 16 | 17 | &__connection 18 | height 2px 19 | position absolute 20 | background var(--color) 21 | -------------------------------------------------------------------------------- /src/components/Timeline/Views/Wave/index.ts: -------------------------------------------------------------------------------- 1 | import { TimelineView } from '../../Types'; 2 | import { Wave } from './Wave'; 3 | 4 | const View: TimelineView = { 5 | View: Wave, 6 | settings: { 7 | playpauseHotkey: 'media:playpause', 8 | stepBackHotkey: 'media:step-backward', 9 | stepForwardHotkey: 'media:step-forward', 10 | }, 11 | }; 12 | 13 | export default View; 14 | -------------------------------------------------------------------------------- /src/components/Timeline/Views/index.ts: -------------------------------------------------------------------------------- 1 | import { default as frames } from './Frames'; 2 | import { default as wave } from './Wave'; 3 | 4 | const Views = { 5 | frames, 6 | wave, 7 | }; 8 | 9 | export type ViewTypes = keyof typeof Views; 10 | export type ViewType = (typeof Views)[T]; 11 | 12 | export default Views; 13 | -------------------------------------------------------------------------------- /src/components/Toolbar/Toolbar.styl: -------------------------------------------------------------------------------- 1 | .toolbar 2 | width 40px 3 | background #FFFFFF 4 | box-shadow 0px 0px 0px 1px rgba(0, 0, 0, 0.05), 0px 5px 10px rgba(0, 0, 0, 0.1) 5 | border-radius 7px 6 | position sticky 7 | top 70px 8 | margin-top 50px 9 | 10 | &::before 11 | height 12px 12 | display block 13 | background-color rgba(#000, 0.05) 14 | content "" 15 | border-radius 7px 7px 0 0 16 | 17 | &__group ~ &__group 18 | margin-top 4px 19 | border-top 2px solid rgba(0, 0, 0, 0.05) 20 | 21 | &_expanded 22 | width auto 23 | min-width 210px 24 | display flex 25 | flex-direction column 26 | -------------------------------------------------------------------------------- /src/components/Toolbar/ToolbarContext.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const ToolbarContext = createContext({ expanded: false }); 4 | 5 | export const ToolbarProvider = ToolbarContext.Provider; 6 | -------------------------------------------------------------------------------- /src/components/Tools/Styles.module.scss: -------------------------------------------------------------------------------- 1 | .block { 2 | display: flex; 3 | flex-flow: column; 4 | align-items: center; 5 | border: 1px solid rgba(34, 36, 38, 0.15); 6 | border-radius: 0.28571429rem; 7 | width: fit-content; 8 | padding: 0.5em; 9 | } 10 | 11 | .divider { 12 | margin: 12px 0; 13 | } 14 | 15 | .button { 16 | margin: 0.3rem 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/TopBar/HistoryActions.styl: -------------------------------------------------------------------------------- 1 | .history-buttons 2 | display flex 3 | 4 | &__action 5 | width 36px 6 | height 36px 7 | border none 8 | padding 0 !important 9 | background none !important 10 | 11 | &:disabled 12 | opacity 0.6 13 | 14 | svg 15 | display block 16 | 17 | &_delete 18 | color #DD0000 19 | -------------------------------------------------------------------------------- /src/components/TreeValidation/TreeValidation.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PropTypes } from 'prop-types'; 3 | import { getEnv } from 'mobx-state-tree'; 4 | import { inject, observer } from 'mobx-react'; 5 | 6 | import { ErrorMessage } from '../ErrorMessage/ErrorMessage'; 7 | 8 | export const TreeValidation = inject('store')( 9 | observer(({ store, errors }) => { 10 | return ( 11 |
12 | {errors.map((error, index) => ( 13 | 14 | ))} 15 |
16 | ); 17 | }), 18 | ); 19 | 20 | TreeValidation.propTypes = { 21 | errors: PropTypes.array.isRequired, 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/VideoCanvas/VideoConstants.ts: -------------------------------------------------------------------------------- 1 | export const MIN_ZOOM = 0.1; 2 | export const MAX_ZOOM = 10; 3 | export const ZOOM_STEP = 0.1; 4 | export const ZOOM_STEP_WHEEL = 0.00025; 5 | export const MIN_ZOOM_WHEEL = 0.05; 6 | export const MAX_ZOOM_WHEEL = 0.5; 7 | -------------------------------------------------------------------------------- /src/components/Waveform/Waveform.module.scss: -------------------------------------------------------------------------------- 1 | .progress { 2 | color: #ff5630; 3 | } 4 | 5 | .wave { 6 | position: relative; 7 | 8 | canvas { 9 | // prevent reset.css from breaking waveforms 10 | max-width: unset; 11 | } 12 | } 13 | 14 | .menu { 15 | margin: 2em 0; 16 | } 17 | -------------------------------------------------------------------------------- /src/core/feature-flags/flags.json: -------------------------------------------------------------------------------- 1 | { 2 | "ff_front_1170_outliner_030222_short": true, 3 | "ff_front_DEV_1713_audio_ui_150222_short": false, 4 | "ff_front_dev_2715_audio_3_280722_short": true, 5 | "fflag_fix_front_dev_3391_interactive_view_all": false, 6 | "fflag_fix_front_dev_3617_taxonomy_memory_leaks_fix": true, 7 | "fflag_feat_front_dev_3873_labeling_ui_improvements_short": true, 8 | "fflag_feat_front_dev_4081_magic_wand_tool": true, 9 | "fflag_feat_front_lsdv_3012_syncable_tags_070423_short": true, 10 | "fflag_feat_front_lsdv_4832_new_ranker_tag_120423_short": true, 11 | "fflag_feat_front_lsdv_4620_richtext_opimization_060423_short": true, 12 | "fflag_fix_front_lsdv_4620_memory_leaks_100723_short": true 13 | } 14 | -------------------------------------------------------------------------------- /src/core/feature-flags/index.ts: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV !== 'production' && !window.APP_SETTINGS) { 2 | const feature_flags = (() => { 3 | try { 4 | return require('./flags.json'); 5 | } catch (err) { 6 | return {}; 7 | } 8 | })(); 9 | 10 | window.APP_SETTINGS = { feature_flags }; 11 | } 12 | -------------------------------------------------------------------------------- /src/core/settings/types.ts: -------------------------------------------------------------------------------- 1 | import { ChangeEvent } from 'react'; 2 | 3 | export interface SettingsProperty { 4 | description: string; 5 | defaultValue: any; 6 | type: 'boolean' | 'number' | 'text'; 7 | min?: number; 8 | max?: number; 9 | step?: number; 10 | ff?: string; 11 | onChangeEvent?: (e: ChangeEvent) => void; 12 | } 13 | 14 | export type SettingsProperties = Record 15 | -------------------------------------------------------------------------------- /src/core/settings/videosettings.ts: -------------------------------------------------------------------------------- 1 | import { FF_DEV_3350 } from '../../utils/feature-flags'; 2 | import { SettingsProperties } from './types'; 3 | 4 | export default { 5 | 'videoDrawOutside': { 6 | 'description': 'Allow drawing outside of video boundaries', 7 | 'defaultValue': false, 8 | 'type': 'boolean', 9 | 'ff': FF_DEV_3350, 10 | }, 11 | 'videoHopSize': { 12 | 'description': 'Video hop size', 13 | 'defaultValue': 10, 14 | 'type': 'number', 15 | }, 16 | } as SettingsProperties; 17 | 18 | -------------------------------------------------------------------------------- /src/defaultOptions.js: -------------------------------------------------------------------------------- 1 | export default { 2 | interfaces: [ 3 | 'panel', 4 | 'update', 5 | 'submit', 6 | 'skip', 7 | 'controls', 8 | 'infobar', 9 | 'topbar', 10 | 'instruction', 11 | 'side-column', 12 | 'annotations:history', 13 | 'annotations:tabs', 14 | 'annotations:menu', 15 | 'annotations:current', 16 | 'annotations:add-new', 17 | 'annotations:delete', 18 | 'annotations:view-all', 19 | 'predictions:tabs', 20 | 'predictions:menu', 21 | 'auto-annotation', 22 | 'edit-history', 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /src/hooks/useMedia.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export const useMedia = (query: string) => { 4 | const [match, setMatch] = useState(window.matchMedia(query)); 5 | 6 | useEffect(() => { 7 | const handleWindowResize = () => { 8 | setMatch(window.matchMedia(query)); 9 | }; 10 | 11 | window.addEventListener('resize', handleWindowResize); 12 | 13 | return () => window.removeEventListener('resize', handleWindowResize); 14 | }, []); 15 | 16 | useEffect(() => { 17 | setMatch(window.matchMedia(query)); 18 | }, [query]); 19 | 20 | return match; 21 | }; 22 | -------------------------------------------------------------------------------- /src/hooks/useMemoizedHandlers.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | export const useMemoizedHandlers = >(handlers: T): T => { 4 | const handlersRef = useRef(handlers); 5 | 6 | useEffect(() => { 7 | Object.assign(handlersRef.current, handlers); 8 | }, [handlers]); 9 | 10 | return handlersRef.current; 11 | }; 12 | -------------------------------------------------------------------------------- /src/hooks/useToggle.ts: -------------------------------------------------------------------------------- 1 | import { useMemo, useState } from 'react'; 2 | 3 | type ToggleHookReturn = [ 4 | boolean, 5 | () => any, 6 | () => any, 7 | () => any, 8 | ] 9 | 10 | /** 11 | * Handle boolean states conveniently 12 | * @param {boolean=false} defaultValue 13 | */ 14 | export const useToggle = (defaultValue = false): ToggleHookReturn => { 15 | const [value, setValue] = useState(defaultValue); 16 | const [setTrue, setFalse, toggleValue] = useMemo(() => [ 17 | setValue.bind(null, true), 18 | setValue.bind(null, false), 19 | () => setValue(value => !value), 20 | ], []); 21 | 22 | return [value, setTrue, setFalse, toggleValue]; 23 | }; 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './core/feature-flags'; 2 | import './assets/styles/global.scss'; 3 | import { LabelStudio } from './LabelStudio'; 4 | 5 | window.LabelStudio = LabelStudio; 6 | 7 | export default LabelStudio; 8 | 9 | export { LabelStudio }; 10 | -------------------------------------------------------------------------------- /src/lib/AudioUltra/Common/Cacheable.ts: -------------------------------------------------------------------------------- 1 | export class Cacheable { 2 | private cache = new Map(); 3 | 4 | createKey(...args: any[]) { 5 | if (args.length === 1) { 6 | return args[0].toString(); 7 | } 8 | 9 | return args.join(':'); 10 | } 11 | 12 | clearCache() { 13 | this.cache.clear(); 14 | } 15 | 16 | cached(key: number|string|Array, fn: () => any) { 17 | const cacheKey = this.createKey(key); 18 | 19 | if (this.cache.has(cacheKey)) { 20 | return this.cache.get(cacheKey); 21 | } 22 | 23 | const result = fn(); 24 | 25 | this.cache.set(cacheKey, result); 26 | 27 | return result; 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/lib/AudioUltra/Common/Destructable.ts: -------------------------------------------------------------------------------- 1 | export class Destructable { 2 | private destroyed = false; 3 | 4 | get isDestroyed() { 5 | return this.destroyed; 6 | } 7 | 8 | destroy() { 9 | this.destroyed = true; 10 | this.destroy = () => null; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/AudioUltra/Common/Style.ts: -------------------------------------------------------------------------------- 1 | export type FontWeight = 'normal'|'bold'|'bolder'|'lighter'|'initial'|'inherit'|'300'|'400'|'500'|'600'|'700'|'800'|'900'; 2 | 3 | export interface Padding { 4 | top?: number; 5 | bottom?: number; 6 | left?: number; 7 | right?: number; 8 | } 9 | -------------------------------------------------------------------------------- /src/lib/AudioUltra/index.ts: -------------------------------------------------------------------------------- 1 | export { Waveform } from './Waveform'; 2 | export * from './Common/Utils'; 3 | -------------------------------------------------------------------------------- /src/mixins/IsReadyMixin.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | 3 | const IsReadyMixin = types.model({}).volatile(() => { 4 | return { 5 | _isReady: true, 6 | }; 7 | }).views(self => ({ 8 | get isReady() { 9 | return self._isReady; 10 | }, 11 | })).actions(self => { 12 | return { 13 | setReady(value) { 14 | self._isReady = value; 15 | }, 16 | }; 17 | }); 18 | 19 | export default IsReadyMixin; 20 | 21 | export const IsReadyWithDepsMixin = IsReadyMixin.views(self => ({ 22 | get isReady() { 23 | return self._isReady && !self.regs?.filter(r => !r.isReady).length; 24 | }, 25 | })); 26 | -------------------------------------------------------------------------------- /src/mixins/LabelMixin.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | 3 | /** 4 | * @todo we didn't need all these methods, so mixin is empty for now. 5 | * Relevant parts of SelectedMixin can be moved here 6 | * to finally split Labels and Choices; so the file left in place. 7 | */ 8 | const LabelMixin = types.model('LabelMixin'); 9 | 10 | export default LabelMixin; 11 | -------------------------------------------------------------------------------- /src/mixins/PerRegionModes.ts: -------------------------------------------------------------------------------- 1 | export const PER_REGION_MODES = { 2 | TAG: 'tag', 3 | REGION_LIST: 'region-list', 4 | }; 5 | -------------------------------------------------------------------------------- /src/mixins/SeparatedControlMixin.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | 3 | const SeparatedControlMixin = types 4 | .model() 5 | .volatile(() => { 6 | return { 7 | isSeparated: true, 8 | }; 9 | }) 10 | .views(self => ({ 11 | get obj() { 12 | return self.annotation?.names.get(self.toname); 13 | }, 14 | 15 | get selectedLabels() { 16 | return []; 17 | }, 18 | selectedValues() { 19 | return []; 20 | }, 21 | getResultValue() { 22 | return {}; 23 | }, 24 | })); 25 | 26 | export default SeparatedControlMixin; 27 | -------------------------------------------------------------------------------- /src/mixins/TagParentMixin.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | import Types from '../core/Types'; 3 | 4 | export const TagParentMixin = types.model('AnnotationMixin', 5 | { 6 | parentTypes: Types.tagsTypes([]), 7 | }).views((self) => ({ 8 | get parent() { 9 | return Types.getParentTagOfTypeString(self, self.parentTypes); 10 | }, 11 | })); 12 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/regions/HyperTextRegion/HyperTextRegion.module.scss: -------------------------------------------------------------------------------- 1 | .htx-highlight, 2 | .annotator-hl { 3 | } 4 | 5 | .htx-highlight:hover { 6 | cursor: pointer; 7 | background-color: red; 8 | } 9 | -------------------------------------------------------------------------------- /src/regions/RegionWrapper.js: -------------------------------------------------------------------------------- 1 | import { observer } from 'mobx-react'; 2 | import { Fragment, useContext } from 'react'; 3 | import { ImageViewContext } from '../components/ImageView/ImageViewContext'; 4 | import { SuggestionControls } from '../components/ImageView/SuggestionControls'; 5 | 6 | export const RegionWrapper = observer(({ item, children }) => { 7 | const { suggestion } = useContext(ImageViewContext) ?? {}; 8 | 9 | return ( 10 | 11 | {children} 12 | {suggestion && ( 13 | 14 | )} 15 | 16 | ); 17 | }); 18 | -------------------------------------------------------------------------------- /src/regions/TextRegion.js: -------------------------------------------------------------------------------- 1 | // stub file to keep TextRegion docs 2 | 3 | /** 4 | * @example 5 | * { 6 | * "value": { 7 | * "start": 2, 8 | * "end": 81, 9 | * "labels": ["Car"] 10 | * } 11 | * } 12 | * @typedef {Object} TextRegionResult 13 | * @property {Object} value 14 | * @property {string} value.start position of the start of the region in characters 15 | * @property {string} value.end position of the end of the region in characters 16 | * @property {string} [value.text] text content of the region, can be skipped 17 | */ 18 | -------------------------------------------------------------------------------- /src/regions/TextRegion/TextRegion.module.scss: -------------------------------------------------------------------------------- 1 | .state { 2 | color: red; 3 | user-select: none; 4 | padding: 2px 0; 5 | font-size: 100%; 6 | top: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Initializing Test Environment 3 | */ 4 | /* global jest, global */ 5 | 6 | import { configure } from 'enzyme'; 7 | import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; 8 | 9 | const localStorageMock = { 10 | getItem: jest.fn(), 11 | setItem: jest.fn(), 12 | removeItem: jest.fn(), 13 | clear: jest.fn(), 14 | }; 15 | 16 | global.localStorage = localStorageMock; 17 | 18 | configure({ adapter: new Adapter() }); 19 | -------------------------------------------------------------------------------- /src/stores/ProjectStore.js: -------------------------------------------------------------------------------- 1 | import { getParent, types } from 'mobx-state-tree'; 2 | 3 | /** 4 | * Project Store 5 | */ 6 | const ProjectStore = types 7 | .model('Project', { 8 | /** 9 | * Project ID 10 | */ 11 | id: types.identifierNumber, 12 | }) 13 | .views(self => ({ 14 | get app() { 15 | return getParent(self); 16 | }, 17 | })); 18 | 19 | export default ProjectStore; 20 | -------------------------------------------------------------------------------- /src/styles/global.module.scss: -------------------------------------------------------------------------------- 1 | .link { 2 | color: #1890ff; 3 | cursor: pointer; 4 | 5 | &:hover { 6 | color: #40a9ff; 7 | } 8 | 9 | &:focus { 10 | color: #1890ff; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tags/TagBase.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | 3 | const BaseTag = types 4 | .model('BaseTag'); 5 | 6 | export { BaseTag }; 7 | -------------------------------------------------------------------------------- /src/tags/control/Choices/Choises.styl: -------------------------------------------------------------------------------- 1 | .choices 2 | margin-top 1em 3 | margin-bottom 1em 4 | 5 | &_hidden 6 | display none -------------------------------------------------------------------------------- /src/tags/control/Labels/Labels.styl: -------------------------------------------------------------------------------- 1 | .labels 2 | margin 1em 0 3 | display flex 4 | justify-content flex-start 5 | align-items center 6 | flex-flow wrap 7 | 8 | &_hidden 9 | display none 10 | 11 | &:not(&_inline) 12 | margin 0 13 | flex-direction column 14 | align-items flex-start 15 | 16 | -------------------------------------------------------------------------------- /src/tags/control/Taxonomy/Taxonomy.styl: -------------------------------------------------------------------------------- 1 | .taxonomy 2 | margin-top 1em 3 | margin-bottom 1em 4 | 5 | &__loading 6 | border 1px solid #d9d9d9 7 | border-radius 6px 8 | height 38px 9 | display flex 10 | align-items center 11 | justify-content center 12 | margin-top 42px 13 | margin-bottom 14px 14 | width 90px 15 | position relative 16 | 17 | & > div 18 | height 14px 19 | 20 | & > div > span 21 | display block 22 | 23 | &__new &__loading 24 | margin-top 0 25 | height 31px 26 | -------------------------------------------------------------------------------- /src/tags/object/AudioNext/constants.ts: -------------------------------------------------------------------------------- 1 | export const WS_ZOOM_X = { 2 | min: 1, 3 | max: 1500, 4 | step: 10, 5 | default: 1, 6 | }; 7 | 8 | export const WS_SPEED = { 9 | min: 0.5, 10 | max: 2, 11 | step: 0.01, 12 | default: 1, 13 | }; 14 | 15 | export const WS_VOLUME = { 16 | min: 0, 17 | max: 1, 18 | step: 0.01, 19 | default: 1, 20 | }; 21 | -------------------------------------------------------------------------------- /src/tags/object/AudioPlus/AudioPlus.module.scss: -------------------------------------------------------------------------------- 1 | .play { 2 | background: #52c41a; 3 | border: 1px solid #52c41a; 4 | 5 | &:hover { 6 | background: #73d13d; 7 | border: 1px solid #73d13d; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/tags/object/AudioUltra/constants.ts: -------------------------------------------------------------------------------- 1 | export const WS_ZOOM_X = { 2 | min: 1, 3 | max: 1500, 4 | step: 10, 5 | default: 1, 6 | }; 7 | 8 | export const WS_SPEED = { 9 | min: 0.5, 10 | max: 2, 11 | step: 0.01, 12 | default: 1, 13 | }; 14 | 15 | export const WS_VOLUME = { 16 | min: 0, 17 | max: 1, 18 | step: 0.01, 19 | default: 1, 20 | }; 21 | -------------------------------------------------------------------------------- /src/tags/object/AudioUltra/view.styl: -------------------------------------------------------------------------------- 1 | .audio-tag 2 | width 100% 3 | -------------------------------------------------------------------------------- /src/tags/object/Image/ImageSelectionPoint.js: -------------------------------------------------------------------------------- 1 | import { types } from 'mobx-state-tree'; 2 | 3 | export const ImageSelectionPoint = types.model({ 4 | x: types.number, 5 | y: types.number, 6 | }); 7 | -------------------------------------------------------------------------------- /src/tags/object/Image/index.js: -------------------------------------------------------------------------------- 1 | export { ImageModel, HtxImage } from './Image'; 2 | -------------------------------------------------------------------------------- /src/tags/object/Paragraphs/index.js: -------------------------------------------------------------------------------- 1 | import Registry from '../../../core/Registry'; 2 | import { ParagraphsModel } from './model'; 3 | import { HtxParagraphs } from './HtxParagraphs'; 4 | 5 | Registry.addTag('paragraphs', ParagraphsModel, HtxParagraphs); 6 | Registry.addObjectType(ParagraphsModel); 7 | 8 | export * from './model'; 9 | export * from './HtxParagraphs'; 10 | -------------------------------------------------------------------------------- /src/tags/object/RichText/index.js: -------------------------------------------------------------------------------- 1 | import { RichTextModel } from './model'; 2 | import { HtxRichText } from './view'; 3 | import Registry from '../../../core/Registry'; 4 | 5 | Registry.addTag('text', RichTextModel, HtxRichText({ isText: true })); 6 | Registry.addTag('hypertext', RichTextModel, HtxRichText({ isText: false })); 7 | Registry.addObjectType(RichTextModel); 8 | 9 | export { RichTextModel, HtxRichText }; 10 | -------------------------------------------------------------------------------- /src/tags/object/Video/index.js: -------------------------------------------------------------------------------- 1 | import { inject, observer } from 'mobx-react'; 2 | import Registry from '../../../core/Registry'; 3 | 4 | import { HtxVideoView } from './HtxVideo'; 5 | import { VideoModel } from './Video'; 6 | 7 | const HtxVideo = inject('store')(observer(HtxVideoView)); 8 | 9 | Registry.addTag('video', VideoModel, HtxVideo); 10 | Registry.addObjectType(VideoModel); 11 | 12 | export { VideoModel, HtxVideo }; 13 | -------------------------------------------------------------------------------- /src/tags/object/Video/types.ts: -------------------------------------------------------------------------------- 1 | import { Shape, ShapeConfig } from 'konva/lib/Shape'; 2 | import { Stage } from 'konva/lib/Stage'; 3 | 4 | export type WorkingArea = { 5 | width: number, 6 | height: number, 7 | x: number, 8 | y: number, 9 | scale: number, 10 | realWidth: number, 11 | realHeight: number, 12 | } 13 | 14 | export type KonvaNode = Shape | Stage; 15 | -------------------------------------------------------------------------------- /src/tags/object/index.js: -------------------------------------------------------------------------------- 1 | import { AudioModel } from './AudioNext'; 2 | import { ImageModel } from './Image'; 3 | import { ParagraphsModel } from './Paragraphs'; 4 | import { RichTextModel } from './RichText'; 5 | import { TableModel } from './Table'; 6 | import { TimeSeriesModel } from './TimeSeries'; 7 | import { PagedViewModel } from './PagedView'; 8 | import { VideoModel } from './Video'; 9 | import { ListModel } from './List'; 10 | 11 | // stub files to keep docs of these tags 12 | import './HyperText'; 13 | import './Text'; 14 | 15 | export { 16 | AudioModel, 17 | ImageModel, 18 | ParagraphsModel, 19 | TimeSeriesModel, 20 | RichTextModel, 21 | VideoModel, 22 | TableModel, 23 | PagedViewModel, 24 | ListModel 25 | }; 26 | -------------------------------------------------------------------------------- /src/tags/visual/__tests__/Header.test.jsx: -------------------------------------------------------------------------------- 1 | /* global test, expect, jest */ 2 | import Enzyme, { render } from "enzyme"; 3 | import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; 4 | import { HtxHeader } from "../Header"; 5 | 6 | Enzyme.configure({ adapter: new Adapter() }); 7 | 8 | jest.mock('react', () => ({ 9 | ...jest.requireActual('react'), 10 | useLayoutEffect: jest.requireActual('react').useEffect, 11 | })); 12 | 13 | test("Header basic test", () => { 14 | const confStore = { 15 | _value: "header text", 16 | underline: true, 17 | size: 1, 18 | }; 19 | 20 | const view = render(); 21 | const text = view.text(); 22 | 23 | expect(text).toBe("header text"); 24 | }); 25 | -------------------------------------------------------------------------------- /src/tags/visual/index.js: -------------------------------------------------------------------------------- 1 | import { CollapseModel } from './Collapse'; 2 | import { DialogModel } from './Dialog'; 3 | import { HeaderModel } from './Header'; 4 | import { ViewModel } from './View'; 5 | import { StyleModel } from './Style'; 6 | import { FilterModel } from './Filter'; 7 | 8 | export { CollapseModel, DialogModel, HeaderModel, ViewModel, StyleModel, FilterModel }; 9 | -------------------------------------------------------------------------------- /src/themes/default/colors.styl: -------------------------------------------------------------------------------- 1 | $black = #000 2 | $accent_color = #0099FF 3 | $danger_color = #d00 4 | $magic_color = #944BFF 5 | 6 | $black_2 = rgba($black, 0.02) 7 | $black_10 = rgba($black, 0.1) 8 | $black_15 = rgba($black, 0.15) 9 | $black_20 = rgba($black, 0.20) 10 | $black_40 = rgba($black, 0.40) 11 | 12 | @require "./tools/*" 13 | -------------------------------------------------------------------------------- /src/themes/default/scrollbar.styl: -------------------------------------------------------------------------------- 1 | styled-scrollbars() 2 | scrollbar-color: rgb(180, 180, 180) transparent; 3 | scrollbar-width: thin; 4 | 5 | &::-webkit-scrollbar 6 | width 4px 7 | height 4px 8 | background-color #fff 9 | 10 | &::-webkit-scrollbar-thumb 11 | background rgb(180, 180, 180) 12 | -------------------------------------------------------------------------------- /src/themes/default/tools/scrollbar.styl: -------------------------------------------------------------------------------- 1 | styled-scrollbars() 2 | scrollbar-color: rgb(180, 180, 180) transparent; 3 | scrollbar-width: thin; 4 | 5 | &::-webkit-scrollbar 6 | width 4px 7 | height 4px 8 | background-color #fff 9 | 10 | &::-webkit-scrollbar-thumb 11 | background rgb(180, 180, 180) 12 | -------------------------------------------------------------------------------- /src/themes/default/tools/waiting.styl: -------------------------------------------------------------------------------- 1 | waiting(c1 = #efefef, c2 = #fff) 2 | $base_color = rgba(c2, 0.2) 3 | $accent_color = c1 4 | 5 | background-image repeating-linear-gradient( 6 | -63.43deg, 7 | $base_color 1px, 8 | $accent_color 2px, 9 | $accent_color 6px, 10 | $base_color 7px, 11 | $base_color 12px 12 | ) 13 | 14 | background-color c2 15 | background-size 37px 100% 16 | 17 | &_animated 18 | animation waiting 1s linear infinite 19 | 20 | @keyframes waiting 21 | 0% 22 | background-position 0 0 23 | 24 | 100% 25 | background-position 37px 0 26 | -------------------------------------------------------------------------------- /src/tools/Tools.module.scss: -------------------------------------------------------------------------------- 1 | .tooltitle { 2 | text-transform: uppercase; 3 | font-weight: bold; 4 | font-size: 8px; 5 | color: #666; 6 | } 7 | -------------------------------------------------------------------------------- /src/tools/index.js: -------------------------------------------------------------------------------- 1 | // export { default as Zoom } from "./Zoom"; 2 | // export { default as KeyPoint } from "./KeyPoint"; 3 | 4 | import { Brush } from './Brush'; 5 | import { Erase } from './Erase'; 6 | import { KeyPoint } from './KeyPoint'; 7 | import { Polygon } from './Polygon'; 8 | import { Rect, Rect3Point } from './Rect'; 9 | import { Ellipse } from './Ellipse'; 10 | import { Zoom } from './Zoom'; 11 | import { Rotate } from './Rotate'; 12 | import { Brightness } from './Brightness'; 13 | import { Contrast } from './Contrast'; 14 | import { MagicWand } from './MagicWand'; 15 | import { Selection } from './Selection'; 16 | 17 | export { Brush, Erase, KeyPoint, Polygon, Rect, Rect3Point, Ellipse, Brightness, Contrast, Rotate, Zoom, MagicWand, Selection }; 18 | -------------------------------------------------------------------------------- /src/utils/__tests__/debounce.test.js: -------------------------------------------------------------------------------- 1 | /* global jest, describe, expect, beforeEach, test */ 2 | import { debounce } from '../debounce'; 3 | 4 | jest.useFakeTimers(); 5 | 6 | describe('Debounce function', () => { 7 | let func; 8 | let debouncedFunc; 9 | 10 | beforeEach(() => { 11 | func = jest.fn(); 12 | debouncedFunc = debounce(func, 1000); 13 | }); 14 | 15 | test('Execute just once', () => { 16 | for (let i = 0; i < 100; i++) { 17 | debouncedFunc(); 18 | } 19 | 20 | jest.runAllTimers(); 21 | 22 | expect(func).toBeCalledTimes(1); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/utils/__tests__/unique.test.js: -------------------------------------------------------------------------------- 1 | /* global it, expect */ 2 | import { guidGenerator } from '../unique'; 3 | 4 | it('Random ID generate', () => { 5 | expect(guidGenerator(10)).toHaveLength(10); 6 | }); 7 | -------------------------------------------------------------------------------- /src/utils/hooks.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export const useRenderTime = name => { 4 | console.time(`RENDER TIME ${name}`); 5 | 6 | React.useEffect(() => console.timeEnd(`RENDER TIME ${name}`), [name]); 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import * as Checkers from './utilities'; 2 | import * as Colors from './colors'; 3 | import * as Magicwand from './magic-wand'; 4 | import * as Image from './image'; 5 | import * as UDate from './date'; 6 | import * as HTML from './html'; 7 | import * as Selection from './selection-tools'; 8 | import { debounce } from './debounce'; 9 | import { guidGenerator } from './unique'; 10 | import { styleToProp } from './styles'; 11 | 12 | export default { 13 | Image, 14 | HTML, 15 | Checkers, 16 | Colors, 17 | UDate, 18 | guidGenerator, 19 | debounce, 20 | styleToProp, 21 | Magicwand, 22 | Selection, 23 | }; 24 | -------------------------------------------------------------------------------- /src/utils/namedColors.ts: -------------------------------------------------------------------------------- 1 | export const colors = { 2 | red: '#F5222D', 3 | volcano: '#FA541C', 4 | orange: '#FA8C16', 5 | gold: '#FAAD14', 6 | yellow: '#FADB14', 7 | lime: '#A0D911', 8 | green: '#52C41A', 9 | cyan: '#13C2C2', 10 | blue: '#1890FF', 11 | geekBlue: '#2F54EB', 12 | purple: '#722ED1', 13 | magenta: '#EB2F96', 14 | accent: '#0099FF', 15 | }; 16 | -------------------------------------------------------------------------------- /src/utils/resize-observer.ts: -------------------------------------------------------------------------------- 1 | class ResizeObserverFallback { 2 | observe() { 3 | 4 | } 5 | unobserve() { 6 | 7 | } 8 | disconnect() { 9 | 10 | } 11 | } 12 | 13 | const ResizeObserver = window.ResizeObserver ?? ResizeObserverFallback; 14 | 15 | export default ResizeObserver; 16 | -------------------------------------------------------------------------------- /src/utils/unique.ts: -------------------------------------------------------------------------------- 1 | // @todo for nanoid@3 there should be default import 2 | import { nanoid } from 'nanoid'; 3 | 4 | /** 5 | * Unique hash generator 6 | * @param {number} lgth 7 | */ 8 | export const guidGenerator = (length = 10) => nanoid(length); 9 | -------------------------------------------------------------------------------- /tests/functional/.gitignore: -------------------------------------------------------------------------------- 1 | output/* 2 | !output/snapshots/ 3 | runner-results/ 4 | xunit.xml 5 | test-results.xml 6 | .nyc_output 7 | -------------------------------------------------------------------------------- /tests/functional/cypress.config.js: -------------------------------------------------------------------------------- 1 | import configure from '@heartexlabs/ls-test'; 2 | 3 | export default configure(); 4 | -------------------------------------------------------------------------------- /tests/functional/cypress/parallel-weights.json: -------------------------------------------------------------------------------- 1 | {"specs/core/feature_flags.cy.ts":{"time":3845,"weight":7},"specs/core/label_studio.cy.ts":{"time":3791,"weight":7},"specs/image_segmentation/basic.cy.ts":{"time":6895,"weight":14}} -------------------------------------------------------------------------------- /tests/functional/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | import { CURRENT_FLAGS } from '../../feature-flags'; 2 | import '@heartexlabs/ls-test/cypress/support/e2e'; 3 | 4 | beforeEach(() => { 5 | cy.on('uncaught:exception', err => { 6 | return !err.message.includes('ResizeObserver loop completed with undelivered notifications.'); 7 | }); 8 | cy.on('window:before:load', (win) => { 9 | console.log('Setting feature flags', CURRENT_FLAGS); 10 | Object.assign(win, { 11 | DISABLE_DEFAULT_LSF_INIT: true, 12 | APP_SETTINGS: { 13 | ...(win.APP_SETTINGS ?? {}), 14 | feature_flags: CURRENT_FLAGS, 15 | }, 16 | }); 17 | }); 18 | cy.log('Feature flags set'); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/functional/data/core/hotkeys.ts: -------------------------------------------------------------------------------- 1 | export const createConfigWithHotkey = (hotkey: string) => ` 2 | 3 | 4 | 6 | `; 7 | -------------------------------------------------------------------------------- /tests/functional/data/image_segmentation/crosshair.ts: -------------------------------------------------------------------------------- 1 | export const crosshairConfig = ` 2 | 3 | 4 | 5 | `; 6 | -------------------------------------------------------------------------------- /tests/functional/data/image_segmentation/tools/magic-wand.ts: -------------------------------------------------------------------------------- 1 | export const magicWandConfig = ` 2 | 3 | 4 | 5 | 6 | `; 7 | 8 | export const magicWandImageData = { 9 | image: 'https://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_scale_1_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg', 10 | }; 11 | -------------------------------------------------------------------------------- /tests/functional/data/image_segmentation/tools/rect3point.ts: -------------------------------------------------------------------------------- 1 | export const rect3Config = ` 2 | 3 | 4 | 5 | 6 | `; 7 | 8 | export const simpleImageData = { 9 | image: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg', 10 | }; -------------------------------------------------------------------------------- /tests/functional/data/image_segmentation/tools/zoom.ts: -------------------------------------------------------------------------------- 1 | export const simpleConfig = ` 2 | 3 | 4 | 5 | 7 | 8 | `; 9 | 10 | export const simpleImageData = { 11 | image: 'https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg', 12 | }; -------------------------------------------------------------------------------- /tests/functional/feature-flags.ts: -------------------------------------------------------------------------------- 1 | import * as FLAGS from '../../src/utils/feature-flags'; 2 | 3 | export const CURRENT_FLAGS = { 4 | [FLAGS.FF_DEV_1284]: true, 5 | [FLAGS.FF_DEV_1170]: true, 6 | [FLAGS.FF_PROD_309]: true, 7 | [FLAGS.FF_LSDV_4930]: true, 8 | [FLAGS.FF_LSDV_4992]: true, 9 | [FLAGS.FF_LSDV_4673]: true, 10 | [FLAGS.FF_DEV_2100]: true, 11 | [FLAGS.FF_DEV_2715]: true, 12 | [FLAGS.FF_LSDV_4620_3]: true, 13 | [FLAGS.FF_LSDV_4620_3_ML]: true, 14 | [FLAGS.FF_OUTLINER_OPTIM]: true, 15 | [FLAGS.FF_DBLCLICK_DELAY]: true, 16 | [FLAGS.FF_ZOOM_OPTIM]: true, 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /tests/functional/multi-reporter-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporterEnabled": "cypress-parallel/json-stream.reporter.js, spec" 3 | } -------------------------------------------------------------------------------- /tests/functional/output/snapshots/Audio -- Renders audio with merged channels by default.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/Audio -- Renders audio with merged channels by default.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/Audio -- Renders separate audio channels with splitchannels=true.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/Audio -- Renders separate audio channels with splitchannels=true.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/Audio Paragraphs Sync -- Highlights the correct Audio segment whenever it is played or seeked.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/Audio Paragraphs Sync -- Highlights the correct Audio segment whenever it is played or seeked.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/Audio Paragraphs Sync -- Correctly loads with Paragraph segments as Audio segments.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/HighlightAfterFinishedPlayback.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/HighlightAfterFinishedPlayback.snap.png -------------------------------------------------------------------------------- /tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/HighlightOnFirstSeek.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HumanSignal/label-studio-frontend/4bc1393620845c06c5b59ccd643580ab5c0e6e67/tests/functional/output/snapshots/audio/audio_paragraphs.cy.ts/HighlightOnFirstSeek.snap.png -------------------------------------------------------------------------------- /tests/functional/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "baseUrl": "./", 5 | "noImplicitThis": true, 6 | "lib": ["es5", "dom"], 7 | "types": ["cypress", "node"] 8 | }, 9 | "include": [ 10 | "./node_modules/cypress", 11 | "**/*.ts", 12 | "./node_modules/@heartexlabs/ls-test/**/*.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "allowJs": true, 6 | "checkJs": false, 7 | "jsx": "react", 8 | "strict": true, 9 | "rootDirs": ["./src"], 10 | "typeRoots": ["./types", "./node_modules/@types"], 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "forceConsistentCasingInFileNames": true, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "allowJs": false, 6 | "checkJs": false, 7 | "jsx": "preserve", 8 | "strict": true, 9 | "rootDirs": ["./src"], 10 | "typeRoots": ["./types", "./node_modules/@types"], 11 | "esModuleInterop": true, 12 | "skipLibCheck": true, 13 | "forceConsistentCasingInFileNames": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /types/Global.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentClass, FC, FunctionComponent, ReactHTML, ReactSVG } from "react"; 2 | 3 | declare type AnyComponent = FC | keyof ReactHTML | keyof ReactSVG | ComponentClass | FunctionComponent | string 4 | 5 | declare global { 6 | interface Window { 7 | APP_SETTINGS: Record; 8 | FEATURE_FLAGS?: Record; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /types/Keymap.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Hotkey { 2 | description: string; 3 | key: string; 4 | mac?: string; 5 | modifier?: string; 6 | modifierDescription?: string; 7 | } 8 | 9 | declare interface Keymap { 10 | [key: string]: Hotkey; 11 | } 12 | 13 | declare module '*/keymap.json' { 14 | export type K = Keymap; 15 | } 16 | -------------------------------------------------------------------------------- /types/React.d.ts: -------------------------------------------------------------------------------- 1 | import "react"; 2 | 3 | type CustomProp = { [key in `--${string}`]: string }; 4 | 5 | declare module "react" { 6 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 7 | export interface CSSProperties extends CustomProp {} 8 | } 9 | -------------------------------------------------------------------------------- /types/Regions.d.ts: -------------------------------------------------------------------------------- 1 | declare interface LSFResult { 2 | type: string; 3 | from_name: any; // tag on page 4 | to_name: any; // tag on page 5 | value: Record; 6 | mainValue: any; 7 | } 8 | 9 | declare interface LSFRegion { 10 | results: LSFResult[]; 11 | } 12 | -------------------------------------------------------------------------------- /types/shallow-equal.d.ts: -------------------------------------------------------------------------------- 1 | declare module "shallow-equal" { 2 | export function shallowEqualArrays(arr1: any[], arr2: any[]): boolean; 3 | export function shallowEqualObjects(arr1: Record, arr2: Record): boolean; 4 | } 5 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./webpack.config-builder")(); 2 | --------------------------------------------------------------------------------