@assistant-ui/react-native 0.1.34 → 0.1.36
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/dist/primitives/composer/ComposerInput.js +1 -0
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/package.json +10 -10
- package/src/primitives/composer/ComposerInput.test.tsx +12 -0
- package/src/primitives/composer/ComposerInput.tsx +1 -0
- package/src/primitives/message/MessageContent.test.tsx +25 -31
|
@@ -86,6 +86,7 @@ const ComposerInput = (t0) => {
|
|
|
86
86
|
if ($[11] !== aui || $[12] !== onKeyPressProp || $[13] !== submitMode) {
|
|
87
87
|
t5 = (e) => {
|
|
88
88
|
onKeyPressProp?.(e);
|
|
89
|
+
if (e.isDefaultPrevented()) return;
|
|
89
90
|
if (Platform.OS !== "web") return;
|
|
90
91
|
if (submitMode !== "enter") return;
|
|
91
92
|
const nativeEvent = e.nativeEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComposerInput.js","names":["useCallback","useEffect","useRef","Platform","TextInput","NativeSyntheticEvent","TextInputKeyPressEventData","TextInputProps","useAui","useAuiState","flushTapSync","ComposerInputProps","Omit","submitMode","getWebTextareaMaxHeight","el","HTMLTextAreaElement","computedMaxHeight","globalThis","getComputedStyle","maxHeight","parsedComputedMaxHeight","Number","parseFloat","isFinite","inlineMaxHeight","style","adjustWebTextareaHeight","ref","React","RefObject","OS","current","rows","overflowX","overflowY","height","scrollHeight","needsScrollbar","nextHeight","Math","min","ComposerInput","t0","$","_c","numberOfLines","onKeyPressProp","props","t1","onKeyPress","undefined","aui","text","_temp","inputRef","t2","value","composer","setText","onChangeText","t3","Symbol","for","t4","t5","e","nativeEvent","shiftKey","isComposing","keyCode","key","Event","preventDefault","send","t6","t7","s"],"sources":["../../../src/primitives/composer/ComposerInput.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport {\n Platform,\n TextInput,\n type NativeSyntheticEvent,\n type TextInputKeyPressEventData,\n type TextInputProps,\n} from \"react-native\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\n\nexport type ComposerInputProps = Omit<\n TextInputProps,\n \"value\" | \"onChangeText\"\n> & {\n /**\n * Controls how the Enter key submits messages (web only).\n * - \"enter\": Plain Enter submits (Shift+Enter for newline)\n * - \"none\": Enter inserts newline (no keyboard submission)\n * @default \"enter\"\n */\n submitMode?: \"enter\" | \"none\";\n};\n\nconst getWebTextareaMaxHeight = (el: HTMLTextAreaElement) => {\n const computedMaxHeight = globalThis.getComputedStyle?.(el).maxHeight;\n const parsedComputedMaxHeight = Number.parseFloat(computedMaxHeight ?? \"\");\n if (Number.isFinite(parsedComputedMaxHeight)) return parsedComputedMaxHeight;\n\n const inlineMaxHeight = Number.parseFloat(el.style.maxHeight);\n return Number.isFinite(inlineMaxHeight) ? inlineMaxHeight : null;\n};\n\nconst adjustWebTextareaHeight = (ref: React.RefObject<TextInput | null>) => {\n if (Platform.OS !== \"web\") return;\n // On web, the TextInput ref IS the DOM element\n const el = ref.current as unknown as HTMLTextAreaElement | null;\n if (!el) return;\n // Ensure rows=1 so scrollHeight reflects actual content\n if (el.rows !== 1) el.rows = 1;\n\n el.style.overflowX = \"hidden\";\n el.style.overflowY = \"hidden\";\n el.style.height = \"auto\";\n\n const scrollHeight = el.scrollHeight;\n const maxHeight = getWebTextareaMaxHeight(el);\n const needsScrollbar = maxHeight !== null && scrollHeight > maxHeight;\n const nextHeight =\n maxHeight === null ? scrollHeight : Math.min(scrollHeight, maxHeight);\n\n el.style.overflowY = needsScrollbar ? \"auto\" : \"hidden\";\n el.style.height = `${nextHeight}px`;\n};\n\nexport const ComposerInput = ({\n submitMode = \"enter\",\n onKeyPress: onKeyPressProp,\n numberOfLines,\n style,\n ...props\n}: ComposerInputProps) => {\n const aui = useAui();\n const text = useAuiState((s) => s.composer.text);\n const inputRef = useRef<TextInput>(null);\n\n const onChangeText = useCallback(\n (value: string) => {\n if (Platform.OS === \"web\") {\n // Keep the controlled value in sync mid-IME so react-dom does not reset the textarea to a stale value\n flushTapSync(() => {\n aui.composer.setText(value);\n });\n return;\n }\n aui.composer.setText(value);\n },\n [aui],\n );\n\n // Auto-resize textarea on web when text changes\n useEffect(() => {\n void text; // trigger re-run when text changes\n adjustWebTextareaHeight(inputRef);\n }, [text]);\n\n const onKeyPress = useCallback(\n (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {\n onKeyPressProp?.(e);\n\n if (Platform.OS !== \"web\") return;\n if (submitMode !== \"enter\") return;\n\n const nativeEvent = e.nativeEvent as TextInputKeyPressEventData & {\n shiftKey?: boolean;\n isComposing?: boolean;\n keyCode?: number;\n };\n // react-native-web forwards the raw DOM keydown before its own composition guard, so re-check it here (mirrors isEventComposing)\n if (nativeEvent.isComposing || nativeEvent.keyCode === 229) return;\n if (nativeEvent.key === \"Enter\" && !nativeEvent.shiftKey) {\n (e as unknown as Event).preventDefault?.();\n aui.composer.send();\n }\n },\n [aui, submitMode, onKeyPressProp],\n );\n\n return (\n <TextInput\n ref={inputRef}\n value={text}\n onChangeText={onChangeText}\n onKeyPress={onKeyPress}\n numberOfLines={numberOfLines ?? (Platform.OS === \"web\" ? 1 : undefined)}\n style={style}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;AAwBA,MAAMc,2BAA2BC,OAA4B;CAC3D,MAAME,oBAAoBC,WAAWC,mBAAmBJ,EAAE,CAAC,CAACK;CAC5D,MAAMC,0BAA0BC,OAAOC,WAAWN,qBAAqB,EAAE;CACzE,IAAIK,OAAOE,SAASH,uBAAuB,GAAG,OAAOA;CAErD,MAAMI,kBAAkBH,OAAOC,WAAWR,GAAGW,MAAMN,SAAS;CAC5D,OAAOE,OAAOE,SAASC,eAAe,IAAIA,kBAAkB;AAC9D;AAEA,MAAME,2BAA2BC,QAA2C;CAC1E,IAAIzB,SAAS4B,OAAO,OAAO;CAE3B,MAAMhB,KAAKa,IAAII;CACf,IAAI,CAACjB,IAAI;CAET,IAAIA,GAAGkB,SAAS,GAAGlB,GAAGkB,OAAO;CAE7BlB,GAAGW,MAAMQ,YAAY;CACrBnB,GAAGW,MAAMS,YAAY;CACrBpB,GAAGW,MAAMU,SAAS;CAElB,MAAMC,eAAetB,GAAGsB;CACxB,MAAMjB,YAAYN,wBAAwBC,EAAE;CAC5C,MAAMuB,iBAAiBlB,cAAc,QAAQiB,eAAejB;CAC5D,MAAMmB,aACJnB,cAAc,OAAOiB,eAAeG,KAAKC,IAAIJ,cAAcjB,SAAS;CAEtEL,GAAGW,MAAMS,YAAYG,iBAAiB,SAAS;CAC/CvB,GAAGW,MAAMU,SAAS,GAAGG,WAAU;AACjC;AAEA,MAAaG,iBAAgBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAtB;CAAA,IAAAuB;CAAA,IAAAL,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAA9B,YAAAoC,IAAAC,YAAAH,gBAAAD,eAAApB,UAAAsB,SAAAL;EAMTC,EAAA,KAAAD;EAAAC,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAlB;EAAAkB,EAAA,KAAAK;CAAA,OAAA;EAAAH,gBAAAF,EAAA;EAAAG,iBAAAH,EAAA;EAAAI,QAAAJ,EAAA;EAAAlB,QAAAkB,EAAA;EAAAK,KAAAL,EAAA;CAAA;CALnB,MAAA/B,aAAAoC,OAAAE,KAAAA,IAAA,UAAAF;CAMA,MAAAG,MAAY5C,OAAO;CACnB,MAAA6C,OAAa5C,YAAY6C,KAAsB;CAC/C,MAAAC,WAAiBrD,OAAkB,IAAI;CAAE,IAAAsD;CAAA,IAAAZ,EAAA,OAAAQ,KAAA;EAGvCI,MAAAC,UAAA;GACE,IAAItD,SAAQ4B,OAAQ,OAAK;IAEvBrB,mBAAa;KACX0C,IAAGM,SAASC,QAASF,KAAK;IAAC,CAC5B;IAAC;GAAA;GAGJL,IAAGM,SAASC,QAASF,KAAK;EAAC;EAC5Bb,EAAA,KAAAQ;EAAAR,EAAA,KAAAY;CAAA,OAAAA,KAAAZ,EAAA;CAVH,MAAAgB,eAAqBJ;CAYnB,IAAAK;CAAA,IAAAjB,EAAA,OAAAkB,OAAAC,IAAA,2BAAA,GAAA;EAGQF,WAAA;GAERlC,wBAAwB4B,QAAQ;EAAC;EAClCX,EAAA,KAAAiB;CAAA,OAAAA,KAAAjB,EAAA;CAAA,IAAAoB;CAAA,IAAApB,EAAA,OAAAS,MAAA;EAAEW,KAAA,CAACX,IAAI;EAACT,EAAA,KAAAS;EAAAT,EAAA,MAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAHT3C,UAAU4D,IAGPG,EAAM;CAAC,IAAAC;CAAA,IAAArB,EAAA,QAAAQ,OAAAR,EAAA,QAAAG,kBAAAH,EAAA,QAAA/B,YAAA;EAGRoD,MAAAC,MAAA;GACEnB,iBAAiBmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ComposerInput.js","names":["useCallback","useEffect","useRef","Platform","TextInput","NativeSyntheticEvent","TextInputKeyPressEventData","TextInputProps","useAui","useAuiState","flushTapSync","ComposerInputProps","Omit","submitMode","getWebTextareaMaxHeight","el","HTMLTextAreaElement","computedMaxHeight","globalThis","getComputedStyle","maxHeight","parsedComputedMaxHeight","Number","parseFloat","isFinite","inlineMaxHeight","style","adjustWebTextareaHeight","ref","React","RefObject","OS","current","rows","overflowX","overflowY","height","scrollHeight","needsScrollbar","nextHeight","Math","min","ComposerInput","t0","$","_c","numberOfLines","onKeyPressProp","props","t1","onKeyPress","undefined","aui","text","_temp","inputRef","t2","value","composer","setText","onChangeText","t3","Symbol","for","t4","t5","e","isDefaultPrevented","nativeEvent","shiftKey","isComposing","keyCode","key","Event","preventDefault","send","t6","t7","s"],"sources":["../../../src/primitives/composer/ComposerInput.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\nimport {\n Platform,\n TextInput,\n type NativeSyntheticEvent,\n type TextInputKeyPressEventData,\n type TextInputProps,\n} from \"react-native\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\n\nexport type ComposerInputProps = Omit<\n TextInputProps,\n \"value\" | \"onChangeText\"\n> & {\n /**\n * Controls how the Enter key submits messages (web only).\n * - \"enter\": Plain Enter submits (Shift+Enter for newline)\n * - \"none\": Enter inserts newline (no keyboard submission)\n * @default \"enter\"\n */\n submitMode?: \"enter\" | \"none\";\n};\n\nconst getWebTextareaMaxHeight = (el: HTMLTextAreaElement) => {\n const computedMaxHeight = globalThis.getComputedStyle?.(el).maxHeight;\n const parsedComputedMaxHeight = Number.parseFloat(computedMaxHeight ?? \"\");\n if (Number.isFinite(parsedComputedMaxHeight)) return parsedComputedMaxHeight;\n\n const inlineMaxHeight = Number.parseFloat(el.style.maxHeight);\n return Number.isFinite(inlineMaxHeight) ? inlineMaxHeight : null;\n};\n\nconst adjustWebTextareaHeight = (ref: React.RefObject<TextInput | null>) => {\n if (Platform.OS !== \"web\") return;\n // On web, the TextInput ref IS the DOM element\n const el = ref.current as unknown as HTMLTextAreaElement | null;\n if (!el) return;\n // Ensure rows=1 so scrollHeight reflects actual content\n if (el.rows !== 1) el.rows = 1;\n\n el.style.overflowX = \"hidden\";\n el.style.overflowY = \"hidden\";\n el.style.height = \"auto\";\n\n const scrollHeight = el.scrollHeight;\n const maxHeight = getWebTextareaMaxHeight(el);\n const needsScrollbar = maxHeight !== null && scrollHeight > maxHeight;\n const nextHeight =\n maxHeight === null ? scrollHeight : Math.min(scrollHeight, maxHeight);\n\n el.style.overflowY = needsScrollbar ? \"auto\" : \"hidden\";\n el.style.height = `${nextHeight}px`;\n};\n\nexport const ComposerInput = ({\n submitMode = \"enter\",\n onKeyPress: onKeyPressProp,\n numberOfLines,\n style,\n ...props\n}: ComposerInputProps) => {\n const aui = useAui();\n const text = useAuiState((s) => s.composer.text);\n const inputRef = useRef<TextInput>(null);\n\n const onChangeText = useCallback(\n (value: string) => {\n if (Platform.OS === \"web\") {\n // Keep the controlled value in sync mid-IME so react-dom does not reset the textarea to a stale value\n flushTapSync(() => {\n aui.composer.setText(value);\n });\n return;\n }\n aui.composer.setText(value);\n },\n [aui],\n );\n\n // Auto-resize textarea on web when text changes\n useEffect(() => {\n void text; // trigger re-run when text changes\n adjustWebTextareaHeight(inputRef);\n }, [text]);\n\n const onKeyPress = useCallback(\n (e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {\n onKeyPressProp?.(e);\n if (e.isDefaultPrevented()) return;\n\n if (Platform.OS !== \"web\") return;\n if (submitMode !== \"enter\") return;\n\n const nativeEvent = e.nativeEvent as TextInputKeyPressEventData & {\n shiftKey?: boolean;\n isComposing?: boolean;\n keyCode?: number;\n };\n // react-native-web forwards the raw DOM keydown before its own composition guard, so re-check it here (mirrors isEventComposing)\n if (nativeEvent.isComposing || nativeEvent.keyCode === 229) return;\n if (nativeEvent.key === \"Enter\" && !nativeEvent.shiftKey) {\n (e as unknown as Event).preventDefault?.();\n aui.composer.send();\n }\n },\n [aui, submitMode, onKeyPressProp],\n );\n\n return (\n <TextInput\n ref={inputRef}\n value={text}\n onChangeText={onChangeText}\n onKeyPress={onKeyPress}\n numberOfLines={numberOfLines ?? (Platform.OS === \"web\" ? 1 : undefined)}\n style={style}\n {...props}\n />\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "React Native bindings for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
],
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@assistant-ui/core": "^0.3.
|
|
35
|
-
"@assistant-ui/store": "^0.3.
|
|
36
|
-
"@assistant-ui/tap": "^0.9.
|
|
37
|
-
"assistant-stream": "^0.3.
|
|
34
|
+
"@assistant-ui/core": "^0.3.13",
|
|
35
|
+
"@assistant-ui/store": "^0.3.9",
|
|
36
|
+
"@assistant-ui/tap": "^0.9.12",
|
|
37
|
+
"assistant-stream": "^0.3.36",
|
|
38
38
|
"zustand": "^5.0.14"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/react": "^19.2.
|
|
52
|
-
"@types/react-dom": "^19.2.
|
|
53
|
-
"jsdom": "^
|
|
51
|
+
"@types/react": "^19.2.18",
|
|
52
|
+
"@types/react-dom": "^19.2.4",
|
|
53
|
+
"jsdom": "^30.0.1",
|
|
54
54
|
"react": "^19.2.8",
|
|
55
55
|
"react-dom": "^19.2.8",
|
|
56
|
-
"react-native": "^0.86.
|
|
56
|
+
"react-native": "^0.86.2",
|
|
57
57
|
"react-native-web": "^0.21.2",
|
|
58
58
|
"vitest": "^4.1.10",
|
|
59
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
59
|
+
"@assistant-ui/x-buildutils": "0.0.22"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public",
|
|
@@ -315,6 +315,18 @@ describe("ComposerInput", () => {
|
|
|
315
315
|
|
|
316
316
|
expect(onKeyPress).toHaveBeenCalledTimes(2);
|
|
317
317
|
});
|
|
318
|
+
|
|
319
|
+
it("does not submit when the consumer prevents the key event", async () => {
|
|
320
|
+
const input = await mount({
|
|
321
|
+
onKeyPress: (event) => event.preventDefault(),
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
await act(async () => {
|
|
325
|
+
fireKeyDown(input, { key: "Enter" });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
expect(h.sendSpy).not.toHaveBeenCalled();
|
|
329
|
+
});
|
|
318
330
|
});
|
|
319
331
|
|
|
320
332
|
describe("native", () => {
|
|
@@ -87,6 +87,7 @@ export const ComposerInput = ({
|
|
|
87
87
|
const onKeyPress = useCallback(
|
|
88
88
|
(e: NativeSyntheticEvent<TextInputKeyPressEventData>) => {
|
|
89
89
|
onKeyPressProp?.(e);
|
|
90
|
+
if (e.isDefaultPrevented()) return;
|
|
90
91
|
|
|
91
92
|
if (Platform.OS !== "web") return;
|
|
92
93
|
if (submitMode !== "enter") return;
|
|
@@ -74,11 +74,9 @@ describe("MessageContent", () => {
|
|
|
74
74
|
|
|
75
75
|
it("prefers a provided renderText over the default renderer", async () => {
|
|
76
76
|
h.state.message.content = [{ type: "text", text: "raw" }];
|
|
77
|
-
const renderText = vi.fn(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
),
|
|
81
|
-
);
|
|
77
|
+
const renderText = vi.fn(({ part, index }): ReactElement => (
|
|
78
|
+
<span data-testid={`text-${index}`}>custom:{part.text}</span>
|
|
79
|
+
));
|
|
82
80
|
await mount({ renderText });
|
|
83
81
|
|
|
84
82
|
expect(renderText).toHaveBeenCalledTimes(1);
|
|
@@ -115,18 +113,18 @@ describe("MessageContent", () => {
|
|
|
115
113
|
{ type: "source", sourceType: "url", id: "1", url: "u" },
|
|
116
114
|
{ type: "file", filename: "f" },
|
|
117
115
|
];
|
|
118
|
-
const renderImage = vi.fn(
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
const renderReasoning = vi.fn(
|
|
122
|
-
|
|
123
|
-
);
|
|
124
|
-
const renderSource = vi.fn(
|
|
125
|
-
|
|
126
|
-
);
|
|
127
|
-
const renderFile = vi.fn(
|
|
128
|
-
|
|
129
|
-
);
|
|
116
|
+
const renderImage = vi.fn(({ index }): ReactElement => (
|
|
117
|
+
<span>image-{index}</span>
|
|
118
|
+
));
|
|
119
|
+
const renderReasoning = vi.fn(({ index }): ReactElement => (
|
|
120
|
+
<span>reasoning-{index}</span>
|
|
121
|
+
));
|
|
122
|
+
const renderSource = vi.fn(({ index }): ReactElement => (
|
|
123
|
+
<span>source-{index}</span>
|
|
124
|
+
));
|
|
125
|
+
const renderFile = vi.fn(({ index }): ReactElement => (
|
|
126
|
+
<span>file-{index}</span>
|
|
127
|
+
));
|
|
130
128
|
await mount({ renderImage, renderReasoning, renderSource, renderFile });
|
|
131
129
|
|
|
132
130
|
expect(container.textContent).toBe("image-0reasoning-1source-2file-3");
|
|
@@ -183,13 +181,11 @@ describe("MessageContent", () => {
|
|
|
183
181
|
h.state.message.content = [
|
|
184
182
|
{ type: "tool-call", toolName: "search", toolCallId: "c1" },
|
|
185
183
|
];
|
|
186
|
-
const renderToolCall = vi.fn(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
),
|
|
192
|
-
);
|
|
184
|
+
const renderToolCall = vi.fn(({ part, index }): ReactElement => (
|
|
185
|
+
<span data-testid="fallback">
|
|
186
|
+
fallback:{String(part.toolName)}:{index}
|
|
187
|
+
</span>
|
|
188
|
+
));
|
|
193
189
|
await mount({ renderToolCall });
|
|
194
190
|
|
|
195
191
|
const el = container.querySelector('[data-testid="fallback"]');
|
|
@@ -239,13 +235,11 @@ describe("MessageContent", () => {
|
|
|
239
235
|
|
|
240
236
|
it("falls back to renderData when no renderer is registered", async () => {
|
|
241
237
|
h.state.message.content = [{ type: "data", name: "chart", data: {} }];
|
|
242
|
-
const renderData = vi.fn(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
),
|
|
248
|
-
);
|
|
238
|
+
const renderData = vi.fn(({ part, index }): ReactElement => (
|
|
239
|
+
<span data-testid="dfallback">
|
|
240
|
+
fallback:{String(part.name)}:{index}
|
|
241
|
+
</span>
|
|
242
|
+
));
|
|
249
243
|
await mount({ renderData });
|
|
250
244
|
|
|
251
245
|
const el = container.querySelector('[data-testid="dfallback"]');
|