draply-dev 1.5.3 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +3 -2
- package/package.json +1 -1
- package/src/overlay.js +127 -10
package/bin/cli.js
CHANGED
|
@@ -112,10 +112,11 @@ async function callAI(cfg, prompt) {
|
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// OpenAI / Groq — compatible API
|
|
115
|
+
// OpenAI / Groq / Gemini — compatible API
|
|
116
116
|
const hosts = {
|
|
117
117
|
openai: ['api.openai.com', '/v1/chat/completions', 'gpt-4o-mini'],
|
|
118
|
-
groq: ['api.groq.com', '/openai/v1/chat/completions', 'llama-3.3-70b-versatile']
|
|
118
|
+
groq: ['api.groq.com', '/openai/v1/chat/completions', 'llama-3.3-70b-versatile'],
|
|
119
|
+
gemini: ['generativelanguage.googleapis.com', '/v1beta/openai/chat/completions', 'gemini-2.5-flash']
|
|
119
120
|
};
|
|
120
121
|
const [hostname, apiPath, defaultModel] = hosts[provider] || hosts.groq;
|
|
121
122
|
const body = JSON.stringify({ model: cfg.model || defaultModel, messages: [{ role: 'user', content: prompt }], temperature: 0.1, max_tokens: 8192 });
|
package/package.json
CHANGED
package/src/overlay.js
CHANGED
|
@@ -678,6 +678,7 @@
|
|
|
678
678
|
|
|
679
679
|
<div class="ps-foot">
|
|
680
680
|
<button id="__ps_sv__" disabled>SAVE CHANGES</button>
|
|
681
|
+
<button id="__ps_ai_cfg__" style="width:100%;margin-top:8px;background:none;border:1px solid #2a2a3a;border-radius:6px;padding:6px;color:#5a5a7a;font-size:9px;cursor:pointer;letter-spacing:1px;text-transform:uppercase;transition:color .15s, border-color .15s" onmouseover="this.style.color='#7fff6e'; this.style.borderColor='#7fff6e'" onmouseout="this.style.color='#5a5a7a'; this.style.borderColor='#2a2a3a'">⚙️ AI Provider</button>
|
|
681
682
|
</div>
|
|
682
683
|
</div>
|
|
683
684
|
|
|
@@ -1640,6 +1641,7 @@ document.addEventListener('mouseup', () => {
|
|
|
1640
1641
|
// ══════════════════════════════════════════
|
|
1641
1642
|
// Full history — every individual action
|
|
1642
1643
|
const history = [];
|
|
1644
|
+
const redoHistory = [];
|
|
1643
1645
|
|
|
1644
1646
|
function rec(el, props, prevPropsOverride, isCreate = false) {
|
|
1645
1647
|
const selector = el.dataset.pixelshiftId ? null : gsel(el);
|
|
@@ -1695,7 +1697,14 @@ function rec(el, props, prevPropsOverride, isCreate = false) {
|
|
|
1695
1697
|
|
|
1696
1698
|
// Push to history
|
|
1697
1699
|
const hid = Date.now() + Math.random();
|
|
1698
|
-
history.push({
|
|
1700
|
+
history.push({
|
|
1701
|
+
hid, el, props, prevProps, selector: key, isCreate,
|
|
1702
|
+
parent: isCreate ? el.parentElement : null,
|
|
1703
|
+
nextSibling: isCreate ? el.nextElementSibling : null
|
|
1704
|
+
});
|
|
1705
|
+
|
|
1706
|
+
// Clear redo stack on new action
|
|
1707
|
+
redoHistory.length = 0;
|
|
1699
1708
|
|
|
1700
1709
|
updateUnsUI();
|
|
1701
1710
|
}
|
|
@@ -1736,9 +1745,12 @@ function revertChange(h) {
|
|
|
1736
1745
|
if (h.prevProps.innerHTML !== undefined) h.el.innerHTML = h.prevProps.innerHTML;
|
|
1737
1746
|
if (h.prevProps.innerText !== undefined) h.el.innerText = h.prevProps.innerText;
|
|
1738
1747
|
}
|
|
1739
|
-
// Remove from history
|
|
1748
|
+
// Remove from history and add to redo stack
|
|
1740
1749
|
const idx = history.findIndex(x => x.hid === h.hid);
|
|
1741
|
-
if (idx >= 0)
|
|
1750
|
+
if (idx >= 0) {
|
|
1751
|
+
redoHistory.push(history[idx]);
|
|
1752
|
+
history.splice(idx, 1);
|
|
1753
|
+
}
|
|
1742
1754
|
// Rebuild state.changes — preserve all original fields (#1, #2)
|
|
1743
1755
|
state.changes = [];
|
|
1744
1756
|
history.forEach(x => {
|
|
@@ -1758,9 +1770,91 @@ function revertChange(h) {
|
|
|
1758
1770
|
}
|
|
1759
1771
|
});
|
|
1760
1772
|
updateUnsUI();
|
|
1773
|
+
|
|
1774
|
+
if (state.selectedEl) {
|
|
1775
|
+
const isVisible = document.body.contains(state.selectedEl) && getComputedStyle(state.selectedEl).display !== 'none';
|
|
1776
|
+
if (isVisible) {
|
|
1777
|
+
if (state.tool === 'mov') placeHdl(state.selectedEl);
|
|
1778
|
+
if (state.tool === 'rsz') placeRH(state.selectedEl);
|
|
1779
|
+
} else if (state.selectedEl === h.el) {
|
|
1780
|
+
state.selectedEl.classList.remove('__ps__', '__ps_multi__');
|
|
1781
|
+
state.selectedEl = null;
|
|
1782
|
+
state.selectedEls = [];
|
|
1783
|
+
hdl.classList.remove('v');
|
|
1784
|
+
Object.values(rhs).forEach(rh => rh.classList.remove('v'));
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1761
1788
|
toast('↩ Reverted');
|
|
1762
1789
|
}
|
|
1763
1790
|
|
|
1791
|
+
function redoChange() {
|
|
1792
|
+
if (redoHistory.length === 0) {
|
|
1793
|
+
toast('Nothing to redo');
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
const h = redoHistory.pop();
|
|
1797
|
+
|
|
1798
|
+
if (h.isCreate) {
|
|
1799
|
+
if (h.parent) {
|
|
1800
|
+
if (h.nextSibling) {
|
|
1801
|
+
h.parent.insertBefore(h.el, h.nextSibling);
|
|
1802
|
+
} else {
|
|
1803
|
+
h.parent.appendChild(h.el);
|
|
1804
|
+
}
|
|
1805
|
+
} else {
|
|
1806
|
+
document.body.appendChild(h.el);
|
|
1807
|
+
}
|
|
1808
|
+
} else {
|
|
1809
|
+
// Re-apply properties
|
|
1810
|
+
Object.entries(h.props).forEach(([prop, val]) => {
|
|
1811
|
+
const camel = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
1812
|
+
h.el.style[camel] = val;
|
|
1813
|
+
});
|
|
1814
|
+
if (h.props.innerHTML !== undefined) h.el.innerHTML = h.props.innerHTML;
|
|
1815
|
+
if (h.props.innerText !== undefined) h.el.innerText = h.props.innerText;
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
history.push(h);
|
|
1819
|
+
|
|
1820
|
+
// Rebuild state.changes
|
|
1821
|
+
state.changes = [];
|
|
1822
|
+
history.forEach(x => {
|
|
1823
|
+
const key = x.selector;
|
|
1824
|
+
const i = state.changes.findIndex(c => (c.pixelshiftId || c.selector) === key);
|
|
1825
|
+
if (i >= 0) {
|
|
1826
|
+
Object.assign(state.changes[i].props, x.props);
|
|
1827
|
+
} else {
|
|
1828
|
+
state.changes.push({
|
|
1829
|
+
type: x.isCreate ? 'create' : 'css',
|
|
1830
|
+
isCreate: x.isCreate || false,
|
|
1831
|
+
selector: key,
|
|
1832
|
+
props: { ...x.props },
|
|
1833
|
+
outerHTML: x.isCreate && x.el ? x.el.outerHTML : undefined,
|
|
1834
|
+
tagName: x.el ? x.el.tagName?.toLowerCase() : undefined
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
});
|
|
1838
|
+
|
|
1839
|
+
updateUnsUI();
|
|
1840
|
+
|
|
1841
|
+
if (state.selectedEl) {
|
|
1842
|
+
const isVisible = document.body.contains(state.selectedEl) && getComputedStyle(state.selectedEl).display !== 'none';
|
|
1843
|
+
if (isVisible) {
|
|
1844
|
+
if (state.tool === 'mov') placeHdl(state.selectedEl);
|
|
1845
|
+
if (state.tool === 'rsz') placeRH(state.selectedEl);
|
|
1846
|
+
} else if (state.selectedEl === h.el) {
|
|
1847
|
+
state.selectedEl.classList.remove('__ps__', '__ps_multi__');
|
|
1848
|
+
state.selectedEl = null;
|
|
1849
|
+
state.selectedEls = [];
|
|
1850
|
+
hdl.classList.remove('v');
|
|
1851
|
+
Object.values(rhs).forEach(rh => rh.classList.remove('v'));
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
toast('↷ Redone');
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1764
1858
|
sv.addEventListener('click', async () => {
|
|
1765
1859
|
// Check key config status
|
|
1766
1860
|
let hasKey = false;
|
|
@@ -1774,7 +1868,7 @@ sv.addEventListener('click', async () => {
|
|
|
1774
1868
|
|
|
1775
1869
|
if (!hasKey) {
|
|
1776
1870
|
// Ask for provider first (#8)
|
|
1777
|
-
const provider = prompt('Draply AI Save: Choose provider (groq / openai / anthropic / ollama):', 'groq');
|
|
1871
|
+
const provider = prompt('Draply AI Save: Choose provider (groq / openai / anthropic / gemini / ollama):', 'groq');
|
|
1778
1872
|
if (!provider) { toast('Save aborted'); return; }
|
|
1779
1873
|
const key = provider === 'ollama' ? 'local' : prompt(`Enter your ${provider.toUpperCase()} API key:`);
|
|
1780
1874
|
if (key) {
|
|
@@ -1819,18 +1913,41 @@ sv.addEventListener('click', async () => {
|
|
|
1819
1913
|
toast('⚠ Server unreachable');
|
|
1820
1914
|
}
|
|
1821
1915
|
|
|
1822
|
-
state.changes = []; history.length = 0;
|
|
1916
|
+
state.changes = []; history.length = 0; redoHistory.length = 0;
|
|
1823
1917
|
sv.innerHTML = 'Save'; sv.textContent = 'Save';
|
|
1824
1918
|
updateUnsUI();
|
|
1825
1919
|
});
|
|
1826
1920
|
|
|
1827
1921
|
document.getElementById('__uns_clear__').onclick = () => {
|
|
1828
|
-
state.changes = []; history.length = 0; updateUnsUI();
|
|
1922
|
+
state.changes = []; history.length = 0; redoHistory.length = 0; updateUnsUI();
|
|
1829
1923
|
toast('🗑 History cleared');
|
|
1830
1924
|
};
|
|
1831
1925
|
|
|
1832
1926
|
document.getElementById('__uns_save__').onclick = () => sv.click();
|
|
1833
1927
|
|
|
1928
|
+
document.getElementById('__ps_ai_cfg__').onclick = async () => {
|
|
1929
|
+
const provider = prompt('Change AI Provider (groq / openai / anthropic / gemini / ollama):', 'groq');
|
|
1930
|
+
if (!provider) return;
|
|
1931
|
+
const key = provider === 'ollama' ? 'local' : prompt(`Enter your ${provider.toUpperCase()} API key:`);
|
|
1932
|
+
|
|
1933
|
+
if (!key && provider !== 'ollama') {
|
|
1934
|
+
toast('⚠ API Key required');
|
|
1935
|
+
return;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
toast('Verifying...');
|
|
1939
|
+
try {
|
|
1940
|
+
const vRes = await fetch('/draply-validate-key', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: key.trim(), provider: provider.trim() }) });
|
|
1941
|
+
const vData = await vRes.json();
|
|
1942
|
+
if (!vData.valid && provider !== 'ollama') {
|
|
1943
|
+
toast('⚠ Invalid API key'); return;
|
|
1944
|
+
}
|
|
1945
|
+
} catch { /* ignore validation fail */ }
|
|
1946
|
+
|
|
1947
|
+
await fetch('/draply-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: key.trim(), provider: provider.trim() }) });
|
|
1948
|
+
toast('✅ AI Provider saved!');
|
|
1949
|
+
};
|
|
1950
|
+
|
|
1834
1951
|
// ══════════════════════════════════════════
|
|
1835
1952
|
// KEYBOARD SHORTCUTS (#4, #5, #15)
|
|
1836
1953
|
// ══════════════════════════════════════════
|
|
@@ -1840,7 +1957,7 @@ document.addEventListener('keydown', e => {
|
|
|
1840
1957
|
if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) return;
|
|
1841
1958
|
|
|
1842
1959
|
// Ctrl+Z — Undo last change (#4)
|
|
1843
|
-
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key === 'z') {
|
|
1960
|
+
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && e.key.toLowerCase() === 'z') {
|
|
1844
1961
|
e.preventDefault();
|
|
1845
1962
|
if (history.length > 0) {
|
|
1846
1963
|
revertChange(history[history.length - 1]);
|
|
@@ -1850,10 +1967,10 @@ document.addEventListener('keydown', e => {
|
|
|
1850
1967
|
return;
|
|
1851
1968
|
}
|
|
1852
1969
|
|
|
1853
|
-
// Ctrl+Shift+Z — Redo (
|
|
1854
|
-
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'z') {
|
|
1970
|
+
// Ctrl+Shift+Z — Redo (implemented)
|
|
1971
|
+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key.toLowerCase() === 'z') {
|
|
1855
1972
|
e.preventDefault();
|
|
1856
|
-
|
|
1973
|
+
redoChange();
|
|
1857
1974
|
return;
|
|
1858
1975
|
}
|
|
1859
1976
|
|