@gongfu/prompt-editor
Version:
A powerful prompt engineering editor SDK for AI applications
1,203 lines (1,183 loc) • 141 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client"
// src/components/PromptEditor.tsx
var _react = require('react');
var _react3 = require('@monaco-editor/react'); var _react4 = _interopRequireDefault(_react3);
var _framermotion = require('framer-motion');
// src/hooks/usePromptEditorStore.ts
var _zustand = require('zustand');
var _immer = require('zustand/middleware/immer');
var initialTemplate = {
id: "",
name: "Untitled Prompt",
content: "",
variables: [],
tags: []
};
var usePromptEditorStore = _zustand.create.call(void 0, )(
_immer.immer.call(void 0, (set2, get) => ({
// State
template: initialTemplate,
isDirty: false,
isValid: true,
errors: {},
history: [initialTemplate],
historyIndex: 0,
selectedVariableId: void 0,
previewValues: {},
// Actions
updateTemplate: (updates) => set2((state) => {
Object.assign(state.template, updates);
state.isDirty = true;
addToHistory(state);
}),
updateContent: (content) => set2((state) => {
state.template.content = content;
state.isDirty = true;
addToHistory(state);
}),
addVariable: (variable) => set2((state) => {
if (state.template.variables.some((v) => v.name === variable.name)) {
state.errors[variable.name] = "Variable name already exists";
return;
}
state.template.variables.push(variable);
state.isDirty = true;
state.errors = {};
addToHistory(state);
}),
updateVariable: (name, updates) => set2((state) => {
const index = state.template.variables.findIndex((v) => v.name === name);
if (index !== -1) {
if (updates.name && updates.name !== name) {
const nameExists = state.template.variables.some(
(v, i) => i !== index && v.name === updates.name
);
if (nameExists) {
state.errors[name] = "Variable name already exists";
return;
}
}
Object.assign(state.template.variables[index], updates);
state.isDirty = true;
state.errors = {};
addToHistory(state);
}
}),
removeVariable: (name) => set2((state) => {
state.template.variables = state.template.variables.filter((v) => v.name !== name);
delete state.previewValues[name];
state.isDirty = true;
addToHistory(state);
}),
setPreviewValue: (name, value) => set2((state) => {
state.previewValues[name] = value;
}),
undo: () => set2((state) => {
if (state.historyIndex > 0) {
state.historyIndex--;
state.template = JSON.parse(JSON.stringify(state.history[state.historyIndex]));
state.isDirty = state.historyIndex !== 0;
}
}),
redo: () => set2((state) => {
if (state.historyIndex < state.history.length - 1) {
state.historyIndex++;
state.template = JSON.parse(JSON.stringify(state.history[state.historyIndex]));
state.isDirty = true;
}
}),
reset: () => set2((state) => {
state.template = initialTemplate;
state.isDirty = false;
state.isValid = true;
state.errors = {};
state.history = [initialTemplate];
state.historyIndex = 0;
state.previewValues = {};
}),
loadTemplate: (template) => set2((state) => {
state.template = template;
state.isDirty = false;
state.isValid = true;
state.errors = {};
state.history = [template];
state.historyIndex = 0;
state.previewValues = {};
template.variables.forEach((variable) => {
if (variable.defaultValue !== void 0) {
state.previewValues[variable.name] = variable.defaultValue;
}
});
}),
save: async () => {
const state = get();
if (state.isValid) {
set2((state2) => {
state2.isDirty = false;
state2.template.updatedAt = /* @__PURE__ */ new Date();
});
}
},
validate: () => {
const state = get();
const errors = {};
let isValid = true;
if (!state.template.name || state.template.name.trim() === "") {
errors.name = "Template name is required";
isValid = false;
}
const variableNames = /* @__PURE__ */ new Set();
state.template.variables.forEach((variable, index) => {
if (!variable.name || variable.name.trim() === "") {
errors[`variable_${index}`] = "Variable name is required";
isValid = false;
} else if (variableNames.has(variable.name)) {
errors[`variable_${index}`] = "Duplicate variable name";
isValid = false;
} else {
variableNames.add(variable.name);
}
if (_optionalChain([variable, 'access', _ => _.validation, 'optionalAccess', _2 => _2.pattern])) {
try {
new RegExp(variable.validation.pattern);
} catch (e2) {
errors[`variable_${index}_pattern`] = "Invalid regular expression";
isValid = false;
}
}
});
set2((state2) => {
state2.errors = errors;
state2.isValid = isValid;
});
return isValid;
}
}))
);
function addToHistory(state) {
if (state.historyIndex < state.history.length - 1) {
state.history = state.history.slice(0, state.historyIndex + 1);
}
const newHistory = JSON.parse(JSON.stringify(state.template));
state.history.push(newHistory);
state.historyIndex = state.history.length - 1;
const maxHistory = 50;
if (state.history.length > maxHistory) {
state.history = state.history.slice(-maxHistory);
state.historyIndex = state.history.length - 1;
}
}
// src/components/VariablePanel.tsx
// src/components/VariableForm.tsx
var _jsxruntime = require('react/jsx-runtime');
var VariableForm = ({
variable,
onSubmit,
onCancel,
customTypes,
locale = "en"
}) => {
const [formData, setFormData] = _react.useState.call(void 0, {
name: _optionalChain([variable, 'optionalAccess', _3 => _3.name]) || "",
type: _optionalChain([variable, 'optionalAccess', _4 => _4.type]) || "text",
description: _optionalChain([variable, 'optionalAccess', _5 => _5.description]) || "",
defaultValue: _optionalChain([variable, 'optionalAccess', _6 => _6.defaultValue]) || "",
required: _optionalChain([variable, 'optionalAccess', _7 => _7.required]) || false,
options: _optionalChain([variable, 'optionalAccess', _8 => _8.options]) || [],
validation: _optionalChain([variable, 'optionalAccess', _9 => _9.validation]) || {}
});
const [errors, setErrors] = _react.useState.call(void 0, {});
const labels = {
en: {
name: "Variable Name",
type: "Type",
description: "Description",
defaultValue: "Default Value",
required: "Required",
save: "Save",
cancel: "Cancel",
addOption: "Add Option",
optionLabel: "Label",
optionValue: "Value",
validation: "Validation",
minValue: "Min Value",
maxValue: "Max Value",
pattern: "Pattern (RegExp)",
errorMessage: "Error Message"
},
"zh-CN": {
name: "\u53D8\u91CF\u540D\u79F0",
type: "\u7C7B\u578B",
description: "\u63CF\u8FF0",
defaultValue: "\u9ED8\u8BA4\u503C",
required: "\u5FC5\u586B",
save: "\u4FDD\u5B58",
cancel: "\u53D6\u6D88",
addOption: "\u6DFB\u52A0\u9009\u9879",
optionLabel: "\u6807\u7B7E",
optionValue: "\u503C",
validation: "\u9A8C\u8BC1\u89C4\u5219",
minValue: "\u6700\u5C0F\u503C",
maxValue: "\u6700\u5927\u503C",
pattern: "\u6B63\u5219\u8868\u8FBE\u5F0F",
errorMessage: "\u9519\u8BEF\u63D0\u793A"
}
}[locale];
const typeOptions = [
{ value: "text", label: locale === "zh-CN" ? "\u6587\u672C" : "Text" },
{ value: "number", label: locale === "zh-CN" ? "\u6570\u5B57" : "Number" },
{ value: "boolean", label: locale === "zh-CN" ? "\u5E03\u5C14\u503C" : "Boolean" },
{ value: "select", label: locale === "zh-CN" ? "\u9009\u62E9" : "Select" },
{ value: "multiline", label: locale === "zh-CN" ? "\u591A\u884C\u6587\u672C" : "Multiline" },
..._optionalChain([customTypes, 'optionalAccess', _10 => _10.map, 'call', _11 => _11((t) => ({ value: t.type, label: t.label }))]) || []
];
const validate = () => {
const newErrors = {};
if (!_optionalChain([formData, 'access', _12 => _12.name, 'optionalAccess', _13 => _13.trim, 'call', _14 => _14()])) {
newErrors.name = locale === "zh-CN" ? "\u53D8\u91CF\u540D\u4E0D\u80FD\u4E3A\u7A7A" : "Variable name is required";
} else if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(formData.name)) {
newErrors.name = locale === "zh-CN" ? "\u53D8\u91CF\u540D\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u4E0B\u5212\u7EBF\uFF0C\u4E14\u4E0D\u80FD\u4EE5\u6570\u5B57\u5F00\u5934" : "Variable name must contain only letters, numbers, and underscores, and cannot start with a number";
}
if (formData.type === "select" && (!formData.options || formData.options.length === 0)) {
newErrors.options = locale === "zh-CN" ? "\u9009\u62E9\u7C7B\u578B\u5FC5\u987B\u81F3\u5C11\u6709\u4E00\u4E2A\u9009\u9879" : "Select type must have at least one option";
}
if (_optionalChain([formData, 'access', _15 => _15.validation, 'optionalAccess', _16 => _16.pattern])) {
try {
new RegExp(formData.validation.pattern);
} catch (e3) {
newErrors.pattern = locale === "zh-CN" ? "\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F" : "Invalid regular expression";
}
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
onSubmit(formData);
}
};
const handleAddOption = () => {
setFormData({
...formData,
options: [...formData.options || [], { label: "", value: "" }]
});
};
const handleUpdateOption = (index, field, value) => {
const newOptions = [...formData.options || []];
newOptions[index] = { ...newOptions[index], [field]: value };
setFormData({ ...formData, options: newOptions });
};
const handleRemoveOption = (index) => {
const newOptions = [...formData.options || []];
newOptions.splice(index, 1);
setFormData({ ...formData, options: newOptions });
};
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { className: "variable-form", onSubmit: handleSubmit, children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { children: [
labels.name,
" *"
] }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
value: formData.name,
onChange: (e) => setFormData({ ...formData, name: e.target.value }),
className: errors.name ? "error" : ""
}
),
errors.name && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "error-message", children: errors.name })
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { children: [
labels.type,
" *"
] }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"select",
{
value: formData.type,
onChange: (e) => setFormData({ ...formData, type: e.target.value }),
children: typeOptions.map((option) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: option.value, children: option.label }, option.value))
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.description }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"textarea",
{
value: formData.description,
onChange: (e) => setFormData({ ...formData, description: e.target.value }),
rows: 2
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.defaultValue }),
formData.type === "boolean" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "checkbox",
checked: formData.defaultValue || false,
onChange: (e) => setFormData({ ...formData, defaultValue: e.target.checked })
}
) : formData.type === "multiline" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"textarea",
{
value: formData.defaultValue,
onChange: (e) => setFormData({ ...formData, defaultValue: e.target.value }),
rows: 2
}
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: formData.type === "number" ? "number" : "text",
value: formData.defaultValue,
onChange: (e) => setFormData({ ...formData, defaultValue: e.target.value })
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "form-group", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "checkbox",
checked: formData.required,
onChange: (e) => setFormData({ ...formData, required: e.target.checked })
}
),
labels.required
] }) }),
formData.type === "select" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: "Options" }),
_optionalChain([formData, 'access', _17 => _17.options, 'optionalAccess', _18 => _18.map, 'call', _19 => _19((option, index) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "option-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
placeholder: labels.optionLabel,
value: option.label,
onChange: (e) => handleUpdateOption(index, "label", e.target.value)
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
placeholder: labels.optionValue,
value: option.value,
onChange: (e) => handleUpdateOption(index, "value", e.target.value)
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", onClick: () => handleRemoveOption(index), children: "\xD7" })
] }, index))]),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { type: "button", onClick: handleAddOption, className: "add-option-btn", children: [
"+ ",
labels.addOption
] }),
errors.options && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "error-message", children: errors.options })
] }),
(formData.type === "text" || formData.type === "number") && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "details", { className: "form-group", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "summary", { children: labels.validation }),
formData.type === "number" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.minValue }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "number",
value: _optionalChain([formData, 'access', _20 => _20.validation, 'optionalAccess', _21 => _21.min]) || "",
onChange: (e) => setFormData({
...formData,
validation: { ...formData.validation, min: e.target.value ? Number(e.target.value) : void 0 }
})
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.maxValue }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "number",
value: _optionalChain([formData, 'access', _22 => _22.validation, 'optionalAccess', _23 => _23.max]) || "",
onChange: (e) => setFormData({
...formData,
validation: { ...formData.validation, max: e.target.value ? Number(e.target.value) : void 0 }
})
}
)
] })
] }),
formData.type === "text" && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.pattern }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
value: _optionalChain([formData, 'access', _24 => _24.validation, 'optionalAccess', _25 => _25.pattern]) || "",
onChange: (e) => setFormData({
...formData,
validation: { ...formData.validation, pattern: e.target.value }
}),
className: errors.pattern ? "error" : ""
}
),
errors.pattern && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "error-message", children: errors.pattern })
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-row", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "label", { children: labels.errorMessage }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
value: _optionalChain([formData, 'access', _26 => _26.validation, 'optionalAccess', _27 => _27.message]) || "",
onChange: (e) => setFormData({
...formData,
validation: { ...formData.validation, message: e.target.value }
})
}
)
] })
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "form-actions", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", onClick: onCancel, className: "btn-secondary", children: labels.cancel }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "submit", className: "btn-primary", children: labels.save })
] })
] });
};
// src/components/VariableList.tsx
var VariableList = ({
variables,
editingVariable,
onEdit,
onUpdate,
onRemove,
customTypes,
readOnly = false,
locale = "en"
}) => {
const typeLabels = {
text: locale === "zh-CN" ? "\u6587\u672C" : "Text",
number: locale === "zh-CN" ? "\u6570\u5B57" : "Number",
boolean: locale === "zh-CN" ? "\u5E03\u5C14\u503C" : "Boolean",
select: locale === "zh-CN" ? "\u9009\u62E9" : "Select",
multiline: locale === "zh-CN" ? "\u591A\u884C\u6587\u672C" : "Multiline"
};
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "variable-list", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _framermotion.AnimatePresence, { children: variables.map((variable) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_framermotion.motion.div,
{
className: "variable-item",
initial: { opacity: 0, y: -10 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -10 },
layout: true,
children: editingVariable === variable.name ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
VariableForm,
{
variable,
onSubmit: (updatedVariable) => {
onUpdate(variable.name, updatedVariable);
onEdit(null);
},
onCancel: () => onEdit(null),
customTypes,
locale
}
) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__content", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__header", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__info", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "variable-item__name", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "code", { children: `{{${variable.name}}}` }) }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "variable-item__type", children: typeLabels[variable.type] || variable.type }),
variable.required && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "variable-item__required", children: "*" })
] }),
!readOnly && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__actions", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"button",
{
className: "variable-item__action",
onClick: () => onEdit(variable.name),
title: locale === "zh-CN" ? "\u7F16\u8F91" : "Edit",
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, EditIcon, {})
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"button",
{
className: "variable-item__action variable-item__action--danger",
onClick: () => onRemove(variable.name),
title: locale === "zh-CN" ? "\u5220\u9664" : "Delete",
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DeleteIcon, {})
}
)
] })
] }),
variable.description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "variable-item__description", children: variable.description }),
variable.defaultValue !== void 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__default", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: locale === "zh-CN" ? "\u9ED8\u8BA4\u503C\uFF1A" : "Default: " }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "code", { children: JSON.stringify(variable.defaultValue) })
] }),
variable.type === "select" && variable.options && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__options", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: locale === "zh-CN" ? "\u9009\u9879\uFF1A" : "Options: " }),
variable.options.map((opt, idx) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "variable-item__option", children: [
opt.label,
" (",
opt.value,
")"
] }, idx))
] }),
variable.validation && Object.keys(variable.validation).length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-item__validation", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: locale === "zh-CN" ? "\u9A8C\u8BC1\uFF1A" : "Validation: " }),
variable.validation.min !== void 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
"Min: ",
variable.validation.min
] }),
variable.validation.max !== void 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
"Max: ",
variable.validation.max
] }),
variable.validation.pattern && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
"Pattern: ",
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "code", { children: variable.validation.pattern })
] })
] })
] })
},
variable.name
)) }) });
};
var EditIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M12.146.854a.5.5 0 01.708 0l2.292 2.292a.5.5 0 010 .708l-9 9a.5.5 0 01-.168.11l-5 2a.5.5 0 01-.65-.65l2-5a.5.5 0 01.11-.168l9-9zM12.5 2L14 3.5 12.5 5 11 3.5 12.5 2zm-10 10L1 14l2-1.5L12.5 3 11 1.5 1.5 11z" }) });
var DeleteIcon = () => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M5.5 5.5A.5.5 0 016 6v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V6z" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M14.5 3a1 1 0 01-1 1H13v9a2 2 0 01-2 2H5a2 2 0 01-2-2V4h-.5a1 1 0 01-1-1V2a1 1 0 011-1H6a1 1 0 011-1h2a1 1 0 011 1h3.5a1 1 0 011 1v1zM4 4h8v9a1 1 0 01-1 1H5a1 1 0 01-1-1V4z" })
] });
// src/components/VariablePanel.tsx
var VariablePanel = ({
variables,
customTypes,
readOnly = false,
locale = "en"
}) => {
const [isAddingVariable, setIsAddingVariable] = _react.useState.call(void 0, false);
const [editingVariable, setEditingVariable] = _react.useState.call(void 0, null);
const { addVariable, updateVariable, removeVariable } = usePromptEditorStore();
const handleAddVariable = (variable) => {
addVariable(variable);
setIsAddingVariable(false);
};
const handleUpdateVariable = (name, updates) => {
updateVariable(name, updates);
setEditingVariable(null);
};
const handleRemoveVariable = (name) => {
if (window.confirm(locale === "zh-CN" ? "\u786E\u5B9A\u5220\u9664\u8BE5\u53D8\u91CF\u5417\uFF1F" : "Are you sure to delete this variable?")) {
removeVariable(name);
}
};
const labels = {
en: {
title: "Variables",
addVariable: "Add Variable",
noVariables: "No variables defined",
description: "Define variables to make your prompt dynamic"
},
"zh-CN": {
title: "\u53D8\u91CF",
addVariable: "\u6DFB\u52A0\u53D8\u91CF",
noVariables: "\u6682\u65E0\u53D8\u91CF",
description: "\u5B9A\u4E49\u53D8\u91CF\u4F7F\u63D0\u793A\u8BCD\u66F4\u52A0\u7075\u6D3B"
}
}[locale];
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-panel", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-panel__header", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: labels.title }),
!readOnly && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"button",
{
className: "variable-panel__add-btn",
onClick: () => setIsAddingVariable(true),
disabled: isAddingVariable,
children: [
"+ ",
labels.addVariable
]
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "variable-panel__content", children: variables.length === 0 && !isAddingVariable ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "variable-panel__empty", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { children: labels.noVariables }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "small", { children: labels.description })
] }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
isAddingVariable && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_framermotion.motion.div,
{
initial: { opacity: 0, y: -10 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -10 },
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
VariableForm,
{
onSubmit: handleAddVariable,
onCancel: () => setIsAddingVariable(false),
customTypes,
locale
}
)
}
),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
VariableList,
{
variables,
editingVariable,
onEdit: setEditingVariable,
onUpdate: handleUpdateVariable,
onRemove: handleRemoveVariable,
customTypes,
readOnly,
locale
}
)
] }) })
] });
};
// src/components/Toolbar.tsx
var Toolbar = ({
config,
isDirty,
onSave,
onExport,
onImport,
locale = "en"
}) => {
const { undo, redo, history, historyIndex } = usePromptEditorStore();
const canUndo = historyIndex > 0;
const canRedo = historyIndex < history.length - 1;
const handleImport = () => {
const input = document.createElement("input");
input.type = "file";
input.accept = ".json,.yaml,.yml,.txt";
input.onchange = async (e) => {
const file = _optionalChain([e, 'access', _28 => _28.target, 'access', _29 => _29.files, 'optionalAccess', _30 => _30[0]]);
if (file && onImport) {
await onImport(file);
}
};
input.click();
};
const handleExport = (format) => {
if (onExport) {
onExport(format);
}
};
const labels = {
en: {
save: "Save",
export: "Export",
import: "Import",
undo: "Undo",
redo: "Redo",
format: "Format",
preview: "Preview",
settings: "Settings"
},
"zh-CN": {
save: "\u4FDD\u5B58",
export: "\u5BFC\u51FA",
import: "\u5BFC\u5165",
undo: "\u64A4\u9500",
redo: "\u91CD\u505A",
format: "\u683C\u5F0F\u5316",
preview: "\u9884\u89C8",
settings: "\u8BBE\u7F6E"
}
}[locale];
const defaultConfig = {
showSave: true,
showExport: true,
showImport: true,
showUndo: true,
showRedo: true,
showFormat: false,
showPreview: false,
showSettings: false,
...config
};
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "prompt-editor__toolbar", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "toolbar__group", children: [
defaultConfig.showSave && onSave && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"button",
{
className: "toolbar__btn toolbar__btn--primary",
onClick: onSave,
disabled: !isDirty,
title: labels.save,
children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, SaveIcon, {}),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: labels.save })
]
}
),
defaultConfig.showUndo && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"button",
{
className: "toolbar__btn",
onClick: undo,
disabled: !canUndo,
title: labels.undo,
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, UndoIcon, {})
}
),
defaultConfig.showRedo && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"button",
{
className: "toolbar__btn",
onClick: redo,
disabled: !canRedo,
title: labels.redo,
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, RedoIcon, {})
}
)
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "toolbar__group", children: [
defaultConfig.showImport && onImport && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"button",
{
className: "toolbar__btn",
onClick: handleImport,
title: labels.import,
children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ImportIcon, {}),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: labels.import })
]
}
),
defaultConfig.showExport && onExport && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "toolbar__dropdown", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "button", { className: "toolbar__btn", title: labels.export, children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ExportIcon, {}),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: labels.export }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, ChevronDownIcon, {})
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "toolbar__dropdown-menu", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { onClick: () => handleExport("json"), children: "JSON" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { onClick: () => handleExport("yaml"), children: "YAML" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { onClick: () => handleExport("markdown"), children: "Markdown" }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { onClick: () => handleExport("txt"), children: "Text" })
] })
] })
] }),
defaultConfig.customActions && defaultConfig.customActions.length > 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "toolbar__group", children: defaultConfig.customActions.map((action) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"button",
{
className: "toolbar__btn",
onClick: action.onClick,
disabled: action.disabled,
title: action.tooltip,
children: [
action.icon,
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { children: action.label })
]
},
action.id
)) })
] });
};
var SaveIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M13.5 2h-11A1.5 1.5 0 001 3.5v9A1.5 1.5 0 002.5 14h11a1.5 1.5 0 001.5-1.5v-9A1.5 1.5 0 0013.5 2zM12 12H4V6h8v6z" }) });
var UndoIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 3a5 5 0 00-5 5h2a3 3 0 016 0 3 3 0 01-6 0H3l3-3-3-3v2a5 5 0 105 5V8z" }) });
var RedoIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 3a5 5 0 015 5h-2a3 3 0 00-6 0 3 3 0 006 0h2l-3-3 3-3v2a5 5 0 11-5 5V8z" }) });
var ImportIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 10l-4-4h3V2h2v4h3l-4 4zm-6 3v1h12v-1H2z" }) });
var ExportIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M8 6l4 4H9v4H7v-4H4l4-4zM2 3v1h12V3H2z" }) });
var ChevronDownIcon = () => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "path", { d: "M2 4l4 4 4-4H2z" }) });
// src/components/PreviewPanel.tsx
var PreviewPanel = ({
template,
locale = "en"
}) => {
const { previewValues, setPreviewValue } = usePromptEditorStore();
const previewContent = _react.useMemo.call(void 0, () => {
let content = template.content;
template.variables.forEach((variable) => {
const value = _nullishCoalesce(_nullishCoalesce(previewValues[variable.name], () => ( variable.defaultValue)), () => ( ""));
const regex = new RegExp(`{{\\s*${variable.name}\\s*}}`, "g");
content = content.replace(regex, String(value));
});
return content;
}, [template.content, template.variables, previewValues]);
const labels = {
en: {
title: "Preview",
variables: "Variables",
output: "Output",
noContent: "No content to preview"
},
"zh-CN": {
title: "\u9884\u89C8",
variables: "\u53D8\u91CF\u503C",
output: "\u8F93\u51FA\u7ED3\u679C",
noContent: "\u6682\u65E0\u5185\u5BB9\u53EF\u9884\u89C8"
}
}[locale];
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "preview-panel", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "preview-panel__header", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { children: labels.title }) }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "preview-panel__content", children: [
template.variables.length > 0 && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "preview-panel__variables", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h4", { children: labels.variables }),
template.variables.map((variable) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "preview-panel__variable", children: [
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "label", { children: [
variable.name,
variable.required && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "required", children: "*" })
] }),
renderVariableInput(
variable,
previewValues[variable.name],
(value) => setPreviewValue(variable.name, value)
),
variable.description && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "small", { children: variable.description })
] }, variable.name))
] }),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "preview-panel__output", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h4", { children: labels.output }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "preview-panel__output-content", children: previewContent ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "pre", { children: previewContent }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "preview-panel__empty", children: labels.noContent }) })
] })
] })
] });
};
function renderVariableInput(variable, value, onChange) {
switch (variable.type) {
case "text":
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
value: value || "",
onChange: (e) => onChange(e.target.value),
placeholder: variable.defaultValue
}
);
case "number":
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "number",
value: value || "",
onChange: (e) => onChange(e.target.value ? Number(e.target.value) : ""),
min: _optionalChain([variable, 'access', _31 => _31.validation, 'optionalAccess', _32 => _32.min]),
max: _optionalChain([variable, 'access', _33 => _33.validation, 'optionalAccess', _34 => _34.max]),
placeholder: variable.defaultValue
}
);
case "boolean":
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "checkbox",
checked: _nullishCoalesce(_nullishCoalesce(value, () => ( variable.defaultValue)), () => ( false)),
onChange: (e) => onChange(e.target.checked)
}
);
case "select":
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"select",
{
value: value || "",
onChange: (e) => onChange(e.target.value),
children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: "", children: "-- Select --" }),
_optionalChain([variable, 'access', _35 => _35.options, 'optionalAccess', _36 => _36.map, 'call', _37 => _37((option) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "option", { value: option.value, children: option.label }, option.value))])
]
}
);
case "multiline":
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"textarea",
{
value: value || "",
onChange: (e) => onChange(e.target.value),
placeholder: variable.defaultValue,
rows: 3
}
);
default:
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
"input",
{
type: "text",
value: value || "",
onChange: (e) => onChange(e.target.value),
placeholder: variable.defaultValue
}
);
}
}
// src/components/PromptEditor.tsx
var PromptEditor = ({
initialTemplate: initialTemplate2,
mode = "light",
readOnly = false,
showLineNumbers = true,
showVariablePanel = true,
showPreview = true,
height = "600px",
width = "100%",
theme,
locale = "en",
onSave,
onExport,
onImport,
customVariableTypes,
toolbar,
className,
style
}) => {
const {
template,
isDirty,
updateContent,
loadTemplate,
save
} = usePromptEditorStore();
_react.useEffect.call(void 0, () => {
if (initialTemplate2) {
loadTemplate(initialTemplate2);
}
}, [initialTemplate2, loadTemplate]);
const handleEditorChange = _react.useCallback.call(void 0, (value) => {
if (value !== void 0 && !readOnly) {
updateContent(value);
}
}, [updateContent, readOnly]);
const handleSave = _react.useCallback.call(void 0, async () => {
if (onSave) {
await onSave(template);
await save();
} else {
await save();
}
}, [template, onSave, save]);
const editorOptions = _react.useMemo.call(void 0, () => ({
readOnly,
lineNumbers: showLineNumbers ? "on" : "off",
minimap: { enabled: false },
scrollBeyondLastLine: false,
wordWrap: "on",
fontSize: 14,
fontFamily: 'Consolas, "Courier New", monospace',
padding: { top: 16, bottom: 16 },
automaticLayout: true
}), [readOnly, showLineNumbers]);
const monacoTheme = _optionalChain([theme, 'optionalAccess', _38 => _38.monacoTheme]) || (mode === "dark" ? "vs-dark" : "vs");
const containerStyle = {
height,
width,
...style
};
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
"div",
{
className: `prompt-editor prompt-editor--${mode} ${className || ""}`,
style: containerStyle,
"data-theme": mode,
children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
Toolbar,
{
config: toolbar,
isDirty,
onSave: handleSave,
onExport: onExport ? (format) => onExport(template, format) : void 0,
onImport,
locale
}
),
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "prompt-editor__main", children: [
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _framermotion.AnimatePresence, { children: showVariablePanel && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_framermotion.motion.div,
{
className: "prompt-editor__variables",
initial: { width: 0, opacity: 0 },
animate: { width: 300, opacity: 1 },
exit: { width: 0, opacity: 0 },
transition: { duration: 0.2 },
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
VariablePanel,
{
variables: template.variables,
customTypes: customVariableTypes,
readOnly,
locale
}
)
}
) }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "prompt-editor__content", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_react4.default,
{
value: template.content,
language: "markdown",
theme: monacoTheme,
options: editorOptions,
onChange: handleEditorChange,
beforeMount: (monaco) => {
registerVariableCompletion(monaco, template.variables);
}
}
) }),
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _framermotion.AnimatePresence, { children: showPreview && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
_framermotion.motion.div,
{
className: "prompt-editor__preview",
initial: { width: 0, opacity: 0 },
animate: { width: 400, opacity: 1 },
exit: { width: 0, opacity: 0 },
transition: { duration: 0.2 },
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
PreviewPanel,
{
template,
locale
}
)
}
) })
] })
]
}
);
};
function registerVariableCompletion(monaco, variables) {
monaco.languages.registerCompletionItemProvider("markdown", {
provideCompletionItems: (model, position) => {
const word = model.getWordUntilPosition(position);
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
const suggestions = variables.map((variable) => ({
label: `{{${variable.name}}}`,
kind: monaco.languages.CompletionItemKind.Variable,
documentation: variable.description,
insertText: `{{${variable.name}}}`,
range
}));
return { suggestions };
},
triggerCharacters: ["{"]
});
}
// ../../node_modules/.pnpm/js-yaml@4.1.0/node_modules/js-yaml/dist/js-yaml.mjs
function isNothing(subject) {
return typeof subject === "undefined" || subject === null;
}
function isObject(subject) {
return typeof subject === "object" && subject !== null;
}
function toArray(sequence) {
if (Array.isArray(sequence))
return sequence;
else if (isNothing(sequence))
return [];
return [sequence];
}
function extend(target, source) {
var index, length, key, sourceKeys;
if (source) {
sourceKeys = Object.keys(source);
for (index = 0, length = sourceKeys.length; index < length; index += 1) {
key = sourceKeys[index];
target[key] = source[key];
}
}
return target;
}
function repeat(string, count) {
var result = "", cycle;
for (cycle = 0; cycle < count; cycle += 1) {
result += string;
}
return result;
}
function isNegativeZero(number) {
return number === 0 && Number.NEGATIVE_INFINITY === 1 / number;
}
var isNothing_1 = isNothing;
var isObject_1 = isObject;
var toArray_1 = toArray;
var repeat_1 = repeat;
var isNegativeZero_1 = isNegativeZero;
var extend_1 = extend;
var common = {
isNothing: isNothing_1,
isObject: isObject_1,
toArray: toArray_1,
repeat: repeat_1,
isNegativeZero: isNegativeZero_1,
extend: extend_1
};
function formatError(exception2, compact) {
var where = "", message = exception2.reason || "(unknown reason)";
if (!exception2.mark)
return message;
if (exception2.mark.name) {
where += 'in "' + exception2.mark.name + '" ';
}
where += "(" + (exception2.mark.line + 1) + ":" + (exception2.mark.column + 1) + ")";
if (!compact && exception2.mark.snippet) {
where += "\n\n" + exception2.mark.snippet;
}
return message + " " + where;
}
function YAMLException$1(reason, mark) {
Error.call(this);
this.name = "YAMLException";
this.reason = reason;
this.mark = mark;
this.message = formatError(this, false);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error().stack || "";
}
}
YAMLException$1.prototype = Object.create(Error.prototype);
YAMLException$1.prototype.constructor = YAMLException$1;
YAMLException$1.prototype.toString = function toString(compact) {
return this.name + ": " + formatError(this, compact);
};
var exception = YAMLException$1;
function getLine(buffer, lineStart, lineEnd, position, maxLineLength) {
var head = "";
var tail = "";
var maxHalfLength = Math.floor(maxLineLength / 2) - 1;
if (position - lineStart > maxHalfLength) {
head = " ... ";
lineStart = position - maxHalfLength + head.length;
}
if (lineEnd - position > maxHalfLength) {
tail = " ...";
lineEnd = position + maxHalfLength - tail.length;
}
return {
str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, "\u2192") + tail,
pos: position - lineStart + head.length
// relative position
};
}
function padStart(string, max) {
return common.repeat(" ", max - string.length) + string;
}
function makeSnippet(mark, options) {
options = Object.create(options || null);
if (!mark.buffer)
return null;
if (!options.maxLength)
options.maxLength = 79;
if (typeof options.indent !== "number")
options.indent = 1;
if (typeof options.linesBefore !== "number")
options.linesBefore = 3;
if (typeof options.linesAfter !==