tmp/webapp-docs/frontend/components/WrapperFab.js

/**
 * The "wrapper" control: a floating bottom-right button that frames every generated prompt
 * with a START and an END snippet (the v3 root layer = open + middle + close). Clicking it
 * opens a small popover listing saved wrapper presets (apply one, or "None"); a "Manage
 * presets" button there opens a modal with the two side-by-side Start/End editors plus
 * preset naming/management. The selected wrapper lives in `settings.wrapper` (so it is
 * shared by the share-link); the preset library lives in localStorage.
 * @module gui/components/WrapperFab
 */
import { useIntl, defineMessages } from "react-intl";
import DplEditor from "./DplEditor.jsx";
import { useWrapperPresets } from "../lib/wrapper/useWrapperPresets.js";
import { jsxDEV as _jsxDEV, Fragment as _Fragment } from "react/jsx-dev-runtime";
const ico = {
  width: 18,
  height: 18,
  viewBox: "0 0 24 24",
  fill: "none",
  stroke: "currentColor",
  strokeWidth: 2,
  strokeLinecap: "round",
  strokeLinejoin: "round"
};
// Brackets — a "wraps around your prompt" glyph.
const WrapIcon = () => /*#__PURE__*/_jsxDEV("svg", {
  ...ico,
  "aria-hidden": "true",
  children: [/*#__PURE__*/_jsxDEV("path", {
    d: "M8 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h2"
  }, void 0, false), /*#__PURE__*/_jsxDEV("path", {
    d: "M16 4h2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-2"
  }, void 0, false)]
}, void 0, true);
// Counter-clockwise arrow — "revert to default".
const RevertIcon = () => /*#__PURE__*/_jsxDEV("svg", {
  ...ico,
  width: 15,
  height: 15,
  "aria-hidden": "true",
  children: [/*#__PURE__*/_jsxDEV("path", {
    d: "M3 4v6h6"
  }, void 0, false), /*#__PURE__*/_jsxDEV("path", {
    d: "M3.5 10a8 8 0 1 1-.5 6"
  }, void 0, false)]
}, void 0, true);
const msgs = defineMessages({
  triggerTitle: {
    id: "wrapper.triggerTitle",
    defaultMessage: "Wrapper: {name} — frames every prompt with a start and end"
  },
  presetsAria: {
    id: "wrapper.presetsAria",
    defaultMessage: "Wrapper presets"
  },
  wrapperHead: {
    id: "wrapper.head",
    defaultMessage: "Wrapper"
  },
  manage: {
    id: "wrapper.manage",
    defaultMessage: "Manage Wrappers"
  },
  autoToggleTitle: {
    id: "wrapper.autoToggleTitle",
    defaultMessage: "When on, blocks may add their own Auto Begin / Auto End framing to the prompt. When off, only this wrapper frames it."
  },
  autoToggleLabel: {
    id: "wrapper.autoToggleLabel",
    defaultMessage: "Allow used blocks to extend current wrapper"
  },
  nameDefault: {
    id: "wrapper.nameDefault",
    defaultMessage: "Default"
  },
  nameNone: {
    id: "wrapper.nameNone",
    defaultMessage: "None"
  },
  noSaved: {
    id: "wrapper.noSaved",
    defaultMessage: "No saved wrappers — Manage presets to add one."
  },
  manageAria: {
    id: "wrapper.manageAria",
    defaultMessage: "Manage wrappers"
  },
  close: {
    id: "wrapper.close",
    defaultMessage: "Close"
  },
  editDefault: {
    id: "wrapper.editDefault",
    defaultMessage: "Edit the Default wrapper"
  },
  editPreset: {
    id: "wrapper.editPreset",
    defaultMessage: "Edit “{name}”"
  },
  deletePreset: {
    id: "wrapper.deletePreset",
    defaultMessage: "Delete “{name}”"
  },
  deletePresetAria: {
    id: "wrapper.deletePresetAria",
    defaultMessage: "Delete {name}"
  },
  newPreset: {
    id: "wrapper.newPreset",
    defaultMessage: "+ New"
  },
  namePlaceholder: {
    id: "wrapper.namePlaceholder",
    defaultMessage: "Wrapper name…"
  },
  nameAria: {
    id: "wrapper.nameAria",
    defaultMessage: "Wrapper name"
  },
  start: {
    id: "wrapper.start",
    defaultMessage: "Start"
  },
  end: {
    id: "wrapper.end",
    defaultMessage: "End"
  },
  revertStartDefault: {
    id: "wrapper.revertStartDefault",
    defaultMessage: "Reset Start to the built-in default"
  },
  revertStartNormal: {
    id: "wrapper.revertStartNormal",
    defaultMessage: "Reset Start to the default wrapper"
  },
  revertEndDefault: {
    id: "wrapper.revertEndDefault",
    defaultMessage: "Reset End to the built-in default"
  },
  revertEndNormal: {
    id: "wrapper.revertEndNormal",
    defaultMessage: "Reset End to the default wrapper"
  },
  revertStartAria: {
    id: "wrapper.revertStartAria",
    defaultMessage: "Revert Start to default"
  },
  revertEndAria: {
    id: "wrapper.revertEndAria",
    defaultMessage: "Revert End to default"
  },
  startAria: {
    id: "wrapper.startAria",
    defaultMessage: "Wrapper start (DPL)"
  },
  endAria: {
    id: "wrapper.endAria",
    defaultMessage: "Wrapper end (DPL)"
  },
  startPlaceholderNew: {
    id: "wrapper.startPlaceholderNew",
    defaultMessage: "Rendered before the prompt…"
  },
  startPlaceholder: {
    id: "wrapper.startPlaceholder",
    defaultMessage: "Rendered before the prompt — e.g. a style or framing lead-in"
  },
  endPlaceholderNew: {
    id: "wrapper.endPlaceholderNew",
    defaultMessage: "Rendered after the prompt…"
  },
  endPlaceholder: {
    id: "wrapper.endPlaceholder",
    defaultMessage: "Rendered after the prompt — e.g. '{#fx}', '{#artists}'"
  },
  footHint: {
    id: "wrapper.footHint",
    defaultMessage: "Wraps every generated prompt: start, your prompt, end."
  },
  cancel: {
    id: "wrapper.cancel",
    defaultMessage: "Cancel"
  },
  save: {
    id: "wrapper.save",
    defaultMessage: "Save"
  }
});

