@echothink-ui/agent 0.1.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.
Files changed (56) hide show
  1. package/README.md +5 -0
  2. package/dist/components/AgentApprovalGate.d.ts +10 -0
  3. package/dist/components/AgentContextViewer.d.ts +7 -0
  4. package/dist/components/AgentGeneratedArtifactPanel.d.ts +8 -0
  5. package/dist/components/AgentHandoffPanel.d.ts +22 -0
  6. package/dist/components/AgentInterruptionPanel.d.ts +13 -0
  7. package/dist/components/AgentMemoryPanel.d.ts +9 -0
  8. package/dist/components/AgentMessageList.d.ts +8 -0
  9. package/dist/components/AgentPlanDiff.d.ts +7 -0
  10. package/dist/components/AgentPlanPreview.d.ts +6 -0
  11. package/dist/components/AgentPromptBox.d.ts +15 -0
  12. package/dist/components/AgentRunControls.d.ts +10 -0
  13. package/dist/components/AgentSafetyPanel.d.ts +6 -0
  14. package/dist/components/AgentStateBadge.d.ts +6 -0
  15. package/dist/components/AgentThinkingChain.d.ts +8 -0
  16. package/dist/components/AgentThinkingPanel.d.ts +8 -0
  17. package/dist/components/AgentToolCallLog.d.ts +7 -0
  18. package/dist/components/AgentTraceViewer.d.ts +6 -0
  19. package/dist/components/AppDomainAgentPanel.d.ts +17 -0
  20. package/dist/components/ChatAgentRail.d.ts +24 -0
  21. package/dist/components/ScopeAttachmentPanel.d.ts +7 -0
  22. package/dist/components/utils.d.ts +20 -0
  23. package/dist/index.cjs +2709 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.css +2433 -0
  26. package/dist/index.css.map +1 -0
  27. package/dist/index.d.ts +44 -0
  28. package/dist/index.js +2666 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/types.d.ts +128 -0
  31. package/package.json +45 -0
  32. package/src/components/AgentApprovalGate.tsx +165 -0
  33. package/src/components/AgentContextViewer.tsx +161 -0
  34. package/src/components/AgentGeneratedArtifactPanel.tsx +224 -0
  35. package/src/components/AgentHandoffPanel.tsx +154 -0
  36. package/src/components/AgentInterruptionPanel.tsx +85 -0
  37. package/src/components/AgentMemoryPanel.tsx +167 -0
  38. package/src/components/AgentMessageList.tsx +209 -0
  39. package/src/components/AgentPlanDiff.tsx +149 -0
  40. package/src/components/AgentPlanPreview.tsx +106 -0
  41. package/src/components/AgentPromptBox.tsx +163 -0
  42. package/src/components/AgentRunControls.tsx +221 -0
  43. package/src/components/AgentSafetyPanel.tsx +113 -0
  44. package/src/components/AgentStateBadge.tsx +30 -0
  45. package/src/components/AgentThinkingChain.tsx +151 -0
  46. package/src/components/AgentThinkingPanel.tsx +56 -0
  47. package/src/components/AgentToolCallLog.tsx +262 -0
  48. package/src/components/AgentTraceViewer.tsx +218 -0
  49. package/src/components/AppDomainAgentPanel.tsx +66 -0
  50. package/src/components/ChatAgentRail.tsx +192 -0
  51. package/src/components/ScopeAttachmentPanel.tsx +130 -0
  52. package/src/components/utils.ts +186 -0
  53. package/src/index.test.tsx +212 -0
  54. package/src/index.tsx +88 -0
  55. package/src/styles.css +2902 -0
  56. package/src/types.ts +158 -0
