@netlify/spark-ui 1.5.0-alpha.14 → 1.5.0-alpha.2

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 (48) hide show
  1. package/dist/assets/AskNetlify.css +1 -1
  2. package/dist/assets/SearchHeading.css +1 -1
  3. package/dist/assets/SearchLink.css +1 -1
  4. package/dist/assets/SearchModal.css +1 -1
  5. package/dist/components/preact/AskNetlify/AskNetlify.d.ts +1 -6
  6. package/dist/components/preact/AskNetlify/AskNetlify.js +2110 -2235
  7. package/dist/components/preact/Icon/index.d.ts +2 -2
  8. package/dist/components/preact/Icon/index.js +72 -108
  9. package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +50 -43
  10. package/dist/components/preact/SearchModal/components/SearchWindow.js +19 -21
  11. package/dist/components/preact/SearchModal/hooks/useSearchToggles.d.ts +1 -3
  12. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +10 -13
  13. package/package.json +2 -3
  14. package/packages/components/preact/AskNetlify/AskNetlify.css +109 -191
  15. package/packages/components/preact/AskNetlify/AskNetlify.tsx +29 -45
  16. package/packages/components/preact/Icon/index.tsx +53 -72
  17. package/packages/components/preact/SearchModal/components/SearchHeading/SearchHeading.css +3 -4
  18. package/packages/components/preact/SearchModal/components/SearchLink/SearchLink.css +9 -14
  19. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.css +29 -11
  20. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +12 -4
  21. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +5 -8
  22. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +3 -11
  23. package/packages/styles/tokens.css +4 -12
  24. package/packages/utils/theme.ts +0 -4
  25. package/dist/components/preact/SpriteIcon/index.d.ts +0 -7
  26. package/dist/components/preact/SpriteIcon/index.js +0 -7
  27. package/dist/components/preact/icons/AskNetlify.d.ts +0 -5
  28. package/dist/components/preact/icons/AskNetlify.js +0 -69
  29. package/dist/components/preact/icons/Book.d.ts +0 -5
  30. package/dist/components/preact/icons/Book.js +0 -23
  31. package/dist/components/preact/icons/CircleQuestion.d.ts +0 -5
  32. package/dist/components/preact/icons/CircleQuestion.js +0 -25
  33. package/dist/components/preact/icons/Comment.d.ts +0 -5
  34. package/dist/components/preact/icons/Comment.js +0 -23
  35. package/dist/components/preact/icons/Lightbulb.d.ts +0 -5
  36. package/dist/components/preact/icons/Lightbulb.js +0 -34
  37. package/dist/components/preact/icons/MagnifyingGlass.d.ts +0 -5
  38. package/dist/components/preact/icons/MagnifyingGlass.js +0 -23
  39. package/packages/assets/icons/AskNetlify.svg +0 -23
  40. package/packages/assets/icons/icons.svg +0 -10
  41. package/packages/assets/icons/lightbulb.svg +0 -4
  42. package/packages/components/preact/SpriteIcon/index.tsx +0 -13
  43. package/packages/components/preact/icons/AskNetlify.tsx +0 -46
  44. package/packages/components/preact/icons/Book.tsx +0 -20
  45. package/packages/components/preact/icons/CircleQuestion.tsx +0 -22
  46. package/packages/components/preact/icons/Comment.tsx +0 -20
  47. package/packages/components/preact/icons/Lightbulb.tsx +0 -26
  48. package/packages/components/preact/icons/MagnifyingGlass.tsx +0 -20
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/spark-ui",
3
3
  "description": "Assets, design tokens, components, and utilities",
4
- "version": "1.5.0-alpha.14",
4
+ "version": "1.5.0-alpha.2",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -25,7 +25,6 @@
25
25
  "exports": {
26
26
  "./preact": "./dist/components/preact/index.js",
27
27
  "./assets/*.css": "./dist/assets/*.css",
28
- "./assets/icons/*.svg": "./packages/assets/icons/*.svg",
29
28
  "./logos/*.svg": "./packages/assets/logos/*.svg",
30
29
  "./styles/*.css": "./packages/styles/*.css",
31
30
  "./components": "./packages/components/index.ts",
@@ -34,7 +33,7 @@
34
33
  "./analytics": "./packages/analytics/index.ts"
35
34
  },
