+
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
diff --git a/src/components/ui/InputWithUnit.vue b/src/components/ui/InputWithUnit.vue
new file mode 100644
index 0000000..c8605a3
--- /dev/null
+++ b/src/components/ui/InputWithUnit.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ui/MarginEditor.vue b/src/components/ui/MarginEditor.vue
new file mode 100644
index 0000000..1802640
--- /dev/null
+++ b/src/components/ui/MarginEditor.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ updateDetailedUnit(side.key, unit)"
+ />
+
+
+
+
+
+
+
diff --git a/src/components/ui/UnitToggle.vue b/src/components/ui/UnitToggle.vue
new file mode 100644
index 0000000..17228cc
--- /dev/null
+++ b/src/components/ui/UnitToggle.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/src/composables/useCssSync.js b/src/composables/useCssSync.js
new file mode 100644
index 0000000..96af721
--- /dev/null
+++ b/src/composables/useCssSync.js
@@ -0,0 +1,119 @@
+import { useStylesheetStore } from '../stores/stylesheet';
+
+export function useCssSync() {
+ const store = useStylesheetStore();
+
+ /**
+ * Extract a simple CSS value (string)
+ */
+ const extractValue = (selector, property) => {
+ const block = store.extractBlock(selector);
+ if (!block) return null;
+
+ const match = block.match(new RegExp(`${property}:\\s*([^;]+)`, 'i'));
+ return match ? match[1].trim() : null;
+ };
+
+ /**
+ * Extract a numeric CSS value with unit
+ * Returns { value: number, unit: string } or null
+ */
+ const extractNumericValue = (selector, property, allowedUnits = ['px', 'em', 'rem', 'mm']) => {
+ const block = store.extractBlock(selector);
+ if (!block) return null;
+
+ const unitsPattern = allowedUnits.join('|');
+ const match = block.match(new RegExp(`${property}:\\s*([0-9.]+)(${unitsPattern})`, 'i'));
+
+ if (match) {
+ return {
+ value: parseFloat(match[1]),
+ unit: match[2].toLowerCase()
+ };
+ }
+ return null;
+ };
+
+ /**
+ * Extract margin/padding shorthand (handles 1 or 4 values)
+ * Returns { simple: { value, unit } } or { detailed: { top, right, bottom, left } }
+ */
+ const extractSpacing = (selector, property, allowedUnits = ['mm', 'px']) => {
+ const block = store.extractBlock(selector);
+ if (!block) return null;
+
+ const unitsPattern = allowedUnits.join('|');
+
+ // Check for detailed properties first (property-top, property-right, etc.)
+ const topMatch = block.match(new RegExp(`${property}-top:\\s*([0-9.]+)(${unitsPattern})`, 'i'));
+
+ if (topMatch) {
+ const rightMatch = block.match(new RegExp(`${property}-right:\\s*([0-9.]+)(${unitsPattern})`, 'i'));
+ const bottomMatch = block.match(new RegExp(`${property}-bottom:\\s*([0-9.]+)(${unitsPattern})`, 'i'));
+ const leftMatch = block.match(new RegExp(`${property}-left:\\s*([0-9.]+)(${unitsPattern})`, 'i'));
+
+ return {
+ detailed: {
+ top: topMatch ? { value: parseFloat(topMatch[1]), unit: topMatch[2] } : { value: 0, unit: 'mm' },
+ right: rightMatch ? { value: parseFloat(rightMatch[1]), unit: rightMatch[2] } : { value: 0, unit: 'mm' },
+ bottom: bottomMatch ? { value: parseFloat(bottomMatch[1]), unit: bottomMatch[2] } : { value: 0, unit: 'mm' },
+ left: leftMatch ? { value: parseFloat(leftMatch[1]), unit: leftMatch[2] } : { value: 0, unit: 'mm' },
+ }
+ };
+ }
+
+ // Check for shorthand property
+ const shorthandMatch = block.match(new RegExp(`${property}:\\s*([^;]+)`, 'i'));
+ if (!shorthandMatch) return null;
+
+ const shorthandValue = shorthandMatch[1].trim();
+
+ // Check for 4-value format: "0mm 0mm 24mm 0mm" (top right bottom left)
+ const fourValuePattern = new RegExp(
+ `^([0-9.]+)(${unitsPattern})\\s+([0-9.]+)(${unitsPattern})\\s+([0-9.]+)(${unitsPattern})\\s+([0-9.]+)(${unitsPattern})$`,
+ 'i'
+ );
+ const fourValueMatch = shorthandValue.match(fourValuePattern);
+
+ if (fourValueMatch) {
+ return {
+ detailed: {
+ top: { value: parseFloat(fourValueMatch[1]), unit: fourValueMatch[2] },
+ right: { value: parseFloat(fourValueMatch[3]), unit: fourValueMatch[4] },
+ bottom: { value: parseFloat(fourValueMatch[5]), unit: fourValueMatch[6] },
+ left: { value: parseFloat(fourValueMatch[7]), unit: fourValueMatch[8] },
+ }
+ };
+ }
+
+ // Single value format: "10mm"
+ const singleValuePattern = new RegExp(`^([0-9.]+)(${unitsPattern})$`, 'i');
+ const singleValueMatch = shorthandValue.match(singleValuePattern);
+
+ if (singleValueMatch) {
+ return {
+ simple: {
+ value: parseFloat(singleValueMatch[1]),
+ unit: singleValueMatch[2]
+ }
+ };
+ }
+
+ return null;
+ };
+
+ /**
+ * Check if a property value equals a specific string
+ */
+ const hasValue = (selector, property, expectedValue) => {
+ const value = extractValue(selector, property);
+ return value === expectedValue;
+ };
+
+ return {
+ extractValue,
+ extractNumericValue,
+ extractSpacing,
+ hasValue,
+ };
+}
diff --git a/src/composables/useCssUpdater.js b/src/composables/useCssUpdater.js
new file mode 100644
index 0000000..2eb6018
--- /dev/null
+++ b/src/composables/useCssUpdater.js
@@ -0,0 +1,120 @@
+import { useStylesheetStore } from '../stores/stylesheet';
+
+export function useCssUpdater() {
+ const store = useStylesheetStore();
+
+ /**
+ * Update or add a CSS property for a given selector
+ */
+ const updateStyle = (selector, property, value) => {
+ const currentBlock = store.extractBlock(selector) || createRule(selector);
+
+ if (currentBlock.includes(`${property}:`)) {
+ const updatedBlock = currentBlock.replace(
+ new RegExp(`(${property}:\\s*)[^;]+`, 'i'),
+ `$1${value}`
+ );
+ store.content = store.content.replace(currentBlock, updatedBlock);
+ } else {
+ const updatedBlock = currentBlock.replace(
+ /(\s*})$/,
+ ` ${property}: ${value};\n$1`
+ );
+ store.content = store.content.replace(currentBlock, updatedBlock);
+ }
+ };
+
+ /**
+ * Remove a CSS property from a selector
+ */
+ const removeProperty = (selector, property) => {
+ const currentBlock = store.extractBlock(selector);
+ if (!currentBlock) return;
+
+ const updatedBlock = currentBlock.replace(
+ new RegExp(`\\s*${property}:\\s*[^;]+;\\n?`, 'gi'),
+ ''
+ );
+
+ if (updatedBlock !== currentBlock) {
+ store.content = store.content.replace(currentBlock, updatedBlock);
+ }
+ };
+
+ /**
+ * Remove multiple CSS properties from a selector
+ */
+ const removeProperties = (selector, properties) => {
+ let currentBlock = store.extractBlock(selector);
+ if (!currentBlock) return;
+
+ let updatedBlock = currentBlock;
+ for (const property of properties) {
+ updatedBlock = updatedBlock.replace(
+ new RegExp(`\\s*${property}:\\s*[^;]+;\\n?`, 'gi'),
+ ''
+ );
+ }
+
+ if (updatedBlock !== currentBlock) {
+ store.content = store.content.replace(currentBlock, updatedBlock);
+ }
+ };
+
+ /**
+ * Create a new CSS rule for a selector
+ */
+ const createRule = (selector) => {
+ store.content += `\n\n${selector} {\n}\n`;
+ return `${selector} {\n}`;
+ };
+
+ /**
+ * Remove detailed margin properties and set shorthand
+ */
+ const setMargin = (selector, value, unit) => {
+ removeProperties(selector, ['margin-top', 'margin-right', 'margin-bottom', 'margin-left']);
+ updateStyle(selector, 'margin', `${value}${unit}`);
+ };
+
+ /**
+ * Remove shorthand margin and set detailed margins
+ */
+ const setDetailedMargins = (selector, top, right, bottom, left) => {
+ removeProperty(selector, 'margin');
+ updateStyle(selector, 'margin-top', `${top.value}${top.unit}`);
+ updateStyle(selector, 'margin-right', `${right.value}${right.unit}`);
+ updateStyle(selector, 'margin-bottom', `${bottom.value}${bottom.unit}`);
+ updateStyle(selector, 'margin-left', `${left.value}${left.unit}`);
+ };
+
+ /**
+ * Remove detailed padding properties and set shorthand
+ */
+ const setPadding = (selector, value, unit) => {
+ removeProperties(selector, ['padding-top', 'padding-right', 'padding-bottom', 'padding-left']);
+ updateStyle(selector, 'padding', `${value}${unit}`);
+ };
+
+ /**
+ * Remove shorthand padding and set detailed padding
+ */
+ const setDetailedPadding = (selector, top, right, bottom, left) => {
+ removeProperty(selector, 'padding');
+ updateStyle(selector, 'padding-top', `${top.value}${top.unit}`);
+ updateStyle(selector, 'padding-right', `${right.value}${right.unit}`);
+ updateStyle(selector, 'padding-bottom', `${bottom.value}${bottom.unit}`);
+ updateStyle(selector, 'padding-left', `${left.value}${left.unit}`);
+ };
+
+ return {
+ updateStyle,
+ removeProperty,
+ removeProperties,
+ createRule,
+ setMargin,
+ setDetailedMargins,
+ setPadding,
+ setDetailedPadding,
+ };
+}