skilldrop-cli 1.1.0 → 1.1.1

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.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/packs/ai-engineering/skills/judge-audit/SKILL.md +24 -6
  4. package/packs/ai-engineering/skills/judge-audit/evals/evals.json +29 -0
  5. package/packs/ai-engineering/skills/judge-audit/evals/files/10/judge_eval.csv +61 -0
  6. package/packs/ai-engineering/skills/judge-audit/evals/files/11/mod_sample_scored.csv +201 -0
  7. package/packs/ai-engineering/skills/judge-audit/manifest.json +1 -1
  8. package/packs/converters/skills/md-to-pptx/SKILL.md +25 -8
  9. package/packs/converters/skills/md-to-pptx/evals/evals.json +30 -0
  10. package/packs/converters/skills/md-to-pptx/evals/files/6/q3-status-report.md +72 -0
  11. package/packs/converters/skills/md-to-pptx/evals/files/7/runbooks/db-failover.md +59 -0
  12. package/packs/converters/skills/md-to-pptx/evals/files/7/runbooks/diagrams/topology.png +0 -0
  13. package/packs/converters/skills/md-to-pptx/manifest.json +1 -1
  14. package/packs/converters/skills/md-to-pptx/reference.md +9 -1
  15. package/packs/converters/skills/md-to-pptx/scripts/md_to_pptx.py +35 -2
  16. package/packs/frontend/skills/bundle-size-review/SKILL.md +5 -2
  17. package/packs/frontend/skills/bundle-size-review/evals/evals.json +30 -0
  18. package/packs/frontend/skills/bundle-size-review/evals/files/8/.browserslistrc +6 -0
  19. package/packs/frontend/skills/bundle-size-review/evals/files/8/babel.config.js +6 -0
  20. package/packs/frontend/skills/bundle-size-review/evals/files/8/dist/stats.json +854 -0
  21. package/packs/frontend/skills/bundle-size-review/evals/files/8/package-lock.json.fixture +73 -0
  22. package/packs/frontend/skills/bundle-size-review/evals/files/8/package.json.fixture +28 -0
  23. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/App.jsx +23 -0
  24. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/BookingForm.jsx +18 -0
  25. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/admin/Reports.jsx +17 -0
  26. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/app/index.jsx +4 -0
  27. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/landing/Hero.jsx +17 -0
  28. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/landing/index.jsx +4 -0
  29. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/polyfills.js +3 -0
  30. package/packs/frontend/skills/bundle-size-review/evals/files/8/src/utils/format.js +4 -0
  31. package/packs/frontend/skills/bundle-size-review/evals/files/8/webpack.config.js +35 -0
  32. package/packs/frontend/skills/bundle-size-review/evals/files/9/.github/workflows/size.yml.fixture +12 -0
  33. package/packs/frontend/skills/bundle-size-review/evals/files/9/build.mjs +17 -0
  34. package/packs/frontend/skills/bundle-size-review/evals/files/9/meta.main.json +100 -0
  35. package/packs/frontend/skills/bundle-size-review/evals/files/9/meta.pr412.json +196 -0
  36. package/packs/frontend/skills/bundle-size-review/evals/files/9/package.json.fixture +17 -0
  37. package/packs/frontend/skills/bundle-size-review/evals/files/9/scripts/check-size.mjs +11 -0
  38. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/api.ts +11 -0
  39. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/composer.tsx +21 -0
  40. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/emoji/data.ts +8 -0
  41. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/emoji/picker.tsx +22 -0
  42. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/styles.ts +11 -0
  43. package/packs/frontend/skills/bundle-size-review/evals/files/9/src/widget.ts +15 -0
  44. package/packs/frontend/skills/bundle-size-review/manifest.json +1 -1
  45. package/packs/solution-architect/skills/event-contract-draft/SKILL.md +36 -8
  46. package/packs/solution-architect/skills/event-contract-draft/evals/evals.json +30 -0
  47. package/packs/solution-architect/skills/event-contract-draft/evals/files/6/infra/topics.yaml +10 -0
  48. package/packs/solution-architect/skills/event-contract-draft/evals/files/6/src/checkout/order_events.py +57 -0
  49. package/packs/solution-architect/skills/event-contract-draft/evals/files/7/docs/webhooks.yaml +40 -0
  50. package/packs/solution-architect/skills/event-contract-draft/manifest.json +1 -1
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "harbourline-web",
3
+ "version": "3.8.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "harbourline-web",
9
+ "version": "3.8.0",
10
+ "dependencies": {
11
+ "@northwind/datepicker": "^1.4.2",
12
+ "date-fns": "^2.30.0",
13
+ "lodash": "^4.17.21",
14
+ "moment": "^2.29.4",
15
+ "promise-polyfill": "^8.3.0",
16
+ "react": "^18.3.1",
17
+ "react-dom": "^18.3.1",
18
+ "whatwg-fetch": "^3.6.20"
19
+ }
20
+ },
21
+ "node_modules/@babel/runtime": {
22
+ "version": "7.25.6",
23
+ "dependencies": { "regenerator-runtime": "^0.14.0" }
24
+ },
25
+ "node_modules/@northwind/datepicker": {
26
+ "version": "1.4.2",
27
+ "dependencies": { "date-fns": "^3.6.0" },
28
+ "peerDependencies": { "react": ">=17" }
29
+ },
30
+ "node_modules/@northwind/datepicker/node_modules/date-fns": {
31
+ "version": "3.6.0"
32
+ },
33
+ "node_modules/date-fns": {
34
+ "version": "2.30.0",
35
+ "dependencies": { "@babel/runtime": "^7.21.0" }
36
+ },
37
+ "node_modules/js-tokens": {
38
+ "version": "4.0.0"
39
+ },
40
+ "node_modules/lodash": {
41
+ "version": "4.17.21"
42
+ },
43
+ "node_modules/loose-envify": {
44
+ "version": "1.4.0",
45
+ "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }
46
+ },
47
+ "node_modules/moment": {
48
+ "version": "2.29.4"
49
+ },
50
+ "node_modules/promise-polyfill": {
51
+ "version": "8.3.0"
52
+ },
53
+ "node_modules/react": {
54
+ "version": "18.3.1",
55
+ "dependencies": { "loose-envify": "^1.1.0" }
56
+ },
57
+ "node_modules/react-dom": {
58
+ "version": "18.3.1",
59
+ "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" },
60
+ "peerDependencies": { "react": "^18.3.1" }
61
+ },
62
+ "node_modules/regenerator-runtime": {
63
+ "version": "0.14.1"
64
+ },
65
+ "node_modules/scheduler": {
66
+ "version": "0.23.2",
67
+ "dependencies": { "loose-envify": "^1.1.0" }
68
+ },
69
+ "node_modules/whatwg-fetch": {
70
+ "version": "3.6.20"
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "harbourline-web",
3
+ "version": "3.8.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "webpack --mode production",
7
+ "build:stats": "webpack --mode production --json=dist/stats.json"
8
+ },
9
+ "dependencies": {
10
+ "@northwind/datepicker": "^1.4.2",
11
+ "date-fns": "^2.30.0",
12
+ "lodash": "^4.17.21",
13
+ "moment": "^2.29.4",
14
+ "promise-polyfill": "^8.3.0",
15
+ "react": "^18.3.1",
16
+ "react-dom": "^18.3.1",
17
+ "whatwg-fetch": "^3.6.20"
18
+ },
19
+ "devDependencies": {
20
+ "@babel/core": "^7.25.2",
21
+ "@babel/preset-env": "^7.25.4",
22
+ "@babel/preset-react": "^7.24.7",
23
+ "babel-loader": "^9.1.3",
24
+ "html-webpack-plugin": "^5.6.0",
25
+ "webpack": "^5.94.0",
26
+ "webpack-cli": "^5.1.4"
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import { lazy, Suspense, useState } from 'react';
2
+ import BookingForm from './BookingForm';
3
+
4
+ const Reports = lazy(() => import(/* webpackChunkName: "admin" */ './admin/Reports'));
5
+
6
+ export default function App() {
7
+ const [view, setView] = useState(window.location.hash === '#admin' ? 'admin' : 'book');
8
+ return (
9
+ <main>
10
+ <nav>
11
+ <button onClick={() => setView('book')}>Book</button>
12
+ <button onClick={() => setView('admin')}>Staff reports</button>
13
+ </nav>
14
+ {view === 'book' ? (
15
+ <BookingForm />
16
+ ) : (
17
+ <Suspense fallback={<p>Loading reports…</p>}>
18
+ <Reports />
19
+ </Suspense>
20
+ )}
21
+ </main>
22
+ );
23
+ }
@@ -0,0 +1,18 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { DatePicker } from '@northwind/datepicker';
3
+ import { format, parseISO } from 'date-fns';
4
+ import { debounce } from '../utils/format';
5
+
6
+ export default function BookingForm() {
7
+ const [departure, setDeparture] = useState('2027-06-14');
8
+ const [query, setQuery] = useState('');
9
+ const search = useMemo(() => debounce((q) => fetch(`/api/cabins?q=${encodeURIComponent(q)}`), 300), []);
10
+
11
+ return (
12
+ <form>
13
+ <DatePicker value={parseISO(departure)} onChange={(d) => setDeparture(format(d, 'yyyy-MM-dd'))} />
14
+ <p>Departing {format(parseISO(departure), 'EEEE d MMMM yyyy')}</p>
15
+ <input value={query} onChange={(e) => { setQuery(e.target.value); search(e.target.value); }} />
16
+ </form>
17
+ );
18
+ }
@@ -0,0 +1,17 @@
1
+ import moment from 'moment';
2
+
3
+ export default function Reports({ rows = [] }) {
4
+ return (
5
+ <table>
6
+ <tbody>
7
+ {rows.map((r) => (
8
+ <tr key={r.id}>
9
+ <td>{r.ship}</td>
10
+ <td>{moment(r.sailedAt).format('LL')}</td>
11
+ <td>{moment(r.sailedAt).fromNow()}</td>
12
+ </tr>
13
+ ))}
14
+ </tbody>
15
+ </table>
16
+ );
17
+ }
@@ -0,0 +1,4 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import App from './App';
3
+
4
+ createRoot(document.getElementById('root')).render(<App />);
@@ -0,0 +1,17 @@
1
+ import { useState } from 'react';
2
+ import { titleCase } from '../utils/format';
3
+
4
+ export default function Hero() {
5
+ const [email, setEmail] = useState('');
6
+ const headline = titleCase('SAIL THE NORTHERN FJORDS THIS SUMMER');
7
+ return (
8
+ <section className="hero">
9
+ <h1>{headline}</h1>
10
+ <p>Small-ship cruises from Bergen and Tromsø, May to September.</p>
11
+ <form action="/api/waitlist" method="post">
12
+ <input type="email" name="email" value={email} onChange={(e) => setEmail(e.target.value)} />
13
+ <button type="submit">Join the waitlist</button>
14
+ </form>
15
+ </section>
16
+ );
17
+ }
@@ -0,0 +1,4 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import Hero from './Hero';
3
+
4
+ createRoot(document.getElementById('root')).render(<Hero />);
@@ -0,0 +1,3 @@
1
+ // Added in 2019 when we still had IE11 customers. Keep first so globals exist.
2
+ import 'whatwg-fetch';
3
+ import 'promise-polyfill/src/polyfill';
@@ -0,0 +1,4 @@
1
+ import _ from 'lodash';
2
+
3
+ export const titleCase = (s) => _.startCase(_.toLower(s));
4
+ export const debounce = (fn, ms) => _.debounce(fn, ms);
@@ -0,0 +1,35 @@
1
+ const path = require('path');
2
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
3
+
4
+ module.exports = {
5
+ entry: {
6
+ app: ['./src/polyfills.js', './src/app/index.jsx'],
7
+ landing: ['./src/polyfills.js', './src/landing/index.jsx'],
8
+ },
9
+ output: {
10
+ path: path.resolve(__dirname, 'dist'),
11
+ filename: '[name].[contenthash:6].js',
12
+ publicPath: '/static/',
13
+ },
14
+ resolve: { extensions: ['.js', '.jsx'] },
15
+ module: {
16
+ rules: [{ test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' }],
17
+ },
18
+ optimization: {
19
+ runtimeChunk: 'single',
20
+ splitChunks: {
21
+ cacheGroups: {
22
+ // one cacheable vendor file for everything
23
+ vendors: {
24
+ test: /[\\/]node_modules[\\/]/,
25
+ name: 'vendors',
26
+ chunks: 'initial',
27
+ },
28
+ },
29
+ },
30
+ },
31
+ plugins: [
32
+ new HtmlWebpackPlugin({ filename: 'index.html', chunks: ['app'] }),
33
+ new HtmlWebpackPlugin({ filename: 'welcome.html', chunks: ['landing'] }),
34
+ ],
35
+ };
@@ -0,0 +1,12 @@
1
+ name: size
2
+ on: [pull_request]
3
+ jobs:
4
+ size:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - uses: actions/setup-node@v4
9
+ with: { node-version: 20 }
10
+ - run: npm ci
11
+ - run: npm run build
12
+ - run: npm run size
@@ -0,0 +1,17 @@
1
+ import { build } from 'esbuild';
2
+ import { writeFileSync } from 'node:fs';
3
+
4
+ const result = await build({
5
+ entryPoints: ['src/widget.ts'],
6
+ bundle: true,
7
+ splitting: true,
8
+ format: 'esm',
9
+ minify: true,
10
+ target: ['es2020'],
11
+ outdir: 'dist',
12
+ jsxFactory: 'h',
13
+ jsxFragment: 'Fragment',
14
+ metafile: true,
15
+ });
16
+
17
+ writeFileSync(process.env.META_OUT ?? 'meta.json', JSON.stringify(result.metafile, null, 2));
@@ -0,0 +1,100 @@
1
+ {
2
+ "inputs": {
3
+ "node_modules/preact/dist/preact.module.js": {
4
+ "bytes": 11480,
5
+ "imports": [],
6
+ "format": "esm"
7
+ },
8
+ "node_modules/preact/hooks/dist/hooks.module.js": {
9
+ "bytes": 3620,
10
+ "imports": [
11
+ {
12
+ "path": "node_modules/preact/dist/preact.module.js",
13
+ "kind": "import-statement",
14
+ "original": "preact"
15
+ }
16
+ ],
17
+ "format": "esm"
18
+ },
19
+ "src/api.ts": {
20
+ "bytes": 3960,
21
+ "imports": [],
22
+ "format": "esm"
23
+ },
24
+ "src/composer.tsx": {
25
+ "bytes": 6850,
26
+ "imports": [
27
+ {
28
+ "path": "node_modules/preact/dist/preact.module.js",
29
+ "kind": "import-statement",
30
+ "original": "preact"
31
+ },
32
+ {
33
+ "path": "node_modules/preact/hooks/dist/hooks.module.js",
34
+ "kind": "import-statement",
35
+ "original": "preact/hooks"
36
+ },
37
+ {
38
+ "path": "src/api.ts",
39
+ "kind": "import-statement",
40
+ "original": "./api"
41
+ }
42
+ ],
43
+ "format": "esm"
44
+ },
45
+ "src/styles.ts": {
46
+ "bytes": 4210,
47
+ "imports": [],
48
+ "format": "esm"
49
+ },
50
+ "src/widget.ts": {
51
+ "bytes": 1420,
52
+ "imports": [
53
+ {
54
+ "path": "node_modules/preact/dist/preact.module.js",
55
+ "kind": "import-statement",
56
+ "original": "preact"
57
+ },
58
+ {
59
+ "path": "src/composer.tsx",
60
+ "kind": "import-statement",
61
+ "original": "./composer"
62
+ },
63
+ {
64
+ "path": "src/styles.ts",
65
+ "kind": "import-statement",
66
+ "original": "./styles"
67
+ }
68
+ ],
69
+ "format": "esm"
70
+ }
71
+ },
72
+ "outputs": {
73
+ "dist/widget.js": {
74
+ "imports": [],
75
+ "exports": [],
76
+ "entryPoint": "src/widget.ts",
77
+ "inputs": {
78
+ "src/widget.ts": {
79
+ "bytesInOutput": 612
80
+ },
81
+ "src/composer.tsx": {
82
+ "bytesInOutput": 3108
83
+ },
84
+ "src/api.ts": {
85
+ "bytesInOutput": 1874
86
+ },
87
+ "src/styles.ts": {
88
+ "bytesInOutput": 3950
89
+ },
90
+ "node_modules/preact/dist/preact.module.js": {
91
+ "bytesInOutput": 10214
92
+ },
93
+ "node_modules/preact/hooks/dist/hooks.module.js": {
94
+ "bytesInOutput": 1436
95
+ }
96
+ },
97
+ "bytes": 21312
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,196 @@
1
+ {
2
+ "inputs": {
3
+ "node_modules/preact/dist/preact.module.js": {
4
+ "bytes": 11480,
5
+ "imports": [],
6
+ "format": "esm"
7
+ },
8
+ "node_modules/preact/hooks/dist/hooks.module.js": {
9
+ "bytes": 3620,
10
+ "imports": [
11
+ {
12
+ "path": "node_modules/preact/dist/preact.module.js",
13
+ "kind": "import-statement",
14
+ "original": "preact"
15
+ }
16
+ ],
17
+ "format": "esm"
18
+ },
19
+ "src/api.ts": {
20
+ "bytes": 3960,
21
+ "imports": [],
22
+ "format": "esm"
23
+ },
24
+ "src/composer.tsx": {
25
+ "bytes": 7720,
26
+ "imports": [
27
+ {
28
+ "path": "node_modules/preact/dist/preact.module.js",
29
+ "kind": "import-statement",
30
+ "original": "preact"
31
+ },
32
+ {
33
+ "path": "node_modules/preact/hooks/dist/hooks.module.js",
34
+ "kind": "import-statement",
35
+ "original": "preact/hooks"
36
+ },
37
+ {
38
+ "path": "src/api.ts",
39
+ "kind": "import-statement",
40
+ "original": "./api"
41
+ },
42
+ {
43
+ "path": "src/emoji/data.ts",
44
+ "kind": "import-statement",
45
+ "original": "./emoji/data"
46
+ }
47
+ ],
48
+ "format": "esm"
49
+ },
50
+ "src/emoji/data.ts": {
51
+ "bytes": 690,
52
+ "imports": [
53
+ {
54
+ "path": "src/emoji/emoji-data.json",
55
+ "kind": "import-statement",
56
+ "original": "./emoji-data.json"
57
+ }
58
+ ],
59
+ "format": "esm"
60
+ },
61
+ "src/emoji/emoji-data.json": {
62
+ "bytes": 142380,
63
+ "imports": [],
64
+ "format": "esm",
65
+ "with": {}
66
+ },
67
+ "src/emoji/picker.tsx": {
68
+ "bytes": 9140,
69
+ "imports": [
70
+ {
71
+ "path": "node_modules/preact/dist/preact.module.js",
72
+ "kind": "import-statement",
73
+ "original": "preact"
74
+ },
75
+ {
76
+ "path": "node_modules/preact/hooks/dist/hooks.module.js",
77
+ "kind": "import-statement",
78
+ "original": "preact/hooks"
79
+ },
80
+ {
81
+ "path": "src/emoji/data.ts",
82
+ "kind": "import-statement",
83
+ "original": "./data"
84
+ }
85
+ ],
86
+ "format": "esm"
87
+ },
88
+ "src/styles.ts": {
89
+ "bytes": 4210,
90
+ "imports": [],
91
+ "format": "esm"
92
+ },
93
+ "src/widget.ts": {
94
+ "bytes": 1610,
95
+ "imports": [
96
+ {
97
+ "path": "node_modules/preact/dist/preact.module.js",
98
+ "kind": "import-statement",
99
+ "original": "preact"
100
+ },
101
+ {
102
+ "path": "src/composer.tsx",
103
+ "kind": "import-statement",
104
+ "original": "./composer"
105
+ },
106
+ {
107
+ "path": "src/styles.ts",
108
+ "kind": "import-statement",
109
+ "original": "./styles"
110
+ },
111
+ {
112
+ "path": "src/emoji/picker.tsx",
113
+ "kind": "dynamic-import",
114
+ "original": "./emoji/picker"
115
+ }
116
+ ],
117
+ "format": "esm"
118
+ }
119
+ },
120
+ "outputs": {
121
+ "dist/widget.js": {
122
+ "imports": [
123
+ {
124
+ "path": "dist/chunk-R4N7QW2L.js",
125
+ "kind": "import-statement"
126
+ },
127
+ {
128
+ "path": "dist/picker-6YB3KD5M.js",
129
+ "kind": "dynamic-import"
130
+ }
131
+ ],
132
+ "exports": [],
133
+ "entryPoint": "src/widget.ts",
134
+ "inputs": {
135
+ "src/widget.ts": {
136
+ "bytesInOutput": 688
137
+ },
138
+ "src/composer.tsx": {
139
+ "bytesInOutput": 3467
140
+ },
141
+ "src/api.ts": {
142
+ "bytesInOutput": 1874
143
+ },
144
+ "src/styles.ts": {
145
+ "bytesInOutput": 3950
146
+ }
147
+ },
148
+ "bytes": 10215
149
+ },
150
+ "dist/picker-6YB3KD5M.js": {
151
+ "imports": [
152
+ {
153
+ "path": "dist/chunk-R4N7QW2L.js",
154
+ "kind": "import-statement"
155
+ }
156
+ ],
157
+ "exports": [
158
+ "default"
159
+ ],
160
+ "entryPoint": "src/emoji/picker.tsx",
161
+ "inputs": {
162
+ "src/emoji/picker.tsx": {
163
+ "bytesInOutput": 5830
164
+ }
165
+ },
166
+ "bytes": 5920
167
+ },
168
+ "dist/chunk-R4N7QW2L.js": {
169
+ "imports": [],
170
+ "exports": [
171
+ "EMOJI",
172
+ "SHORTCODES",
173
+ "h",
174
+ "render",
175
+ "useEffect",
176
+ "useRef",
177
+ "useState"
178
+ ],
179
+ "inputs": {
180
+ "node_modules/preact/dist/preact.module.js": {
181
+ "bytesInOutput": 10214
182
+ },
183
+ "node_modules/preact/hooks/dist/hooks.module.js": {
184
+ "bytesInOutput": 1436
185
+ },
186
+ "src/emoji/emoji-data.json": {
187
+ "bytesInOutput": 96512
188
+ },
189
+ "src/emoji/data.ts": {
190
+ "bytesInOutput": 402
191
+ }
192
+ },
193
+ "bytes": 108695
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@parcelpost/chat-widget",
3
+ "version": "2.6.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "node build.mjs",
8
+ "size": "node scripts/check-size.mjs"
9
+ },
10
+ "dependencies": {
11
+ "preact": "^10.24.3"
12
+ },
13
+ "devDependencies": {
14
+ "esbuild": "^0.24.0",
15
+ "typescript": "^5.6.3"
16
+ }
17
+ }
@@ -0,0 +1,11 @@
1
+ // Fails CI if the widget gets too big for customers' pages.
2
+ import { statSync } from 'node:fs';
3
+
4
+ const LIMIT = 25_000; // bytes, minified, uncompressed
5
+ const size = statSync('dist/widget.js').size;
6
+
7
+ console.log(`dist/widget.js: ${size} bytes (limit ${LIMIT})`);
8
+ if (size > LIMIT) {
9
+ console.error('widget.js is over its size budget');
10
+ process.exit(1);
11
+ }
@@ -0,0 +1,11 @@
1
+ const ENDPOINT = 'https://chat.parcelpost.example/v1/messages';
2
+
3
+ export async function sendMessage(text: string) {
4
+ const res = await fetch(ENDPOINT, {
5
+ method: 'POST',
6
+ headers: { 'content-type': 'application/json', 'x-widget-key': 'PLACEHOLDER_WIDGET_KEY' },
7
+ body: JSON.stringify({ text, page: location.href }),
8
+ });
9
+ if (!res.ok) throw new Error(`send failed: ${res.status}`);
10
+ return res.json();
11
+ }
@@ -0,0 +1,21 @@
1
+ import { h } from 'preact';
2
+ import { useRef, useState } from 'preact/hooks';
3
+ import { sendMessage } from './api';
4
+ import { SHORTCODES } from './emoji/data';
5
+
6
+ // PR #412: turn :smile: style shortcodes into emoji as the visitor types.
7
+ const SHORTCODE_RE = /:([a-z0-9_+-]+):/g;
8
+ const expandShortcodes = (text: string) =>
9
+ text.replace(SHORTCODE_RE, (m, code) => SHORTCODES[code] ?? m);
10
+
11
+ export function Composer({ openPicker }: { openPicker: (cb: (c: string) => void) => void }) {
12
+ const [text, setText] = useState('');
13
+ const input = useRef<HTMLTextAreaElement>(null);
14
+ return (
15
+ <form onSubmit={(e) => { e.preventDefault(); sendMessage(text); setText(''); }}>
16
+ <textarea ref={input} value={text} onInput={(e) => setText(expandShortcodes(e.currentTarget.value))} />
17
+ <button type="button" aria-label="Insert emoji" onClick={() => openPicker((c) => setText(text + c))}>☺</button>
18
+ <button type="submit">Send</button>
19
+ </form>
20
+ );
21
+ }
@@ -0,0 +1,8 @@
1
+ import EMOJI from './emoji-data.json';
2
+
3
+ export interface Emoji { char: string; shortcode: string; name: string; group: string; keywords: string[] }
4
+
5
+ export { EMOJI };
6
+ export const SHORTCODES: Record<string, string> = Object.fromEntries(
7
+ (EMOJI as Emoji[]).map((e) => [e.shortcode, e.char]),
8
+ );
@@ -0,0 +1,22 @@
1
+ import { h, render } from 'preact';
2
+ import { useState } from 'preact/hooks';
3
+ import { EMOJI, type Emoji } from './data';
4
+
5
+ function Picker({ onPick }: { onPick: (c: string) => void }) {
6
+ const [q, setQ] = useState('');
7
+ const shown = (EMOJI as Emoji[]).filter((e) => !q || e.keywords.some((k) => k.includes(q))).slice(0, 200);
8
+ return (
9
+ <div class="pp-picker">
10
+ <input placeholder="Search emoji" value={q} onInput={(e) => setQ(e.currentTarget.value)} />
11
+ <div class="pp-grid">
12
+ {shown.map((e) => <button key={e.shortcode} title={e.name} onClick={() => onPick(e.char)}>{e.char}</button>)}
13
+ </div>
14
+ </div>
15
+ );
16
+ }
17
+
18
+ export default function mountPicker(root: HTMLElement, onPick: (c: string) => void) {
19
+ const el = document.createElement('div');
20
+ root.appendChild(el);
21
+ render(<Picker onPick={onPick} />, el);
22
+ }