36
35
  "dependencies": {
37
- "@kapaai/react-sdk": "^0.1.3",
36
+ "@kapaai/react-sdk": "^0.1.2",
38
37
  "@preact/preset-vite": "^2.10.1",
39
38
  "@shikijs/transformers": "^3.3.0",
40
39
  "glob": "^11.0.1",
@@ -1,78 +1,38 @@
1
- /* ----------------------------- */
2
- /* Layout Containers */
3
- /* ----------------------------- */
4
1
  .chat-container {
5
2
  opacity: 0;
6
3
  animation: appearContainer 120ms ease-in-out forwards;
7
4
  animation-delay: 140ms;
8
5
  }
9
6
 
7
+ @keyframes appearContainer {
8
+ 0% {
9
+ opacity: 0;
10
+ }
11
+ 100% {
12
+ opacity: 1;
13
+ }
14
+ }
15
+
10
16
  .conversation {
11
- display: grid;
12
- align-items: flex-end;
13
17
  height: calc(var(--n-search-container-height) + var(--n-search-footer-height));
14
18
  padding: var(--n-search-padding-inline);
15
- overflow-y: auto;
16
19
  scroll-behavior: smooth;
20
+ overflow-y: auto;
21
+ display: grid;
22
+ align-items: flex-end;
17
23
  gap: var(--space-s);
18
24
  }
19
25
 
20
- .form-wrapper {
21
- display: flex;
22
- align-items: center;
23
- position: relative;
24
- height: var(--n-search-input-height);
25
- background-color: var(--search-input-bg-color);
26
- }
27
-
28
- .form-wrapper::after {
29
- content: '';
30
- display: block;
31
- position: absolute;
32
- top: 0;
33
- left: 0;
34
- width: 100%;
35
- height: 1px;
36
- background: var(--color-bg-3);
37
- }
38
-
39
- /* ----------------------------- */
40
- /* Conversation and Message UI */
41
- /* ----------------------------- */
42
26
  .question {
43
27
  border: none;
44
- border-radius: var(--radius-pill);
45
- transform: translateX(-0.75rem);
46
28
  color: var(--color-text-1);
47
- font-size: var(--step--1);
48
29
  font-weight: var(--font-weight-regular);
49
- text-align: left;
50
- text-wrap: pretty;
51
- display: flex;
52
- align-items: center;
53
- gap: var(--space-2xs);
54
- transition: background-color 200ms ease-out;
55
- }
56
-
57
- .question [data-icon='caretRight'] {
58
- opacity: 0;
59
- color: var(--color-text-weaker);
60
- transform: translateX(-3px);
61
- transition: transform 200ms ease-out;
62
- }
63
-
64
- .question:hover {
65
- background-color: var(--search-back-btn-bg-color);
66
-
67
- [data-icon='caretRight'] {
68
- opacity: 1;
69
- transform: translateX(0px);
70
- }
30
+ font-size: var(--step--1);
31
+ border-radius: var(--radius-pill);
32
+ transform: translateX(-0.75rem);
71
33
  }
72
34
 
73
35
  .question-list li {
74
- position: relative;
75
- width: fit-content;
76
36
  opacity: 0;
77
37
  transform: translateY(10px);
78
38
  animation: fadeInUp 0.5s forwards;
@@ -81,37 +41,99 @@
81
41
  .question-list li:nth-child(1) {
82
42
  animation-delay: 0.5s;
83
43
  }
44
+
84
45
  .question-list li:nth-child(2) {
85
46
  animation-delay: 0.3s;
86
47
  }
48
+
87
49
  .question-list li:nth-child(3) {
88
50
  animation-delay: 0.1s;
89
51
  }
90
52
 
53
+ @keyframes fadeInUp {
54
+ to {
55
+ opacity: 1;
56
+ transform: translateY(0);
57
+ }
58
+ }
59
+
91
60
  .message {
92
61
  display: grid;
93
- grid-template-columns: minmax(0, 1fr);
94
62
  gap: var(--space-s);
95
- margin-block-start: var(--space-2xl);
96
63
  }
97
64
 
98
65
  .message-bubble {
99
- width: fit-content;
100
- max-width: 85%;
101
66
  padding: var(--space-xs) var(--space-s);
102
67
  background: var(--search-answer-message-color);
103
68
  border-radius: var(--radius-l);
69
+ width: fit-content;
70
+ max-width: 85%;
104
71
  font-size: var(--step-0);
105
72
  }
106
73
 
74
+ .prose {
75
+ font-size: var(--step--0);
76
+
77
+ > * + * {
78
+ margin-top: var(--flow-space, var(--space-xs));
79
+ }
80
+
81
+ h1,
82
+ h2,
83
+ h3 {
84
+ font-size: var(--step-1);
85
+ }
86
+
87
+ h4,
88
+ h5,
89
+ h6 {
90
+ font-size: var(--step-0);
91
+ }
92
+
93
+ pre {
94
+ background: var(--neutral-light-700);
95
+ border-radius: var(--radius-m);
96
+ max-width: 100%;
97
+ box-sizing: border-box;
98
+ padding: var(--space-s);
99
+ color: var(--color-text-inverse);
100
+ overflow-x: auto;
101
+ }
102
+
103
+ code {
104
+ overflow-x: auto;
105
+ }
106
+
107
+ code[class*='language-'],
108
+ pre[class*='language-'] {
109
+ display: block;
110
+ font-size: var(--font-size-text-xs);
111
+ }
112
+ }
113
+
107
114
  .user-message {
108
115
  margin-inline-start: auto;
109
116
  background: var(--search-user-message-color);
110
117
  }
111
118
 
112
- /* ----------------------------- */
113
- /* Form UI */
114
- /* ----------------------------- */
119
+ .form-wrapper {
120
+ display: flex;
121
+ align-items: center;
122
+ position: relative;
123
+ height: var(--n-search-input-height);
124
+ }
125
+
126
+ .form-wrapper::after {
127
+ content: '';
128
+ display: block;
129
+ width: 100%;
130
+ height: 1px;
131
+ background: var(--color-bg-3);
132
+ position: absolute;
133
+ top: 0;
134
+ left: 0;
135
+ }
136
+
115
137
  .form-input {
116
138
  border: none;
117
139
  outline: none;
@@ -119,11 +141,12 @@
119
141
  }
120
142
 
121
143
  .form-submit {
144
+ border: none;
122
145
  padding: 0.7rem;
146
+ border-radius: var(--radius-pill);
123
147
  margin-inline-end: var(--space-2xs);
124
148
  border: none;
125
- border-radius: var(--radius-pill);
126
- background: var(--search-submit-btn-bg-color);
149
+ background: var(--color-link);
127
150
  color: var(--neutral-000);
128
151
  transition: background-color 150ms ease-out;
129
152
  }
@@ -138,150 +161,45 @@
138
161
 
139
162
  .form-submit[disabled] {
140
163
  background: var(--neutral-500);
141
- }
142
164
 
143
- .form-submit[disabled] > * {
144
- opacity: 0.5;
165
+ & > * {
166
+ opacity: 0.5;
167
+ }
145
168
  }
146
169
 
147
- /* ----------------------------- */
148
- /* Buttons */
149
- /* ----------------------------- */
150
170
  .button-back {
171
+ width: fit-content;
172
+ background: transparent;
151
173
  position: absolute;
152
174
  z-index: 1;
153
- width: fit-content;
154
- margin: 1rem;
155
175
  padding: 0.5rem 1rem;
156
176
  background: var(--search-back-btn-bg-color);
157
- border: 0.5px solid var(--color-bg-3);
177
+ margin: 1rem;
178
+ border: 2px solid var(--color-bg-3);
158
179
  border-radius: var(--radius-pill);
159
180
  font-size: var(--step--1);
160
- color: var(--color-text-1);
181
+ border: 1px solid;
161
182
  cursor: pointer;
183
+ border-color: var(--color-bg-3);
184
+ color: var(--color-text-1);
185
+ border-width: 0.5px;
162
186
  }
163
187
 
164
- /* ----------------------------- */
165
- /* Typography & Prose */
166
- /* ----------------------------- */
167
- .prose {
168
- font-size: var(--step--0);
169
- }
170
-
171
- .prose > * + * {
172
- margin-top: var(--flow-space, var(--space-s));
173
- }
174
-
175
- .prose :where(ol, ul, li) > * + * {
176
- margin-top: var(--flow-space, var(--space-3xs));
177
- }
178
-
179
- .prose h1,
180
- .prose h2,
181
- .prose h3 {
182
- font-size: var(--step-1);
183
- font-weight: var(--font-weight-bold);
184
- }
185
-
186
- .prose h4,
187
- .prose h5,
188
- .prose h6 {
189
- font-size: var(--step-0);
190
- font-weight: var(--font-weight-bold);
191
- }
192
-
193
- .prose pre {
194
- max-width: 100%;
195
- padding: var(--space-s);
196
- background: var(--neutral-light-700);
197
- border-radius: var(--radius-m);
198
- box-sizing: border-box;
199
- color: var(--neutral-100);
200
- overflow-x: auto;
201
- }
202
-
203
- .prose code {
204
- overflow-x: auto;
205
- }
206
-
207
- .prose code[class*='language-'],
208
- .prose pre[class*='language-'] {
209
- display: block;
210
- font-size: var(--font-size-text-xs);
211
- }
212
-
213
- /* ----------------------------- */
214
- /* Loaders and SVG Effects */
215
- /* ----------------------------- */
216
- .message-loader {
217
- width: 2.5rem;
218
- height: auto;
188
+ .pulsing {
219
189
  display: block;
220
- color: var(--search-loading-color);
221
- }
222
-
223
- .message-loader :where(#ask-netlify-tick-1, #ask-netlify-tick-2, #ask-netlify-tick-3) {
224
- opacity: 0;
225
- animation: fadeIn 800ms ease-in-out infinite;
226
- }
227
-
228
- .message-loader #ask-netlify-tick-1 {
229
- animation-delay: 100ms;
230
- }
231
-
232
- .message-loader #ask-netlify-tick-2 {
233
- animation-delay: 200ms;
234
- }
235
-
236
- .message-loader #ask-netlify-tick-3 {
237
- animation-delay: 300ms;
238
- }
239
-
240
- .message-loader :where(#ask-netlify-eyelid-left, #ask-netlify-eyelid-right) {
241
- transform-origin: top center;
242
- transform: scaleY(1.035);
243
- animation: askEyeBlink 4s infinite;
244
- animation-delay: 1s;
245
- }
246
-
247
- /* ----------------------------- */
248
- /* Animations and Keyframes */
249
- /* ----------------------------- */
250
- @keyframes fadeIn {
251
- 0% {
252
- opacity: 0;
253
- }
254
- 100% {
255
- opacity: 1;
256
- }
257
- }
258
-
259
- @keyframes fadeInUp {
260
- to {
261
- opacity: 1;
262
- transform: translateY(0);
263
- }
190
+ width: 1rem;
191
+ height: 1rem;
192
+ clip-path: circle(40%);
193
+ background: var(--search-loading-color);
194
+ animation: pulsing 2.5s infinite ease-in-out;
264
195
  }