@@ -0,0 +1,66 @@
1
+ import * as React from "react";
2
+ import type { AgentMessage, AgentRunState, ChatAgentRailAttachment } from "../types";
3
+ import { ChatAgentRail } from "./ChatAgentRail";
4
+
5
+ export interface AppDomainAgentPanelProps
6
+ extends Omit<React.HTMLAttributes<HTMLElement>, "children" | "onSubmit"> {
7
+ appDomainRef: string;
8
+ agentRef?: string;
9
+ projectRef?: string;
10
+ messages?: AgentMessage[];
11
+ state?: AgentRunState;
12
+ prompt?: string;
13
+ onPromptChange?: (value: string) => void;
14
+ onSubmit?: (prompt: string, attachments?: ChatAgentRailAttachment[]) => void;
15
+ attachments?: ChatAgentRailAttachment[];
16
+ onAttachmentRemove?: (id: string) => void;
17
+ runControls?: React.ReactNode;
18
+ children?: React.ReactNode;
19
+ }
20
+
21
+ export function AppDomainAgentPanel({
22
+ appDomainRef,
23
+ agentRef,
24
+ projectRef,
25
+ messages = [],
26
+ state,
27
+ prompt = "",
28
+ onPromptChange,
29
+ onSubmit,
30
+ attachments,
31
+ onAttachmentRemove,
32
+ runControls,
33
+ children,
34
+ ...props
35
+ }: AppDomainAgentPanelProps) {
36
+ return (
37
+ <ChatAgentRail
38
+ {...props}
39
+ agentRef={agentRef}
40
+ projectRef={projectRef}
41
+ messages={messages}
42
+ state={state}
43
+ prompt={prompt}
44
+ onPromptChange={onPromptChange ?? (() => undefined)}
45
+ onSubmit={onSubmit}
46
+ attachments={attachments}
47
+ onAttachmentRemove={onAttachmentRemove}
48
+ runControls={runControls}
49
+ scopeLabel={appDomainRef}
50
+ header={
51
+ <div className="eth-agent-app-domain-panel__header">
52
+ <p className="eth-agent-app-domain-panel__eyebrow">App domain</p>
53
+ <h2 className="eth-agent-app-domain-panel__title">{appDomainRef}</h2>
54
+ {agentRef || projectRef ? (
55
+ <div className="eth-agent-app-domain-panel__meta" aria-label="Agent panel scope">
56
+ {agentRef ? <span>Agent {agentRef}</span> : null}
57
+ {projectRef ? <span>Project {projectRef}</span> : null}
58
+ </div>
59
+ ) : null}
60
+ {children ? <div className="eth-agent-app-domain-panel__slot">{children}</div> : null}
61
+ </div>
62
+ }
63
+ data-eth-component="AppDomainAgentPanel"
64
+ />
65
+ );
66
+ }
@@ -0,0 +1,192 @@
1
+ import * as React from "react";
2
+ import clsx from "clsx";
3
+ import type { AgentMessage, AgentRunState, ChatAgentRailAttachment } from "../types";
4
+ import { AgentMessageList } from "./AgentMessageList";
5
+ import { AgentPromptBox } from "./AgentPromptBox";
6
+ import { AgentStateBadge } from "./AgentStateBadge";
7
+ import { ScopeAttachmentPanel } from "./ScopeAttachmentPanel";
8
+ import { isActiveAgentState, mergeStyle } from "./utils";
9
+
10
+ export interface ChatAgentRailProps extends Omit<
11
+ React.HTMLAttributes<HTMLElement>,
12
+ "children" | "onSubmit"
13
+ > {
14
+ agentRef?: string;
15
+ projectRef?: string;
16
+ messages: AgentMessage[];
17
+ state?: AgentRunState;
18
+ prompt: string;
19
+ onPromptChange: (value: string) => void;
20
+ onSubmit?: (prompt: string, attachments?: ChatAgentRailAttachment[]) => void;
21
+ attachments?: ChatAgentRailAttachment[];
22
+ onAttachmentRemove?: (id: string) => void;
23
+ onAttach?: () => void;
24
+ onSlash?: () => void;
25
+ autoScroll?: boolean;
26
+ runControls?: React.ReactNode;
27
+ header?: React.ReactNode;
28
+ scopeLabel?: React.ReactNode;
29
+ width?: number | string;
30
+ }
31
+
32
+ export interface LeftAgentRailProps extends ChatAgentRailProps {}
33
+
34
+ interface AgentRailBaseProps extends ChatAgentRailProps {
35
+ componentName: "ChatAgentRail" | "LeftAgentRail";
36
+ railClassName: string;
37
+ placement: "left" | "right";
38
+ }
39
+
40
+ export function ChatAgentRail(props: ChatAgentRailProps) {
41
+ return (
42
+ <AgentRailBase
43
+ {...props}
44
+ componentName="ChatAgentRail"
45
+ railClassName="eth-agent-chat-rail"
46
+ placement="right"
47
+ />
48
+ );
49
+ }
50
+
51
+ export function LeftAgentRail(props: LeftAgentRailProps) {
52
+ return (
53
+ <AgentRailBase
54
+ {...props}
55
+ componentName="LeftAgentRail"
56
+ railClassName="eth-left-agent-rail eth-agent-left-rail"
57
+ placement="left"
58
+ />
59
+ );
60
+ }
61
+
62
+ function AgentRailBase({
63
+ agentRef,
64
+ projectRef,
65
+ messages,
66
+ state = "idle",
67
+ prompt,
68
+ onPromptChange,
69
+ onSubmit,
70
+ attachments = [],
71
+ onAttachmentRemove,
72
+ onAttach,
73
+ onSlash,
74
+ autoScroll = true,
75
+ runControls,
76
+ header,
77
+ scopeLabel,
78
+ width = 360,
79
+ className,
80
+ style,
81
+ componentName,
82
+ railClassName,
83
+ placement,
84
+ "aria-label": ariaLabel,
85
+ ...props
86
+ }: AgentRailBaseProps) {
87
+ const streaming = messages.some((message) => message.streaming) || isActiveAgentState(state);
88
+ const border = "1px solid var(--eth-color-border-subtle)";
89
+
90
+ return (
91
+ <aside
92
+ {...props}
93
+ aria-label={ariaLabel ?? "Agent collaboration rail"}
94
+ className={clsx("eth-chat-rail", railClassName, className)}
95
+ data-eth-component={componentName}
96
+ data-placement={placement}
97
+ style={mergeStyle(
98
+ {
99
+ background: "var(--eth-color-layer-01)",
100
+ borderInlineEnd: placement === "left" ? border : 0,
101
+ borderInlineStart: placement === "right" ? border : 0,
102
+ display: "grid",
103
+ flex: `0 0 ${typeof width === "number" ? `${width}px` : width}`,
104
+ gridTemplateRows: "auto minmax(0, 1fr) auto",
105
+ height: "100%",
106
+ maxWidth: "100%",
107
+ minHeight: 0,
108
+ width
109
+ },
110
+ style
111
+ )}
112
+ >
113
+ <header
114
+ className="eth-chat-rail__header"
115
+ style={{
116
+ borderBottom: "1px solid var(--eth-color-border-subtle)",
117
+ display: "grid",
118
+ gap: "0.75rem",
119
+ padding: "1rem"
120
+ }}
121
+ >
122
+ <div
123
+ className="eth-chat-rail__header-main"
124
+ style={{
125
+ alignItems: "flex-start",
126
+ display: "flex",
127
+ gap: "0.75rem",
128
+ justifyContent: "space-between"
129
+ }}
130
+ >
131
+ <div style={{ minWidth: 0 }}>
132
+ {header ?? (
133
+ <>
134
+ <h2 className="eth-chat-rail__title" style={{ fontSize: "1rem", margin: 0 }}>
135
+ Agent
136
+ </h2>
137
+ {agentRef || projectRef ? (
138
+ <p
139
+ className="eth-chat-rail__meta"
140
+ style={{
141
+ color: "var(--eth-color-text-secondary)",
142
+ fontSize: "0.8rem",
143
+ margin: "0.25rem 0 0"
144
+ }}
145
+ >
146
+ {[agentRef, projectRef].filter(Boolean).join(" · ")}
147
+ </p>
148
+ ) : null}
149
+ </>
150
+ )}
151
+ </div>
152
+ <AgentStateBadge state={state} />
153
+ </div>
154
+ {runControls ? <div className="eth-chat-rail__run-controls">{runControls}</div> : null}
155
+ </header>
156
+
157
+ <AgentMessageList
158
+ className="eth-chat-rail__messages"
159
+ autoScroll={autoScroll}
160
+ messages={messages}
161
+ streaming={streaming}
162
+ style={{ minHeight: 0, padding: "1rem" }}
163
+ />
164
+
165
+ <footer
166
+ className="eth-chat-rail__footer"
167
+ style={{
168
+ borderTop: "1px solid var(--eth-color-border-subtle)",
169
+ display: "grid",
170
+ gap: "0.75rem",
171
+ padding: "1rem"
172
+ }}
173
+ >
174
+ <ScopeAttachmentPanel
175
+ attachments={attachments.map((attachment) => ({
176
+ ...attachment,
177
+ kind: attachment.kind === "file" ? "document" : attachment.kind
178
+ }))}
179
+ onRemove={onAttachmentRemove}
180
+ />
181
+ <AgentPromptBox
182
+ value={prompt}
183
+ onChange={onPromptChange}
184
+ scopeLabel={scopeLabel}
185
+ onAttach={onAttach}
186
+ onSlash={onSlash}
187
+ onSubmit={(value) => onSubmit?.(value, attachments)}
188
+ />
189
+ </footer>
190
+ </aside>
191
+ );
192
+ }
@@ -0,0 +1,130 @@
1
+ import * as React from "react";
2
+ import clsx from "clsx";
3
+ import { Badge, Button, StatusDot, Surface, statusLabel } from "@echothink-ui/core";
4
+ import type { SurfaceComponentProps } from "@echothink-ui/core";
5
+ import type { AgentScopeAttachment } from "../types";
6
+
7
+ export interface ScopeAttachmentPanelProps
8
+ extends Omit<SurfaceComponentProps, "children" | "items" | "metadata"> {
9
+ attachments: AgentScopeAttachment[];
10
+ onRemove?: (id: string) => void;
11
+ }
12
+
13
+ const attentionStatuses = new Set<AgentScopeAttachment["status"]>([
14
+ "approval-required",
15
+ "blocked",
16
+ "failed",
17
+ "pending-approval",
18
+ "stale",
19
+ "warning"
20
+ ]);
21
+
22
+ export function ScopeAttachmentPanel({
23
+ attachments,
24
+ onRemove,
25
+ title = "Attached context",
26
+ subtitle,
27
+ severity,
28
+ className,
29
+ ...props
30
+ }: ScopeAttachmentPanelProps) {
31
+ if (!attachments.length) return null;
32
+
33
+ const hasAttention = attachments.some((attachment) =>
34
+ attachment.status ? attentionStatuses.has(attachment.status) : false
35
+ );
36
+ const listLabel = typeof title === "string" ? `${title} items` : "Attached context items";
37
+ const renderedSubtitle =
38
+ subtitle ??
39
+ `${attachments.length} ${
40
+ attachments.length === 1 ? "scope is" : "scopes are"
41
+ } available to the agent run`;
42
+
43
+ return (
44
+ <Surface
45
+ {...props}
46
+ className={clsx("eth-agent-scope-attachments", className)}
47
+ data-eth-component="ScopeAttachmentPanel"
48
+ severity={severity ?? (hasAttention ? "warning" : undefined)}
49
+ subtitle={renderedSubtitle}
50
+ title={title}
51
+ >
52
+ <ul className="eth-agent-scope-attachments__list" aria-label={listLabel}>
53
+ {attachments.map((attachment) => (
54
+ <li
55
+ key={attachment.id}
56
+ className="eth-agent-scope-attachments__item"
57
+ data-kind={attachment.kind}
58
+ data-status={attachment.status}
59
+ >
60
+ <span className="eth-agent-scope-attachments__icon" aria-hidden="true">
61
+ {kindInitial(attachment.kind)}
62
+ </span>
63
+ <span className="eth-agent-scope-attachments__main">
64
+ <span className="eth-agent-scope-attachments__topline">
65
+ <strong className="eth-agent-scope-attachments__label">
66
+ {attachment.label}
67
+ </strong>
68
+ <Badge severity="neutral">{kindLabel(attachment.kind)}</Badge>
69
+ </span>
70
+ <span className="eth-agent-scope-attachments__meta">
71
+ {kindDescription(attachment.kind)}
72
+ </span>
73
+ </span>
74
+ <span className="eth-agent-scope-attachments__aside">
75
+ {attachment.status ? (
76
+ <StatusDot status={attachment.status} label={statusLabel(attachment.status)} />
77
+ ) : (
78
+ <span className="eth-agent-scope-attachments__untracked">No status</span>
79
+ )}
80
+ {onRemove ? (
81
+ <Button
82
+ aria-label={`Remove ${attachment.label}`}
83
+ density="compact"
84
+ intent="ghost"
85
+ onClick={() => onRemove(attachment.id)}
86
+ >
87
+ Remove
88
+ </Button>
89
+ ) : null}
90
+ </span>
91
+ </li>
92
+ ))}
93
+ </ul>
94
+ </Surface>
95
+ );
96
+ }
97
+
98
+ function kindLabel(kind: AgentScopeAttachment["kind"]) {
99
+ switch (kind) {
100
+ case "app-domain":
101
+ return "App domain";
102
+ case "document":
103
+ return "Document";
104
+ default:
105
+ return kind.charAt(0).toUpperCase() + kind.slice(1);
106
+ }
107
+ }
108
+
109
+ function kindDescription(kind: AgentScopeAttachment["kind"]) {
110
+ switch (kind) {
111
+ case "app-domain":
112
+ return "Runnable application boundary";
113
+ case "document":
114
+ return "Source document";
115
+ case "project":
116
+ return "Project scope";
117
+ case "resource":
118
+ return "External resource";
119
+ case "task":
120
+ return "Task context";
121
+ case "scope":
122
+ return "General scope";
123
+ default:
124
+ return "Attached context";
125
+ }
126
+ }
127
+
128
+ function kindInitial(kind: AgentScopeAttachment["kind"]) {
129
+ return kindLabel(kind).charAt(0);
130
+ }
@@ -0,0 +1,186 @@
1
+ import type * as React from "react";
2
+ import type { EthOperationalStatus, EthSeverity } from "@echothink-ui/core";
3
+ import type {
4
+ AgentDiffItem,
5
+ AgentPlanStep,
6
+ AgentRunState,
7
+ AgentSafetyCheck
8
+ } from "../types";
9
+
10
+ export const EMPTY_MARK = "—";
11
+
12
+ export function formatDateTime(value: string) {
13
+ const date = new Date(value);
14
+ if (Number.isNaN(date.getTime())) return value;
15
+ return new Intl.DateTimeFormat(undefined, {
16
+ dateStyle: "medium",
17
+ timeStyle: "short"
18
+ }).format(date);
19
+ }
20
+
21
+ export function formatTime(value: string) {
22
+ const date = new Date(value);
23
+ if (Number.isNaN(date.getTime())) return value;
24
+ return new Intl.DateTimeFormat(undefined, {
25
+ hour: "2-digit",
26
+ minute: "2-digit",
27
+ second: "2-digit"
28
+ }).format(date);
29
+ }
30
+
31
+ export function formatDuration(durationMs?: number) {
32
+ if (durationMs == null) return EMPTY_MARK;
33
+ if (durationMs < 1000) return `${Math.round(durationMs)} ms`;
34
+ return `${(durationMs / 1000).toFixed(durationMs < 10000 ? 1 : 0)} s`;
35
+ }
36
+
37
+ export function jsonPreview(value: unknown, redacted = false, maxLength = 120) {
38
+ if (redacted) return "[redacted]";
39
+ if (value == null) return EMPTY_MARK;
40
+ const text = typeof value === "string" ? value : JSON.stringify(value);
41
+ if (!text) return EMPTY_MARK;
42
+ return text.length > maxLength ? `${text.slice(0, maxLength - 1)}…` : text;
43
+ }
44
+
45
+ export function prettyJson(value: unknown, redacted = false) {
46
+ if (redacted) return "[redacted]";
47
+ if (value == null) return EMPTY_MARK;
48
+ if (typeof value === "string") return value;
49
+ return JSON.stringify(value, null, 2);
50
+ }
51
+
52
+ export function stateLabel(state: AgentRunState) {
53
+ switch (state) {
54
+ case "idle":
55
+ return "Idle";
56
+ case "listening":
57
+ return "Listening";
58
+ case "thinking":
59
+ return "Thinking";
60
+ case "tool-calling":
61
+ return "Calling tools";
62
+ case "waiting-for-user":
63
+ return "Waiting for user";
64
+ case "interrupted":
65
+ return "Interrupted";
66
+ case "failed":
67
+ return "Failed";
68
+ case "completed":
69
+ return "Completed";
70
+ }
71
+ }
72
+
73
+ export function stateSeverity(state: AgentRunState): EthSeverity {
74
+ switch (state) {
75
+ case "failed":
76
+ return "danger";
77
+ case "completed":
78
+ return "success";
79
+ case "waiting-for-user":
80
+ case "interrupted":
81
+ return "warning";
82
+ case "listening":
83
+ case "thinking":
84
+ case "tool-calling":
85
+ return "info";
86
+ case "idle":
87
+ return "neutral";
88
+ }
89
+ }
90
+
91
+ export function stateStatus(state: AgentRunState): EthOperationalStatus {
92
+ switch (state) {
93
+ case "completed":
94
+ return "completed";
95
+ case "failed":
96
+ return "failed";
97
+ case "waiting-for-user":
98
+ return "pending-approval";
99
+ case "interrupted":
100
+ return "warning";
101
+ case "listening":
102
+ case "thinking":
103
+ case "tool-calling":
104
+ return "running";
105
+ case "idle":
106
+ return "inactive";
107
+ }
108
+ }
109
+
110
+ export function isActiveAgentState(state: AgentRunState) {
111
+ return state === "listening" || state === "thinking" || state === "tool-calling";
112
+ }
113
+
114
+ export function statusSeverity(status: EthOperationalStatus): EthSeverity {
115
+ if (status === "failed" || status === "blocked" || status === "approval-required") {
116
+ return "danger";
117
+ }
118
+ if (status === "warning" || status === "pending-approval") return "warning";
119
+ if (status === "completed" || status === "succeeded" || status === "synced") return "success";
120
+ if (status === "running" || status === "in-progress" || status === "active") return "info";
121
+ return "neutral";
122
+ }
123
+
124
+ export function riskSeverity(riskLevel: "low" | "medium" | "high" | "critical" = "low") {
125
+ if (riskLevel === "critical" || riskLevel === "high") return "danger" satisfies EthSeverity;
126
+ if (riskLevel === "medium") return "warning" satisfies EthSeverity;
127
+ return "info" satisfies EthSeverity;
128
+ }
129
+
130
+ export function safetyStatus(check: AgentSafetyCheck): EthOperationalStatus {
131
+ if (check.status === "pass") return "completed";
132
+ if (check.status === "warn") return "warning";
133
+ return "failed";
134
+ }
135
+
136
+ export function safetySeverity(check: AgentSafetyCheck): EthSeverity {
137
+ if (check.status === "pass") return "success";
138
+ if (check.status === "warn") return "warning";
139
+ return "danger";
140
+ }
141
+
142
+ export function mergeStyle(
143
+ base: React.CSSProperties,
144
+ incoming?: React.CSSProperties
145
+ ): React.CSSProperties {
146
+ return incoming ? { ...base, ...incoming } : base;
147
+ }
148
+
149
+ export function diffPlanSteps(before: AgentPlanStep[], after: AgentPlanStep[]): AgentDiffItem[] {
150
+ const beforeById = new Map(before.map((step) => [step.id, step]));
151
+ const afterById = new Map(after.map((step) => [step.id, step]));
152
+ const ids = Array.from(
153
+ new Set([...after.map((step) => step.id), ...before.map((step) => step.id)])
154
+ );
155
+
156
+ return ids.map((id) => {
157
+ const beforeStep = beforeById.get(id);
158
+ const afterStep = afterById.get(id);
159
+ if (!beforeStep && afterStep) {
160
+ return { id, after: afterStep, status: "added", severity: "success" };
161
+ }
162
+ if (beforeStep && !afterStep) {
163
+ return { id, before: beforeStep, status: "removed", severity: "danger" };
164
+ }
165
+ const changed = JSON.stringify(beforeStep) !== JSON.stringify(afterStep);
166
+ return {
167
+ id,
168
+ before: beforeStep,
169
+ after: afterStep,
170
+ status: changed ? "modified" : "unchanged",
171
+ severity: changed ? "warning" : "neutral"
172
+ };
173
+ });
174
+ }
175
+
176
+ export const visuallyHiddenStyle: React.CSSProperties = {
177
+ border: 0,
178
+ clip: "rect(0 0 0 0)",
179
+ height: 1,
180
+ margin: -1,
181
+ overflow: "hidden",
182
+ padding: 0,
183
+ position: "absolute",
184
+ whiteSpace: "nowrap",
185
+ width: 1
186
+ };