stikfix 1.2.0
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/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/host/src/bind.js +69 -0
- package/dist/host/src/bootstrap/register.js +556 -0
- package/dist/host/src/config.js +147 -0
- package/dist/host/src/extension-id.js +49 -0
- package/dist/host/src/folder-picker.js +142 -0
- package/dist/host/src/index.js +110 -0
- package/dist/host/src/native-host.js +143 -0
- package/dist/host/src/native-msg.js +100 -0
- package/dist/host/src/probe.js +62 -0
- package/dist/host/src/read-note.js +175 -0
- package/dist/host/src/security.js +76 -0
- package/dist/host/src/serial.js +32 -0
- package/dist/host/src/server.js +451 -0
- package/dist/host/src/types.js +5 -0
- package/dist/host/src/validate-folder.js +68 -0
- package/dist/host/src/write-note.js +158 -0
- package/dist/host/stikfix-init.cjs +500 -0
- package/dist/host/stikfix-native.cjs +257 -0
- package/package.json +66 -0
- package/public/icon/128.png +0 -0
- package/public/icon/16.png +0 -0
- package/public/icon/256.png +0 -0
- package/public/icon/32.png +0 -0
- package/public/icon/48.png +0 -0
- package/public/icon/stikfix.ico +0 -0
- package/public/icon/stikfix.svg +47 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// host/src/native-host.ts
|
|
21
|
+
var native_host_exports = {};
|
|
22
|
+
__export(native_host_exports, {
|
|
23
|
+
handlePickFolder: () => handlePickFolder,
|
|
24
|
+
main: () => main,
|
|
25
|
+
validateChosenFolder: () => validateChosenFolder
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(native_host_exports);
|
|
28
|
+
var import_node_fs2 = require("node:fs");
|
|
29
|
+
var import_node_path2 = require("node:path");
|
|
30
|
+
var import_node_os = require("node:os");
|
|
31
|
+
|
|
32
|
+
// host/src/native-msg.ts
|
|
33
|
+
function encodeNativeMessage(msg) {
|
|
34
|
+
const json = JSON.stringify(msg);
|
|
35
|
+
const body = Buffer.from(json, "utf8");
|
|
36
|
+
const header = Buffer.alloc(4);
|
|
37
|
+
header.writeUInt32LE(body.length, 0);
|
|
38
|
+
return Buffer.concat([header, body]);
|
|
39
|
+
}
|
|
40
|
+
function decodeNativeMessages(buf) {
|
|
41
|
+
const messages = [];
|
|
42
|
+
let pos = 0;
|
|
43
|
+
while (buf.length - pos >= 4) {
|
|
44
|
+
const msgLen = buf.readUInt32LE(pos);
|
|
45
|
+
if (buf.length - pos < 4 + msgLen) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const jsonSlice = buf.slice(pos + 4, pos + 4 + msgLen).toString("utf8");
|
|
49
|
+
pos += 4 + msgLen;
|
|
50
|
+
try {
|
|
51
|
+
messages.push(JSON.parse(jsonSlice));
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return { messages, rest: pos > 0 ? buf.slice(pos) : buf };
|
|
56
|
+
}
|
|
57
|
+
function sendNativeMessage(msg, out = process.stdout) {
|
|
58
|
+
out.write(encodeNativeMessage(msg));
|
|
59
|
+
}
|
|
60
|
+
function readNativeMessages(onMessage, inp = process.stdin) {
|
|
61
|
+
let buf = Buffer.alloc(0);
|
|
62
|
+
inp.on("data", (chunk) => {
|
|
63
|
+
buf = Buffer.concat([buf, chunk]);
|
|
64
|
+
const { messages, rest } = decodeNativeMessages(buf);
|
|
65
|
+
buf = Buffer.from(rest);
|
|
66
|
+
for (const msg of messages) {
|
|
67
|
+
onMessage(msg);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
inp.on("end", () => {
|
|
71
|
+
process.exit(0);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// host/src/folder-picker.ts
|
|
76
|
+
var import_node_child_process = require("node:child_process");
|
|
77
|
+
function buildPickerArgs(plat = process.platform, title = "Choose folder") {
|
|
78
|
+
switch (plat) {
|
|
79
|
+
case "win32": {
|
|
80
|
+
const safeTitle = title.replace(/'/g, "''");
|
|
81
|
+
return {
|
|
82
|
+
cmd: "powershell.exe",
|
|
83
|
+
args: [
|
|
84
|
+
"-NoProfile",
|
|
85
|
+
"-NonInteractive",
|
|
86
|
+
"-OutputFormat",
|
|
87
|
+
"Text",
|
|
88
|
+
"-Command",
|
|
89
|
+
`Add-Type -AssemblyName System.Windows.Forms;$owner = New-Object System.Windows.Forms.Form;$owner.TopMost = $true; $owner.ShowInTaskbar = $false;$owner.Opacity = 0; $owner.Show(); $owner.Activate();$d = New-Object System.Windows.Forms.FolderBrowserDialog;$d.Description = '${safeTitle}';$null = $d.ShowDialog($owner);$owner.Dispose();$d.SelectedPath`
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case "darwin": {
|
|
94
|
+
return {
|
|
95
|
+
cmd: "osascript",
|
|
96
|
+
args: ["-e", `choose folder with prompt "Choose a project folder"`]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case "linux":
|
|
100
|
+
default: {
|
|
101
|
+
return {
|
|
102
|
+
cmd: "zenity",
|
|
103
|
+
args: ["--file-selection", "--directory", `--title=${title}`]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function pickFolder(title = "Choose a project folder", plat = process.platform) {
|
|
109
|
+
if (plat === "darwin") {
|
|
110
|
+
return new Promise((resolve2) => {
|
|
111
|
+
const { args } = buildPickerArgs("darwin", title);
|
|
112
|
+
(0, import_node_child_process.execFile)("osascript", args, { timeout: 12e4 }, (err, stdout) => {
|
|
113
|
+
if (err || !stdout.trim()) {
|
|
114
|
+
resolve2(null);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const raw = stdout.trim();
|
|
118
|
+
(0, import_node_child_process.execFile)(
|
|
119
|
+
"osascript",
|
|
120
|
+
["-e", `POSIX path of (${raw})`],
|
|
121
|
+
{},
|
|
122
|
+
(e2, out2) => {
|
|
123
|
+
resolve2(e2 ? null : out2.trim() || null);
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (plat === "win32") {
|
|
130
|
+
const { cmd, args } = buildPickerArgs("win32", title);
|
|
131
|
+
return new Promise((resolve2) => {
|
|
132
|
+
(0, import_node_child_process.execFile)(cmd, args, { timeout: 12e4 }, (err, stdout) => {
|
|
133
|
+
resolve2(err ? null : stdout.trim() || null);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const zenityResult = await tryLinuxPicker("zenity", title);
|
|
138
|
+
if (zenityResult !== void 0) return zenityResult;
|
|
139
|
+
const kdialogResult = await tryLinuxPicker("kdialog", title);
|
|
140
|
+
if (kdialogResult !== void 0) return kdialogResult;
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function tryLinuxPicker(tool, title) {
|
|
144
|
+
return new Promise((resolve2) => {
|
|
145
|
+
const args = tool === "zenity" ? ["--file-selection", "--directory", `--title=${title}`] : ["--getexistingdirectory", "/home"];
|
|
146
|
+
(0, import_node_child_process.execFile)(tool, args, { timeout: 12e4 }, (err, stdout) => {
|
|
147
|
+
if (err) {
|
|
148
|
+
const e = err;
|
|
149
|
+
if (e.code === "ENOENT") {
|
|
150
|
+
resolve2(void 0);
|
|
151
|
+
} else {
|
|
152
|
+
resolve2(null);
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
resolve2(stdout.trim() || null);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// host/src/validate-folder.ts
|
|
162
|
+
var import_node_fs = require("node:fs");
|
|
163
|
+
var import_node_path = require("node:path");
|
|
164
|
+
var SYSTEM_DIRS_NIX = ["/", "/System", "/usr", "/etc"];
|
|
165
|
+
var SYSTEM_DIRS_WIN = ["C:\\Windows", "C:\\Program Files"];
|
|
166
|
+
function validateChosenFolder(folder, plat = process.platform) {
|
|
167
|
+
if (folder === null || typeof folder !== "string" || folder.length === 0) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
if (!(0, import_node_path.isAbsolute)(folder)) return null;
|
|
171
|
+
try {
|
|
172
|
+
if (!(0, import_node_fs.existsSync)(folder)) return null;
|
|
173
|
+
if (!(0, import_node_fs.statSync)(folder).isDirectory()) return null;
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const normalized = (0, import_node_path.resolve)(folder);
|
|
178
|
+
const denyList = plat === "win32" ? SYSTEM_DIRS_WIN : SYSTEM_DIRS_NIX;
|
|
179
|
+
const cmp = plat === "win32" ? normalized.toLowerCase() : normalized;
|
|
180
|
+
for (const sysDir of denyList) {
|
|
181
|
+
const sysCmp = plat === "win32" ? (0, import_node_path.resolve)(sysDir).toLowerCase() : (0, import_node_path.resolve)(sysDir);
|
|
182
|
+
if (cmp === sysCmp) return null;
|
|
183
|
+
}
|
|
184
|
+
return normalized;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// host/src/native-host.ts
|
|
188
|
+
var CONFIG_PATH = (0, import_node_path2.join)((0, import_node_os.homedir)(), ".config", "stikfix", "config.json");
|
|
189
|
+
async function handlePickFolder(origin, pickFn = pickFolder, plat = process.platform, out) {
|
|
190
|
+
let chosen;
|
|
191
|
+
try {
|
|
192
|
+
chosen = await pickFn("Choose a folder for " + (origin ?? "this site"));
|
|
193
|
+
} catch {
|
|
194
|
+
chosen = null;
|
|
195
|
+
}
|
|
196
|
+
const folder = validateChosenFolder(chosen, plat);
|
|
197
|
+
if (out) {
|
|
198
|
+
sendNativeMessage({ type: "FOLDER_PICKED", origin, folder }, out);
|
|
199
|
+
} else {
|
|
200
|
+
sendNativeMessage({ type: "FOLDER_PICKED", origin, folder });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function main() {
|
|
204
|
+
let cfg;
|
|
205
|
+
try {
|
|
206
|
+
cfg = JSON.parse((0, import_node_fs2.readFileSync)(CONFIG_PATH, "utf8"));
|
|
207
|
+
} catch {
|
|
208
|
+
sendNativeMessage({ type: "ERROR", error: "Config not found. Run: npx stikfix init" });
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
readNativeMessages((msg) => {
|
|
212
|
+
const m = msg;
|
|
213
|
+
if (m.type === "GET_TOKEN") {
|
|
214
|
+
let token;
|
|
215
|
+
try {
|
|
216
|
+
token = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(cfg.root, ".stikfix-token"), "utf8").trim();
|
|
217
|
+
} catch {
|
|
218
|
+
sendNativeMessage({ type: "ERROR", error: ".stikfix-token not found. Start the host first." });
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
let port;
|
|
222
|
+
try {
|
|
223
|
+
const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(cfg.root, ".stikfix-port"), "utf8").trim();
|
|
224
|
+
const parsed = parseInt(raw, 10);
|
|
225
|
+
if (!isNaN(parsed)) {
|
|
226
|
+
port = parsed;
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
sendNativeMessage({
|
|
231
|
+
type: "TOKEN",
|
|
232
|
+
token,
|
|
233
|
+
port,
|
|
234
|
+
name: cfg.name,
|
|
235
|
+
notesDir: cfg.notesDir
|
|
236
|
+
});
|
|
237
|
+
process.exit(0);
|
|
238
|
+
}
|
|
239
|
+
if (m.type === "PICK_FOLDER") {
|
|
240
|
+
handlePickFolder(m.origin).then(
|
|
241
|
+
() => process.exit(0),
|
|
242
|
+
() => process.exit(0)
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
process.exit(0);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (typeof require !== "undefined" && typeof module !== "undefined" && require.main === module) {
|
|
250
|
+
main();
|
|
251
|
+
}
|
|
252
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
253
|
+
0 && (module.exports = {
|
|
254
|
+
handlePickFolder,
|
|
255
|
+
main,
|
|
256
|
+
validateChosenFolder
|
|
257
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stikfix",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Pin sticky notes on any page — your AI reads them.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Omer Nesher <omernesher@gmail.com>",
|
|
7
|
+
"homepage": "https://github.com/omernesh/stikfix#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/omernesh/stikfix.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/omernesh/stikfix/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"chrome-extension",
|
|
17
|
+
"sticky-notes",
|
|
18
|
+
"annotations",
|
|
19
|
+
"code-review",
|
|
20
|
+
"ai",
|
|
21
|
+
"ai-agent",
|
|
22
|
+
"mv3",
|
|
23
|
+
"native-messaging",
|
|
24
|
+
"markdown"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"stikfix": "dist/host/stikfix-init.cjs"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist/host/src",
|
|
35
|
+
"dist/host/stikfix-init.cjs",
|
|
36
|
+
"dist/host/stikfix-native.cjs",
|
|
37
|
+
"public/icon",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"dev": "wxt",
|
|
46
|
+
"build:host-bin": "npx esbuild bin/stikfix.ts --platform=node --format=cjs --bundle \"--external:node:*\" --outfile=dist/host/stikfix-init.cjs && npx esbuild host/src/native-host.ts --platform=node --format=cjs --bundle \"--external:node:*\" --outfile=dist/host/stikfix-native.cjs",
|
|
47
|
+
"build": "wxt build && tsc -p tsconfig.host.json && npm run build:host-bin",
|
|
48
|
+
"build:firefox": "wxt build -b firefox && tsc -p tsconfig.host.json && npm run build:host-bin",
|
|
49
|
+
"host": "node dist/host/src/index.js",
|
|
50
|
+
"test:lib": "tsc -p tsconfig.lib.json && node --test \"dist/lib/lib/test/**/*.test.js\"",
|
|
51
|
+
"test": "tsc -p tsconfig.host.json && node --test \"dist/host/test/**/*.test.js\"",
|
|
52
|
+
"check": "tsc --noEmit && tsc --noEmit -p tsconfig.host.json && node scripts/clean-room-check.mjs && node scripts/host-smoke-test.mjs && npm run test:lib && npm test",
|
|
53
|
+
"postinstall": "node -e \"try{require.resolve('wxt')}catch(e){process.exit(0)}\" && wxt prepare"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@medv/finder": "4.0.2",
|
|
57
|
+
"@types/chrome": "0.1.42",
|
|
58
|
+
"@types/node": "25.9.1",
|
|
59
|
+
"interactjs": "1.10.27",
|
|
60
|
+
"typescript": "6.0.3",
|
|
61
|
+
"wxt": "0.20.26"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"yaml": "2.9.0"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="512" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0" stop-color="#5B57E6"/>
|
|
5
|
+
<stop offset="1" stop-color="#7C3AED"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<linearGradient id="note" x1="0" y1="0" x2="0" y2="252" gradientUnits="userSpaceOnUse">
|
|
8
|
+
<stop offset="0" stop-color="#FDE047"/>
|
|
9
|
+
<stop offset="1" stop-color="#FACC15"/>
|
|
10
|
+
</linearGradient>
|
|
11
|
+
</defs>
|
|
12
|
+
|
|
13
|
+
<!-- App background (squircle) -->
|
|
14
|
+
<rect width="512" height="512" rx="116" fill="url(#bg)"/>
|
|
15
|
+
|
|
16
|
+
<!-- Sticky note (tilted), with soft shadow and a folded top-right corner -->
|
|
17
|
+
<g transform="translate(146 168) rotate(-7 118 126)">
|
|
18
|
+
<!-- shadow -->
|
|
19
|
+
<path d="M0,28 Q0,8 20,8 L176,8 L236,68 L236,236 Q236,256 216,256 L20,256 Q0,256 0,236 Z"
|
|
20
|
+
fill="#000000" opacity="0.20" transform="translate(8 14)"/>
|
|
21
|
+
<!-- body -->
|
|
22
|
+
<path d="M0,20 Q0,0 20,0 L176,0 L236,60 L236,232 Q236,252 216,252 L20,252 Q0,252 0,232 Z"
|
|
23
|
+
fill="url(#note)"/>
|
|
24
|
+
<!-- folded corner -->
|
|
25
|
+
<path d="M176,0 L236,60 L188,60 Q176,60 176,48 Z" fill="#EAB308"/>
|
|
26
|
+
<!-- text lines -->
|
|
27
|
+
<g fill="#92400E" opacity="0.55">
|
|
28
|
+
<rect x="34" y="92" width="150" height="16" rx="8"/>
|
|
29
|
+
<rect x="34" y="128" width="168" height="16" rx="8"/>
|
|
30
|
+
<rect x="34" y="164" width="104" height="16" rx="8"/>
|
|
31
|
+
</g>
|
|
32
|
+
</g>
|
|
33
|
+
|
|
34
|
+
<!-- Red map-pin pinning the note -->
|
|
35
|
+
<g>
|
|
36
|
+
<!-- shadow -->
|
|
37
|
+
<path d="M296,156 A52,52 0 1 1 400,156 Q400,229 348,290 Q296,229 296,156 Z"
|
|
38
|
+
fill="#000000" opacity="0.20" transform="translate(7 12)"/>
|
|
39
|
+
<!-- pin body -->
|
|
40
|
+
<path d="M296,156 A52,52 0 1 1 400,156 Q400,229 348,290 Q296,229 296,156 Z"
|
|
41
|
+
fill="#F43F5E"/>
|
|
42
|
+
<!-- pin highlight -->
|
|
43
|
+
<path d="M315,140 A33,33 0 0 1 360,118" stroke="#FFFFFF" stroke-opacity="0.5" stroke-width="10" stroke-linecap="round" fill="none"/>
|
|
44
|
+
<!-- inner hole -->
|
|
45
|
+
<circle cx="348" cy="156" r="22" fill="#FFFFFF"/>
|
|
46
|
+
</g>
|
|
47
|
+
</svg>
|