265
196
 
266
- @keyframes askEyeBlink {
197
+ @keyframes pulsing {
267
198
  0%,
268
- 5% {
269
- transform: scaleY(1.035);
270
- }
271
- 6%,
272
- 10% {
273
- transform: scaleY(1.3);
274
- }
275
- 11%,
276
- 15% {
277
- transform: scaleY(1.035);
278
- }
279
- 16%,
280
- 20% {
281
- transform: scaleY(1.3);
282
- }
283
- 21%,
284
199
  100% {
285
- transform: scaleY(1.035);
200
+ transform: scale(0.9);
201
+ }
202
+ 50% {
203
+ transform: scale(1.05);
286
204
  }
287
205
  }
@@ -1,15 +1,10 @@
1
1
  import { useState, useRef, useEffect } from 'preact/hooks';
2
2
  import { marked } from 'marked';
3
3
  import { KapaProvider, useChat } from '@kapaai/react-sdk';
4
- import Icon from '../Icon';
5
4
 
6
5
  import './AskNetlify.css';
7
6
 
8
- interface AskNetlifyProps {
9
- setModalState?: (state?: { current: string }) => void;
10
- }
11
-
12
- function AskNetlify(props: AskNetlifyProps) {
7
+ function AskNetlify(props) {
13
8
  return (
14
9
  <KapaProvider
15
10
  integrationId="3c0789a6-c6a8-4fbc-b0cc-235f9e4a6351"
@@ -24,48 +19,49 @@ function AskNetlify(props: AskNetlifyProps) {
24
19
  );
25
20
  }
26
21
 
27
- function ChatInterface({ setModalState }: AskNetlifyProps) {
22
+ function ChatInterface({ setModalState }) {
28
23
  const [message, setMessage] = useState('');
29
24
  const { conversation, submitQuery, isGeneratingAnswer, isPreparingAnswer, stopGeneration } =
30
25
  useChat();
31
26
 
32
- const chatRef = useRef<HTMLDivElement>(null);
33
- const indicatorRef = useRef<HTMLSpanElement>(null);
34
- const menuRef = useRef<HTMLUListElement>(null);
35
-
36
- const questions: Record<string, string> = {
37
- 'a69747ed-505b-4e98-8535-fd357c327516': 'How do I deploy an app to Netlify using AI?',
38
- 'e477f4dd-7159-4c31-8a18-4dfc7e84d927': 'How does Netlify simplify my CI/CD pipeline?',
39
- '39229ddb-9000-4e83-bbf8-54f1c1e55ed4':
40
- 'How does Netlify help improve my site’s Core Web Vitals?',
41
- '5c41b3c2-4c90-4cf7-9f24-f0d0b8f0f759':
42
- 'What are the benefits of using Netlify with a headless CMS?',
43
- '0270de6c-387e-401f-b24c-6ae1166db477':
44
- 'What are the benefits of hosting on Netlify’s edge network?',
27
+ const chatRef = useRef(null);
28
+
29
+ const QandAMap = {
30
+ '487cda5f-e852-4223-85dd-d18ab58dc7a9': "Why can't I add my domain to Netlify DNS?",
31
+ 'd133a822-dbe1-4870-8a94-212c5841f194': 'Why is my website not loading?',
32
+ 'e477f4dd-7159-4c31-8a18-4dfc7e84d927': 'How do I set up environment variables?',
45
33
  };
46
34
 
47
- const handleSubmit = (e: Event) => {
35
+ const handleSubmit = e => {
48
36
  e.preventDefault();
49
37
 
50
- const input = e.target as HTMLInputElement;
51
- const questionId = input?.getAttribute('data-id');
52
- const question = questionId ? questions[questionId] : message;
38
+ const id = e.target.getAttribute('data-id');
53
39
 
54
- if (question.trim()) {
55
- submitQuery(question.trim());
40
+ if (id) {
41
+ const question = QandAMap[id];
42
+ if (question.trim()) {
43
+ submitQuery(question.trim());
44
+ }
45
+ setMessage('');
56
46
  }
57
47
 
58
48
  if (message.trim()) {
49
+ submitQuery(message.trim());
59
50
  setMessage('');
60
51
  }
61
52
  };
62
53
 
54
+ useEffect(() => {
55
+ const el = chatRef.current as HTMLElement | null;
56
+ if (el) el.scrollTop = el.scrollHeight;
57
+ }, [conversation]);
58
+
63
59
  return (
64
60
  <div className="chat-container">
65
61
  <button
66
62
  class="button button-back"
67
63
  data-button-variant="outline"
68
- onClick={() => setModalState && setModalState({ current: 'search' })}
64
+ onClick={() => setModalState({ current: 'search' })}
69
65
  aria-label={'Return to search'}
70
66
  >
71
67
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
@@ -78,27 +74,14 @@ function ChatInterface({ setModalState }: AskNetlifyProps) {
78
74
  </button>
79
75
  <div ref={chatRef} className="conversation n-search-window">
80
76
  {!conversation.length && (
81
- <ul ref={menuRef} class="question-list" role="list">
82
- {Object.entries(questions).map(([id, question]) => (
77
+ <ul class="question-list" role="list">
78
+ {Object.entries(QandAMap).map(([id, question]) => (
83
79
  <li>
84
80
  <button class="question" onClick={handleSubmit} data-id={id}>
85
81
  {question}
86
- <Icon name="caretRight" />
87
82
  </button>
88
83
  </li>
89
84
  ))}
90
-
91
- <span
92
- ref={indicatorRef}
93
- style={{
94
- position: 'absolute',
95
- background: 'rgba(0, 0, 0, 0.1)',
96
- borderRadius: '4px',
97
- transition: 'all 200ms ease',
98
- pointerEvents: 'none',
99
- zIndex: -1,
100
- }}
101
- />
102
85
  </ul>
103
86
  )}
104
87
 
@@ -121,9 +104,7 @@ function ChatInterface({ setModalState }: AskNetlifyProps) {
121
104
  />
122
105
  </div>
123
106
  ) : (
124
- <div class="message-loader">
125
- <Icon name="askNetlify" />
126
- </div>
107
+ <span class="pulsing"></span>
127
108
  )}
128
109
  </div>
129
110
  </div>
@@ -139,6 +120,9 @@ function ChatInterface({ setModalState }: AskNetlifyProps) {
139
120
  placeholder="Ask a question..."
140
121
  disabled={isGeneratingAnswer}
141
122
  />
123
+ {/* <button type="submit" className="form-submit" disabled={isGeneratingAnswer}>
124
+ Send
125
+ </button> */}
142
126
 
143
127
  {isGeneratingAnswer ? (
144
128
  <button