/**
 * @param {object} props
 * @param {object} props.settings Current settings (reads `wrapper` / `wrapperName`).
 * @param {Function} props.setSettings Update settings.
 * @returns {JSX.Element}
 */
export default function WrapperFab({
  settings,
  setSettings
}) {
  const intl = useIntl();
  // The built-in "Default" / "None" presets are stored under those literal keys but shown
  // localized; custom preset names are user content and shown verbatim.
  const displayName = n => n === "Default" ? intl.formatMessage(msgs.nameDefault) : n === "None" ? intl.formatMessage(msgs.nameNone) : n;
  const {
    view,
    setView,
    pos,
    btnRef,
    openList,
    names,
    activeName,
    isActive,
    def,
    isNew,
    sel,
    name,
    setName,
    start,
    setStart,
    end,
    setEnd,
    applyWrapper,
    openManage,
    editPreset,
    newPreset,
    save,
    del,
    revertPane
  } = useWrapperPresets({
    settings,
    setSettings
  });
  return /*#__PURE__*/_jsxDEV(_Fragment, {
    children: [/*#__PURE__*/_jsxDEV("button", {
      ref: btnRef,
      className: `field-act wrap-trigger${isActive ? " on" : ""}`,
      onClick: () => view === "list" ? setView("") : openList(),
      title: intl.formatMessage(msgs.triggerTitle, {
        name: displayName(activeName)
      }),
      "aria-label": intl.formatMessage(msgs.presetsAria),
      "aria-pressed": view === "list",
      children: /*#__PURE__*/_jsxDEV(WrapIcon, {}, void 0, false)
    }, void 0, false), view === "list" && /*#__PURE__*/_jsxDEV(_Fragment, {
      children: [/*#__PURE__*/_jsxDEV("div", {
        className: "wrap-pop-scrim",
        onClick: () => setView("")
      }, void 0, false), /*#__PURE__*/_jsxDEV("div", {
        className: "wrap-pop",
        role: "menu",
        "aria-label": intl.formatMessage(msgs.presetsAria),
        style: pos ? {
          position: "fixed",
          right: pos.right,
          bottom: pos.bottom,
          maxHeight: pos.maxHeight
        } : undefined,
        children: [/*#__PURE__*/_jsxDEV("div", {
          className: "wrap-pop-head",
          children: [/*#__PURE__*/_jsxDEV("span", {
            children: intl.formatMessage(msgs.wrapperHead)
          }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
            className: "wrap-manage-btn",
            onClick: () => openManage(),
            children: intl.formatMessage(msgs.manage)
          }, void 0, false)]
        }, void 0, true), /*#__PURE__*/_jsxDEV("label", {
          className: "wrap-auto-toggle",
          title: intl.formatMessage(msgs.autoToggleTitle),
          children: [/*#__PURE__*/_jsxDEV("input", {
            type: "checkbox",
            checked: settings.useAutoSections !== false,
            onChange: e => setSettings({
              ...settings,
              useAutoSections: e.target.checked
            })
          }, void 0, false), /*#__PURE__*/_jsxDEV("span", {
            children: intl.formatMessage(msgs.autoToggleLabel)
          }, void 0, false)]
        }, void 0, true), /*#__PURE__*/_jsxDEV("div", {
          className: "wrap-pop-list",
          children: [/*#__PURE__*/_jsxDEV("button", {
            className: `wrap-pop-item${activeName === "Default" ? " on" : ""}`,
            onClick: () => applyWrapper("Default"),
            children: [/*#__PURE__*/_jsxDEV("span", {
              className: "wrap-pop-name",
              children: intl.formatMessage(msgs.nameDefault)
            }, void 0, false), activeName === "Default" && /*#__PURE__*/_jsxDEV("span", {
              className: "wrap-check",
              children: "✓"
            }, void 0, false)]
          }, void 0, true), /*#__PURE__*/_jsxDEV("button", {
            className: `wrap-pop-item${activeName === "None" ? " on" : ""}`,
            onClick: () => applyWrapper("None"),
            children: [/*#__PURE__*/_jsxDEV("span", {
              className: "wrap-pop-name",
              children: intl.formatMessage(msgs.nameNone)
            }, void 0, false), activeName === "None" && /*#__PURE__*/_jsxDEV("span", {
              className: "wrap-check",
              children: "✓"
            }, void 0, false)]
          }, void 0, true), names.length === 0 && /*#__PURE__*/_jsxDEV("p", {
            className: "wrap-empty",
            children: intl.formatMessage(msgs.noSaved)
          }, void 0, false), names.map(n => /*#__PURE__*/_jsxDEV("button", {
            className: `wrap-pop-item${activeName === n ? " on" : ""}`,
            onClick: () => applyWrapper(n),
            children: [/*#__PURE__*/_jsxDEV("span", {
              className: "wrap-pop-name",
              children: n
            }, void 0, false), activeName === n && /*#__PURE__*/_jsxDEV("span", {
              className: "wrap-check",
              children: "✓"
            }, void 0, false)]
          }, n, true))]
        }, void 0, true)]
      }, void 0, true)]
    }, void 0, true), view === "manage" && /*#__PURE__*/_jsxDEV(_Fragment, {
      children: [/*#__PURE__*/_jsxDEV("div", {
        className: "wrap-modal-overlay",
        onClick: () => setView("list")
      }, void 0, false), /*#__PURE__*/_jsxDEV("div", {
        className: "wrap-modal",
        role: "dialog",
        "aria-label": intl.formatMessage(msgs.manageAria),
        children: [/*#__PURE__*/_jsxDEV("div", {
          className: "wrap-modal-head",
          children: [/*#__PURE__*/_jsxDEV("h2", {
            children: intl.formatMessage(msgs.manage)
          }, void 0, false), /*#__PURE__*/_jsxDEV("div", {
            className: "grow"
          }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
            className: "drawer-close",
            onClick: () => setView("list"),
            "aria-label": intl.formatMessage(msgs.close),
            children: "×"
          }, void 0, false)]
        }, void 0, true), /*#__PURE__*/_jsxDEV("div", {
          className: "wrap-modal-body",
          children: [/*#__PURE__*/_jsxDEV("div", {
            className: "wrap-presets",
            children: [/*#__PURE__*/_jsxDEV("div", {
              className: "wrap-preset-pills",
              children: [/*#__PURE__*/_jsxDEV("span", {
                className: `wrap-preset-pill${sel === "Default" ? " on" : ""}`,
                children: /*#__PURE__*/_jsxDEV("button", {
                  className: "wrap-preset-pick",
                  onClick: () => editPreset("Default"),
                  title: intl.formatMessage(msgs.editDefault),
                  children: intl.formatMessage(msgs.nameDefault)
                }, void 0, false)
              }, void 0, false), names.map(n => /*#__PURE__*/_jsxDEV("span", {
                className: `wrap-preset-pill${sel === n ? " on" : ""}`,
                children: [/*#__PURE__*/_jsxDEV("button", {
                  className: "wrap-preset-pick",
                  onClick: () => editPreset(n),
                  title: intl.formatMessage(msgs.editPreset, {
                    name: n
                  }),
                  children: n
                }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
                  className: "wrap-preset-del",
                  onClick: () => del(n),
                  title: intl.formatMessage(msgs.deletePreset, {
                    name: n
                  }),
                  "aria-label": intl.formatMessage(msgs.deletePresetAria, {
                    name: n
                  }),
                  children: "×"
                }, void 0, false)]
              }, n, true)), /*#__PURE__*/_jsxDEV("button", {
                className: "wrap-preset-new",
                onClick: newPreset,
                children: intl.formatMessage(msgs.newPreset)
              }, void 0, false)]
            }, void 0, true), /*#__PURE__*/_jsxDEV("input", {
              className: "panel-input wrap-name",
              placeholder: intl.formatMessage(msgs.namePlaceholder),
              value: name,
              onChange: e => setName(e.target.value),
              readOnly: sel === "Default",
              "aria-label": intl.formatMessage(msgs.nameAria)
            }, void 0, false)]
          }, void 0, true), /*#__PURE__*/_jsxDEV("div", {
            className: "wrap-grid",
            children: [/*#__PURE__*/_jsxDEV("label", {
              className: "wrap-box",
              children: [/*#__PURE__*/_jsxDEV("span", {
                className: "wrap-box-head",
                children: [/*#__PURE__*/_jsxDEV("span", {
                  className: "wrap-box-label",
                  children: intl.formatMessage(msgs.start)
                }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
                  type: "button",
                  className: "wrap-revert",
                  onClick: () => revertPane("start"),
                  title: intl.formatMessage(sel === "Default" ? msgs.revertStartDefault : msgs.revertStartNormal),
                  "aria-label": intl.formatMessage(msgs.revertStartAria),
                  children: /*#__PURE__*/_jsxDEV(RevertIcon, {}, void 0, false)
                }, void 0, false)]
              }, void 0, true), /*#__PURE__*/_jsxDEV(DplEditor, {
                value: start,
                onChange: setStart,
                settings: settings,
                ariaLabel: intl.formatMessage(msgs.startAria),
                placeholder: isNew ? def.start || intl.formatMessage(msgs.startPlaceholderNew) : intl.formatMessage(msgs.startPlaceholder)
              }, void 0, false)]
            }, void 0, true), /*#__PURE__*/_jsxDEV("label", {
              className: "wrap-box",
              children: [/*#__PURE__*/_jsxDEV("span", {
                className: "wrap-box-head",
                children: [/*#__PURE__*/_jsxDEV("span", {
                  className: "wrap-box-label",
                  children: intl.formatMessage(msgs.end)
                }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
                  type: "button",
                  className: "wrap-revert",
                  onClick: () => revertPane("end"),
                  title: intl.formatMessage(sel === "Default" ? msgs.revertEndDefault : msgs.revertEndNormal),
                  "aria-label": intl.formatMessage(msgs.revertEndAria),
                  children: /*#__PURE__*/_jsxDEV(RevertIcon, {}, void 0, false)
                }, void 0, false)]
              }, void 0, true), /*#__PURE__*/_jsxDEV(DplEditor, {
                value: end,
                onChange: setEnd,
                settings: settings,
                ariaLabel: intl.formatMessage(msgs.endAria),
                placeholder: isNew ? def.end || intl.formatMessage(msgs.endPlaceholderNew) : intl.formatMessage(msgs.endPlaceholder)
              }, void 0, false)]
            }, void 0, true)]
          }, void 0, true)]
        }, void 0, true), /*#__PURE__*/_jsxDEV("div", {
          className: "wrap-modal-foot",
          children: [/*#__PURE__*/_jsxDEV("span", {
            className: "wrap-foot-hint",
            children: intl.formatMessage(msgs.footHint)
          }, void 0, false), /*#__PURE__*/_jsxDEV("div", {
            className: "grow"
          }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
            className: "ghost",
            onClick: () => setView("list"),
            children: intl.formatMessage(msgs.cancel)
          }, void 0, false), /*#__PURE__*/_jsxDEV("button", {
            className: "primary",
            onClick: save,
            disabled: !name.trim(),
            children: intl.formatMessage(msgs.save)
          }, void 0, false)]
        }, void 0, true)]
      }, void 0, true)]
    }, void 0, true)]
  }, void 0, true);
}