Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- In the NML upload route, the additional form field `description` can be specified. If so, it will overwrite the description contained in the NML files. [#8631](https://github.com/scalableminds/webknossos/pull/8631)
- Added the possibility for super users to retry manually cancelled jobs from the jobs list. [#8629](https://github.com/scalableminds/webknossos/pull/8629)
- Added checkboxes to the segments tab that allow to show/hide individual segments. The visibility of segments that are not listed in the segments list can be controlled with a new "Hide unlisted segments" toggle in the layer settings. Additionally, you can right-click a segment in a data viewport and select "Only show this segment" (and similar functionality). [#8546](https://github.com/scalableminds/webknossos/pull/8546)
- Instead of pasting a dataset position from the clipboard to the position input box, you can simply paste it without focussing the position input first. Furthermore, you can also paste a "hash string", such as `#1406,1794,1560,0,0.234,186`, as it can be found in WK URLs. Pasting such a string will also set the encoded zoom, rotation, viewport etc. Note that the `#` has to be included in the pasted text. You can also copy and paste an entire link, but note that the dataset or annotation id in the link will be ignored. [#8652](https://github.com/scalableminds/webknossos/pull/8652)

### Changed
- Remove `data.maybe` dependency and replaced with regular Typescript types. [#8563](https://github.com/scalableminds/webknossos/pull/8563)
Expand Down
13 changes: 9 additions & 4 deletions frontend/javascripts/viewer/controller/url_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,17 @@ class UrlManager {
}

onHashChange = () => {
const urlState = this.parseUrlHash();
applyState(urlState);
this.updateToHash();
};

parseUrlHash(): PartialUrlManagerState {
const urlHash = decodeURIComponent(location.hash.slice(1));
updateToHash(optionalHash?: string) {
const urlState = this.parseUrlHash(optionalHash);
applyState(urlState);
}

parseUrlHash(optionalHash?: string): PartialUrlManagerState {
// Cut off the #
const urlHash = decodeURIComponent(optionalHash ?? location.hash.slice(1));

if (urlHash.includes("{")) {
// The hash is in json format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { Dispatch } from "redux";
import { NavAndStatusBarTheme } from "theme";
import type { APICompoundType } from "types/api_types";
import CrossOriginApi from "viewer/api/cross_origin_api";
import Constants from "viewer/constants";
import Constants, { type Vector3 } from "viewer/constants";
import type { ControllerStatus } from "viewer/controller";
import WebKnossosController from "viewer/controller";
import MergerModeController from "viewer/controller/merger_mode_controller";
Expand All @@ -24,6 +24,7 @@ import { cancelSagaAction } from "viewer/model/actions/actions";
import { resetStoreAction } from "viewer/model/actions/actions";
import { updateUserSettingAction } from "viewer/model/actions/settings_actions";
import rootSaga from "viewer/model/sagas/root_saga";
import { applyState } from "viewer/model_initialization";
import { Store } from "viewer/singletons";
import { Model } from "viewer/singletons";
import { type Theme, type TraceOrViewCommand, type WebknossosState, startSaga } from "viewer/store";
Expand Down Expand Up @@ -114,9 +115,11 @@ class TracingLayoutView extends React.PureComponent<PropsWithRouter, State> {

componentDidMount() {
startSaga(rootSaga);
document.addEventListener("paste", this.onPaste);
}

componentWillUnmount() {
document.removeEventListener("paste", this.onPaste);
UrlManager.stopUrlUpdater();
Model.reset();
destroySceneController();
Expand Down Expand Up @@ -261,6 +264,38 @@ class TracingLayoutView extends React.PureComponent<PropsWithRouter, State> {
getLayoutNamesFromCurrentView = (layoutKey): Array<string> =>
this.props.storedLayouts[layoutKey] ? Object.keys(this.props.storedLayouts[layoutKey]) : [];

onPaste = (event: ClipboardEvent) => {
const target = event.target as HTMLElement;

// Check if the target is an editable input or textarea
const isFormField =
target?.tagName === "INPUT" || target?.tagName === "TEXTAREA" || target?.isContentEditable;

if (isFormField) {
// Let the browser handle the paste normally
return;
}

// Otherwise, prevent default and handle paste
event.preventDefault();

const pastedText = event.clipboardData?.getData("text");

if (!pastedText) {
return;
}

const hashPos = pastedText.indexOf("#");
if (hashPos > -1) {
UrlManager.updateToHash(pastedText.slice(hashPos + 1));
} else {
const numbers = pastedText.split(",").map(Number);
if (numbers.length === 3 && !numbers.some(isNaN)) {
applyState({ position: numbers as Vector3 });
}
}
};

render() {
if (this.state.hasError) {
return (
Expand Down
Loading