Bin
2025-12-16 9e0b2ba2c317b1a86212f24cbae3195ad1f3dbfa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { memo, useCallback } from "react";
import { ThemeToggle, IconLink, IconCopyOutline, Tooltip } from "@humansignal/ui";
import { useAtomValue } from "jotai";
import { configAtom } from "../../atoms/configAtoms";
import { getParentUrl } from "../../utils/url";
import { useCopyText } from "../../hooks/useCopyText";
 
const ShareUrlButton = () => {
  const config = useAtomValue(configAtom);
  const copyText = useCopyText({
    successMessage: "URL copied to clipboard",
    errorMessage: "Failed to copy URL to clipboard",
  });
 
  const handleCopy = useCallback(() => {
    const url = getParentUrl();
    url.searchParams.set("config", encodeURIComponent(config.replace(/\n/g, "<br>")));
    copyText(url.toString());
  }, [config, copyText]);
 
  return (
    <Tooltip title="Share labeling config URL">
      <button
        type="button"
        className="flex items-center justify-center h-8 w-8 gap-2 border border-neutral-border rounded-md"
        aria-label="Share labeling config URL"
        onClick={handleCopy}
      >
        <IconLink width={22} height={22} className="flex-shrink-0" />
      </button>
    </Tooltip>
  );
};
 
const CopyButton = () => {
  const config = useAtomValue(configAtom);
  const copyText = useCopyText({
    successMessage: "Config copied to clipboard",
    errorMessage: "Failed to copy config to clipboard",
  });
 
  const handleCopy = useCallback(() => {
    copyText(config);
  }, [config, copyText]);
 
  return (
    <Tooltip title="Copy labeling config">
      <button
        type="button"
        className="flex items-center justify-center h-8 w-8 gap-2 border border-neutral-border rounded-md"
        aria-label="Copy labeling config"
        onClick={handleCopy}
      >
        <IconCopyOutline width={18} height={18} className="flex-shrink-0" />
      </button>
    </Tooltip>
  );
};
 
const ShareButtons = () => {
  return (
    <div className="flex items-center gap-2">
      <CopyButton />
      <ShareUrlButton />
    </div>
  );
};
 
export const TopBar = memo(
  () => {
    return (
      <div className="flex items-center h-10 px-tight text-heading-medium justify-between select-none border-b border-neutral-border">
        <div className="flex items-center gap-2">
          <span className="font-semibold tracking-tight text-body-medium">
            Label Studio <span className="text-accent-persimmon-base">Playground</span>
          </span>
        </div>
        <div className="flex items-center gap-4">
          <ShareButtons />
          <ThemeToggle />
        </div>
      </div>
    );
  },
  () => true,
);