@optiaxiom/proteus 0.1.12 → 0.1.14

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.
@@ -1,5 +1,5 @@
1
- @layer optiaxiom._14psyja;
2
- @layer optiaxiom._14psyja {
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
3
  .ProteusChart__jmlqij1 {
4
4
  border-radius: 16px;
5
5
  }
@@ -1,5 +1,5 @@
1
- @layer optiaxiom._14psyja;
2
- @layer optiaxiom._14psyja {
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
3
  .ProteusChartTooltipContent__1gsvq810 {
4
4
  min-width: 128px;
5
5
  }
@@ -0,0 +1,36 @@
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
+ .ProteusQuestion__8f590p0 {
4
+ outline: none;
5
+ }
6
+ .ProteusQuestion__8f590p2 {
7
+ background-color: transparent;
8
+ border-color: var(--ax-colors-border-secondary);
9
+ border-inline-color: transparent;
10
+ border-inline-width: 6px;
11
+ cursor: pointer;
12
+ user-select: none;
13
+ }
14
+ .ProteusQuestion__8f590p2:focus-visible {
15
+ outline: 2px solid var(--ax-colors-border-focus);
16
+ outline-offset: 1px;
17
+ z-index: 1;
18
+ }
19
+ .ProteusQuestion__8f590p2[data-disabled] {
20
+ cursor: default;
21
+ opacity: 0.5;
22
+ pointer-events: none;
23
+ }
24
+ .ProteusQuestion__8f590p2:has(+ .ProteusQuestion__8f590p1:hover, + .ProteusQuestion__8f590p1[data-selected], + .ProteusQuestion__8f590p1 [contenteditable]:focus) {
25
+ border-bottom-color: transparent;
26
+ }
27
+ .ProteusQuestion__8f590p3 {
28
+ background-color: var(--ax-colors-bg-avatar-neutral);
29
+ }
30
+ @media (hover: hover) {
31
+ .ProteusQuestion__8f590p2:hover, .ProteusQuestion__8f590p2[data-selected], .ProteusQuestion__8f590p2:has([contenteditable]:focus) {
32
+ background-color: var(--ax-colors-bg-page);
33
+ border-color: var(--ax-colors-bg-page);
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { withIcon } from './withIcon.js';
4
+
5
+ const IconX = withIcon(
6
+ {
7
+ name: "IconX"
8
+ },
9
+ /* @__PURE__ */ jsx(
10
+ "path",
11
+ {
12
+ clipRule: "evenodd",
13
+ d: "M14.8669 3.77596C15.0444 3.59845 15.0444 3.31064 14.8669 3.13313C14.6894 2.95562 14.4016 2.95562 14.224 3.13313L10 7.35718L5.77596 3.13313C5.59845 2.95562 5.31064 2.95562 5.13313 3.13313C4.95562 3.31064 4.95562 3.59845 5.13313 3.77596L9.35718 8L5.13313 12.224C4.95562 12.4016 4.95562 12.6894 5.13313 12.8669C5.31064 13.0444 5.59845 13.0444 5.77596 12.8669L10 8.64282L14.224 12.8669C14.4016 13.0444 14.6894 13.0444 14.8669 12.8669C15.0444 12.6894 15.0444 12.4016 14.8669 12.224L10.6428 8L14.8669 3.77596Z",
14
+ fill: "currentColor",
15
+ fillRule: "evenodd"
16
+ }
17
+ )
18
+ );
19
+
20
+ export { IconX };
@@ -8,6 +8,7 @@ import { useResolveProteusValues } from '../proteus-document/useResolveProteusVa
8
8
  function ProteusAction({
9
9
  children,
10
10
  onClick,
11
+ type = "button",
11
12
  ...props
12
13
  }) {
13
14
  const { onEvent, valid } = useProteusDocumentContext(
@@ -20,7 +21,7 @@ function ProteusAction({
20
21
  return /* @__PURE__ */ jsx(
21
22
  Button,
22
23
  {
23
- disabled: props.appearance === "primary" ? !valid : void 0,
24
+ disabled: type === "submit" && !valid,
24
25
  justifyContent: "center",
25
26
  loading,
26
27
  onClick: async () => {
@@ -31,6 +32,7 @@ function ProteusAction({
31
32
  await onEvent(resolvedOnClick);
32
33
  setLoading(false);
33
34
  },
35
+ type,
34
36
  ...props,
35
37
  children
36
38
  }
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-D33V0Us-.css';
1
+ import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-DMK3zTMn.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
4
  var chart = recipe({base:[{border:'1',borderColor:'border.tertiary',fontSize:'sm',p:'16'},'ProteusChart__jmlqij1','ProteusChart__jmlqij0']});
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-61AtcZEK.css';
1
+ import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-BNYSJiKc.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
4
  var tooltip = recipe({base:[{bg:'bg.default',border:'1',borderColor:'border.secondary',display:'grid',fontSize:'sm',gap:'6',px:'8',py:'10',rounded:'lg',shadow:'lg'},'ProteusChartTooltipContent__1gsvq810']});
@@ -14,9 +14,9 @@ function ProteusDocumentShell({
14
14
  defaultOpen = true,
15
15
  element,
16
16
  onDataChange,
17
+ onInteraction,
17
18
  onMessage,
18
19
  onOpenChange,
19
- onToolCall,
20
20
  open: openProp,
21
21
  readOnly = false,
22
22
  strict
@@ -45,8 +45,8 @@ function ProteusDocumentShell({
45
45
  onDataChange?.(next);
46
46
  }),
47
47
  onEvent: useEffectEvent(async (event) => {
48
- if ("tool" in event) {
49
- await onToolCall?.(event.tool);
48
+ if ("interaction" in event) {
49
+ await onInteraction?.(event.interaction);
50
50
  } else if ("message" in event) {
51
51
  await onMessage?.(event.message);
52
52
  } else if (event.action === "download") {
@@ -0,0 +1,9 @@
1
+ import './../assets/src/proteus-question/ProteusQuestion.css.ts.vanilla-vsj__kKE.css';
2
+ import { recipe } from '@optiaxiom/react/css-runtime';
3
+
4
+ var addon = recipe({base:[{display:'grid',fontWeight:'500',placeItems:'center',rounded:'lg',size:'md',transition:'colors'},'ProteusQuestion__8f590p3'],variants:{cursor:{pointer:{cursor:'pointer'}}}});
5
+ var choice = recipe({base:[{color:'fg.default',flexDirection:'column',fontSize:'md',gap:'8',px:'10',py:'16',rounded:'lg',transition:'colors'},'ProteusQuestion__8f590p2','ProteusQuestion__8f590p1'],variants:{cursor:{pointer:{borderB:'1'},text:{cursor:'text'}}}});
6
+ var choiceGroup = recipe({base:{flexDirection:'column'}});
7
+ var question = recipe({base:[{flexDirection:'column',gap:'16',p:'4'},'ProteusQuestion__8f590p0']});
8
+
9
+ export { addon, choice, choiceGroup, question };
@@ -1,11 +1,16 @@
1
1
  "use client";
2
- import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { Group, Button, Text } from '@optiaxiom/react';
4
- import { useState, useRef, useEffect } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { IconNorth } from '@optiaxiom/icons';
4
+ import { Group, Text, Button, Box, Checkbox } from '@optiaxiom/react';
5
+ import { InlineInput } from '@optiaxiom/react/unstable';
6
+ import * as RovingFocus from '@radix-ui/react-roving-focus';
7
+ import { useState, useCallback, useRef, useEffect } from 'react';
5
8
  import { IconAngleLeft } from '../icons/IconAngleLeft.js';
6
9
  import { IconAngleRight } from '../icons/IconAngleRight.js';
10
+ import { IconPencil } from '../icons/IconPencil.js';
11
+ import { IconX } from '../icons/IconX.js';
7
12
  import { useProteusDocumentContext } from '../proteus-document/ProteusDocumentContext.js';
8
- import { ProteusQuestionItem } from './ProteusQuestionItem.js';
13
+ import { question, choiceGroup, choice, addon } from './ProteusQuestion-css.js';
9
14
 
10
15
  function ProteusQuestion({ questions }) {
11
16
  const { onEvent } = useProteusDocumentContext(
@@ -13,8 +18,13 @@ function ProteusQuestion({ questions }) {
13
18
  );
14
19
  const [answers, setAnswers] = useState([]);
15
20
  const [currentIndex, setCurrentIndex] = useState(0);
16
- const answer = answers[currentIndex];
17
- const valid = Array.isArray(answer) && answer.length > 0 && answer.every(Boolean);
21
+ const value = answers[currentIndex];
22
+ const valid = Array.isArray(value) && value.length > 0 && value.every(Boolean);
23
+ const onDismiss = useCallback(() => {
24
+ void onEvent({
25
+ message: "[User declined to answer the question]"
26
+ });
27
+ }, [onEvent]);
18
28
  const questionRef = useRef(null);
19
29
  const lastIndexRef = useRef(currentIndex);
20
30
  useEffect(() => {
@@ -35,102 +45,319 @@ function ProteusQuestion({ questions }) {
35
45
  }
36
46
  );
37
47
  lastIndexRef.current = currentIndex;
48
+ const item = questionRef.current?.querySelector("[data-selected]") ?? questionRef.current?.querySelector("[tabindex]");
49
+ item?.focus();
38
50
  }
39
51
  }, [currentIndex]);
52
+ const otherInputRef = useRef(null);
53
+ const otherItemRef = useRef(null);
40
54
  if (currentIndex >= questions.length) {
41
55
  return null;
42
56
  }
43
- const current = questions[currentIndex];
57
+ const { options, question: question$1, type } = questions[currentIndex];
44
58
  const isLast = currentIndex === questions.length - 1;
59
+ const otherValue = value?.find((v) => !options.includes(v));
60
+ const otherChecked = otherValue !== void 0;
45
61
  const onComplete = () => onEvent({
46
62
  message: answers.map(
47
- (value, index) => `Q: ${questions[index].question}
48
- A: ${value || "[Not specified]"}`
63
+ (value2, index) => `Q: ${questions[index].question}
64
+ A: ${value2 || "[Not specified]"}`
49
65
  ).join("\n\n")
50
66
  });
51
- return /* @__PURE__ */ jsxs(Group, { flexDirection: "column", gap: "16", children: [
52
- /* @__PURE__ */ jsx(
53
- ProteusQuestionItem,
54
- {
55
- addonAfter: questions.length > 1 && /* @__PURE__ */ jsxs(Group, { gap: "6", children: [
56
- /* @__PURE__ */ jsx(
57
- Button,
58
- {
59
- "aria-label": "Previous",
60
- disabled: currentIndex === 0,
61
- icon: /* @__PURE__ */ jsx(IconAngleLeft, {}),
62
- onClick: (event) => {
63
- event.preventDefault();
64
- setCurrentIndex((i) => i - 1);
65
- },
66
- size: "sm"
67
- }
68
- ),
69
- /* @__PURE__ */ jsxs(Text, { color: "fg.tertiary", fontSize: "sm", children: [
70
- currentIndex + 1,
71
- " of ",
72
- questions.length
67
+ const onValueChange = (value2) => {
68
+ answers[currentIndex] = value2;
69
+ setAnswers([...answers]);
70
+ };
71
+ const onSkip = () => {
72
+ answers[currentIndex] = null;
73
+ setAnswers([...answers]);
74
+ if (isLast) {
75
+ void onComplete();
76
+ } else {
77
+ setCurrentIndex((i) => i + 1);
78
+ }
79
+ };
80
+ const onSubmit = () => {
81
+ if (isLast) {
82
+ void onComplete();
83
+ } else {
84
+ setCurrentIndex((i) => i + 1);
85
+ }
86
+ };
87
+ return /* @__PURE__ */ jsxs(
88
+ Group,
89
+ {
90
+ onKeyDown: (event) => {
91
+ if (event.key === "Escape") {
92
+ event.preventDefault();
93
+ if (valid) {
94
+ onValueChange(null);
95
+ } else {
96
+ onSkip();
97
+ }
98
+ }
99
+ if ((event.key === "ArrowLeft" || event.key === "ArrowRight") && !(event.target instanceof HTMLInputElement) && !event.target.isContentEditable) {
100
+ event.preventDefault();
101
+ if (event.key === "ArrowLeft" && currentIndex > 0) {
102
+ setCurrentIndex((i) => i - 1);
103
+ } else if (event.key === "ArrowRight" && !isLast) {
104
+ setCurrentIndex((i) => i + 1);
105
+ }
106
+ }
107
+ },
108
+ tabIndex: -1,
109
+ ...question(),
110
+ children: [
111
+ /* @__PURE__ */ jsxs(Group, { flexDirection: "column", gap: "16", children: [
112
+ /* @__PURE__ */ jsxs(Group, { pl: "16", children: [
113
+ /* @__PURE__ */ jsx(Text, { flex: "1", fontWeight: "500", children: question$1 }),
114
+ /* @__PURE__ */ jsxs(Group, { gap: "6", children: [
115
+ questions.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
116
+ /* @__PURE__ */ jsx(
117
+ Button,
118
+ {
119
+ appearance: "subtle",
120
+ "aria-label": "Previous",
121
+ disabled: currentIndex === 0,
122
+ icon: /* @__PURE__ */ jsx(IconAngleLeft, {}),
123
+ onClick: (event) => {
124
+ event.preventDefault();
125
+ setCurrentIndex((i) => i - 1);
126
+ },
127
+ size: "sm"
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxs(Text, { color: "fg.tertiary", fontSize: "sm", children: [
131
+ currentIndex + 1,
132
+ " of ",
133
+ questions.length
134
+ ] }),
135
+ /* @__PURE__ */ jsx(
136
+ Button,
137
+ {
138
+ appearance: "subtle",
139
+ "aria-label": "Next",
140
+ disabled: isLast,
141
+ icon: /* @__PURE__ */ jsx(IconAngleRight, {}),
142
+ onClick: (event) => {
143
+ event.preventDefault();
144
+ setCurrentIndex((i) => i + 1);
145
+ },
146
+ size: "sm"
147
+ }
148
+ )
149
+ ] }),
150
+ /* @__PURE__ */ jsx(
151
+ Button,
152
+ {
153
+ appearance: "subtle",
154
+ "aria-label": "Dismiss",
155
+ icon: /* @__PURE__ */ jsx(IconX, {}),
156
+ onClick: (event) => {
157
+ event.preventDefault();
158
+ onDismiss();
159
+ },
160
+ size: "sm"
161
+ }
162
+ )
163
+ ] })
73
164
  ] }),
74
- /* @__PURE__ */ jsx(
75
- Button,
165
+ /* @__PURE__ */ jsx(RovingFocus.Root, { asChild: true, loop: true, orientation: "vertical", ref: questionRef, children: /* @__PURE__ */ jsxs(
166
+ Group,
76
167
  {
77
- "aria-label": "Next",
78
- disabled: isLast,
79
- icon: /* @__PURE__ */ jsx(IconAngleRight, {}),
80
- onClick: (event) => {
81
- event.preventDefault();
82
- setCurrentIndex((i) => i + 1);
83
- },
84
- size: "sm"
168
+ role: type === "single_select" ? "radiogroup" : "listbox",
169
+ ...choiceGroup(),
170
+ children: [
171
+ options.map((option, index) => {
172
+ const checked = type === "single_select" ? value?.[0] === option : value?.includes(option);
173
+ const disabled = type === "single_select" && otherChecked && !!otherValue;
174
+ return /* @__PURE__ */ jsx(RovingFocus.Item, { asChild: true, children: /* @__PURE__ */ jsx(
175
+ Box,
176
+ {
177
+ "aria-checked": type === "single_select" ? checked : void 0,
178
+ "data-disabled": disabled ? "" : void 0,
179
+ "data-selected": checked && type === "single_select" ? "" : void 0,
180
+ onClick: () => {
181
+ if (type === "single_select") {
182
+ onValueChange([option]);
183
+ onSubmit();
184
+ } else {
185
+ const current = value ?? [];
186
+ onValueChange(
187
+ checked ? current.filter((v) => v !== option) : [...current, option]
188
+ );
189
+ }
190
+ },
191
+ onFocus: () => {
192
+ if (type === "single_select") {
193
+ onValueChange([option]);
194
+ }
195
+ },
196
+ onKeyDown: (event) => {
197
+ if (type === "single_select" && event.key === "Enter") {
198
+ event.preventDefault();
199
+ onSubmit();
200
+ } else if (type === "multi_select" && (event.key === "Enter" || event.key === " ")) {
201
+ event.preventDefault();
202
+ const current = value ?? [];
203
+ onValueChange(
204
+ checked ? current.filter((v) => v !== option) : [...current, option]
205
+ );
206
+ }
207
+ },
208
+ role: type === "single_select" ? "radio" : "option",
209
+ tabIndex: 0,
210
+ ...choice({ cursor: "pointer" }),
211
+ children: /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
212
+ /* @__PURE__ */ jsx(Box, { ...addon(), children: type === "single_select" ? index + 1 : /* @__PURE__ */ jsx(
213
+ Checkbox,
214
+ {
215
+ checked,
216
+ hidden: true,
217
+ pointerEvents: "none",
218
+ tabIndex: -1
219
+ }
220
+ ) }),
221
+ /* @__PURE__ */ jsx(Group, { flex: "1", flexDirection: "column", gap: "2", children: /* @__PURE__ */ jsx(Text, { children: option }) }),
222
+ type === "single_select" && checked && /* @__PURE__ */ jsx(Box, { asChild: true, color: "fg.tertiary", ml: "auto", children: /* @__PURE__ */ jsx(IconNorth, {}) })
223
+ ] })
224
+ }
225
+ ) }, option);
226
+ }),
227
+ /* @__PURE__ */ jsx(
228
+ Box,
229
+ {
230
+ "aria-label": "Something else",
231
+ "data-selected": otherChecked ? "" : void 0,
232
+ role: "group",
233
+ ...choice({ cursor: "text" }),
234
+ children: /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
235
+ /* @__PURE__ */ jsx(Box, { ...addon({ cursor: "pointer" }), children: type === "single_select" ? /* @__PURE__ */ jsx(IconPencil, {}) : /* @__PURE__ */ jsx(
236
+ Checkbox,
237
+ {
238
+ checked: otherChecked,
239
+ hidden: true,
240
+ pointerEvents: "none",
241
+ tabIndex: -1
242
+ }
243
+ ) }),
244
+ /* @__PURE__ */ jsx(RovingFocus.Item, { asChild: true, children: /* @__PURE__ */ jsx(
245
+ Group,
246
+ {
247
+ flex: "1",
248
+ flexDirection: "column",
249
+ gap: "2",
250
+ onFocus: () => {
251
+ if (type === "single_select" && !otherValue) {
252
+ onValueChange([""]);
253
+ }
254
+ otherInputRef.current?.focus();
255
+ },
256
+ ref: otherItemRef,
257
+ children: /* @__PURE__ */ jsx(
258
+ InlineInput,
259
+ {
260
+ label: "Something else",
261
+ onKeyDown: (event) => {
262
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
263
+ event.preventDefault();
264
+ otherItemRef.current?.focus();
265
+ otherItemRef.current?.dispatchEvent(
266
+ new KeyboardEvent("keydown", {
267
+ bubbles: true,
268
+ key: event.key
269
+ })
270
+ );
271
+ } else if (event.key === "Enter") {
272
+ onSubmit();
273
+ }
274
+ },
275
+ onValueChange: (text) => {
276
+ if (type === "single_select") {
277
+ onValueChange([text]);
278
+ } else {
279
+ const current = value ?? [];
280
+ onValueChange([
281
+ ...current.filter((v) => options.includes(v)),
282
+ ...text ? [text] : []
283
+ ]);
284
+ }
285
+ },
286
+ ref: otherInputRef,
287
+ value: otherValue ?? ""
288
+ }
289
+ )
290
+ }
291
+ ) }),
292
+ type === "single_select" && /* @__PURE__ */ jsx(
293
+ Button,
294
+ {
295
+ appearance: otherValue ? "primary" : "default",
296
+ "aria-label": otherValue && (isLast ? "Submit" : "Next"),
297
+ icon: otherValue && /* @__PURE__ */ jsx(IconNorth, {}),
298
+ ml: "auto",
299
+ onClick: (event) => {
300
+ event.preventDefault();
301
+ if (otherValue) {
302
+ onSubmit();
303
+ } else {
304
+ onSkip();
305
+ }
306
+ },
307
+ children: !otherValue && "Skip"
308
+ }
309
+ )
310
+ ] })
311
+ },
312
+ "other"
313
+ )
314
+ ]
85
315
  }
86
- )
316
+ ) })
87
317
  ] }),
88
- choiceRef: questionRef,
89
- onValueChange: (value) => {
90
- answers[currentIndex] = value;
91
- setAnswers([...answers]);
92
- },
93
- options: current.options,
94
- question: current.question,
95
- type: current.type,
96
- value: answers[currentIndex] ?? null
97
- }
98
- ),
99
- /* @__PURE__ */ jsxs(Group, { gap: "8", justifyContent: "end", children: [
100
- /* @__PURE__ */ jsx(
101
- Button,
102
- {
103
- onClick: (event) => {
104
- event.preventDefault();
105
- answers[currentIndex] = null;
106
- setAnswers([...answers]);
107
- if (isLast) {
108
- void onComplete();
109
- } else {
110
- setCurrentIndex((i) => i + 1);
111
- }
112
- },
113
- children: "Skip"
114
- }
115
- ),
116
- /* @__PURE__ */ jsx(
117
- Button,
118
- {
119
- appearance: "primary",
120
- disabled: !valid,
121
- onClick: (event) => {
122
- event.preventDefault();
123
- if (isLast) {
124
- void onComplete();
125
- } else {
126
- setCurrentIndex((i) => i + 1);
127
- }
128
- },
129
- children: isLast ? "Submit" : "Next"
130
- }
131
- )
132
- ] })
133
- ] });
318
+ type === "multi_select" && /* @__PURE__ */ jsxs(
319
+ Group,
320
+ {
321
+ borderColor: "border.secondary",
322
+ borderT: "1",
323
+ gap: "8",
324
+ pt: "16",
325
+ px: "16",
326
+ children: [
327
+ /* @__PURE__ */ jsxs(Text, { children: [
328
+ value?.length || 0,
329
+ " selected"
330
+ ] }),
331
+ (value?.length || 0) > 0 ? /* @__PURE__ */ jsx(
332
+ Button,
333
+ {
334
+ appearance: "primary",
335
+ "aria-label": isLast ? "Submit" : "Next",
336
+ disabled: !valid,
337
+ icon: /* @__PURE__ */ jsx(IconNorth, {}),
338
+ ml: "auto",
339
+ onClick: (event) => {
340
+ event.preventDefault();
341
+ onSubmit();
342
+ }
343
+ }
344
+ ) : /* @__PURE__ */ jsx(
345
+ Button,
346
+ {
347
+ ml: "auto",
348
+ onClick: (event) => {
349
+ event.preventDefault();
350
+ onSkip();
351
+ },
352
+ children: "Skip"
353
+ }
354
+ )
355
+ ]
356
+ }
357
+ )
358
+ ]
359
+ }
360
+ );
134
361
  }
135
362
  ProteusQuestion.displayName = "@optiaxiom/proteus/ProteusQuestion";
136
363