fenix-claude-plugin 0.1.2 → 0.2.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.
- package/.claude-plugin/plugin.json +13 -13
- package/.mcp.json +11 -11
- package/README.md +33 -40
- package/artifacts/collections.html +149 -0
- package/artifacts/fenix-workspace.html +1285 -0
- package/artifacts/oa-editor.html +210 -0
- package/artifacts/patent-editor.html +325 -0
- package/artifacts/review-workspace.html +1135 -0
- package/commands/fenix-install.md +19 -11
- package/commands/fenix-setup.md +18 -13
- package/package.json +16 -16
- package/skills/fenix-application/SKILL.md +38 -11
- package/skills/fenix-application/VERSION +1 -1
- package/skills/fenix-application/references/claim1-method-drafting.md +1 -1
- package/skills/fenix-application/references/claims.md +4 -5
- package/skills/fenix-application/references/disclosure.md +116 -10
- package/skills/fenix-application/references/editors.md +228 -39
- package/skills/fenix-application/references/figure-description.md +1 -1
- package/skills/fenix-application/references/figure-strategy.md +166 -0
- package/skills/fenix-application/references/figures-guidance.md +1 -1
- package/skills/fenix-application/references/figures.md +60 -9
- package/skills/fenix-application/references/patent-review-checklist.md +3 -1
- package/skills/fenix-application/references/patent-review.md +6 -2
- package/skills/fenix-application/references/patent-safe.md +1 -1
- package/skills/fenix-application/references/patent.md +11 -10
- package/skills/fenix-application/references/spec-guidance.md +1 -1
- package/skills/fenix-application/references/spec.md +17 -7
- package/skills/fenix-application/scripts/figure_strategy_server.py +136 -0
- package/skills/fenix-diagram/SKILL.md +54 -0
- package/skills/fenix-diagram/VERSION +1 -0
- package/skills/fenix-diagram/references/diagram-svg-guide.md +271 -0
- package/skills/fenix-office-action/SKILL.md +9 -7
- package/skills/fenix-office-action/VERSION +1 -1
- package/skills/fenix-office-action/references/oa-analysis.md +14 -4
- package/skills/fenix-office-action/references/oa-drafting.md +144 -50
- package/skills/fenix-office-action/references/oa-response.md +31 -18
- package/skills/fenix-project/SKILL.md +4 -3
- package/skills/fenix-project/VERSION +1 -1
- package/skills/fenix-project/references/create-project.md +11 -1
- package/artifacts/practice-dashboard.html +0 -469
- package/artifacts/project-overview.html +0 -481
- package/artifacts/user-dashboard.html +0 -368
- package/skills/fenix-application/CUSTOMIZE.md +0 -1
- package/skills/fenix-application/assets/claims_editor.html +0 -86
- package/skills/fenix-application/assets/figures_editor.html +0 -86
- package/skills/fenix-application/assets/spec_editor.html +0 -86
- package/skills/fenix-office-action/CUSTOMIZE.md +0 -1
- package/skills/fenix-project/CUSTOMIZE.md +0 -1
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Team Member</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root{
|
|
9
|
-
color-scheme: dark;
|
|
10
|
-
--bg:#0c0e13; --card:#14171f; --card2:#191d27; --ink:#c3c8d4; --muted:#7e8598;
|
|
11
|
-
--line:#242936; --line2:#2f3543;
|
|
12
|
-
--accent:#818cf8; --accent2:#6366f1; --accentbg:#1c2030; --accentglow:#2a2f4a;
|
|
13
|
-
--red:#f87171; --redbg:#2a1417; --orange:#fb923c; --orangebg:#2a1c10;
|
|
14
|
-
--amber:#fbbf24; --amberbg:#2a230f; --green:#34d399; --greenbg:#0f2a20;
|
|
15
|
-
--gray:#8b92a6; --graybg:#1b1f29; --purple:#a78bfa; --purplebg:#211a33;
|
|
16
|
-
}
|
|
17
|
-
*{box-sizing:border-box}
|
|
18
|
-
body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
|
|
19
|
-
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
|
|
20
|
-
.wrap{max-width:1280px;margin:0 auto;padding:22px 20px 80px}
|
|
21
|
-
.top{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-bottom:4px}
|
|
22
|
-
.brand{display:flex;align-items:center;gap:13px}
|
|
23
|
-
.brand .logo{display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;border-radius:11px;border:1px solid var(--line2);background:#0a0c11;line-height:0}
|
|
24
|
-
.brand .logo img{display:block;width:38px;height:38px;object-fit:contain}
|
|
25
|
-
h1{font-size:22px;margin:0;letter-spacing:-.02em;font-weight:800}
|
|
26
|
-
.sub{color:var(--muted);font-size:12.5px;margin-top:3px}
|
|
27
|
-
.prog{display:flex;flex-wrap:wrap;gap:7px;margin:14px 0 16px;min-height:24px}
|
|
28
|
-
.chip{display:flex;align-items:center;gap:7px;background:var(--card);border:1px solid var(--line);border-radius:20px;padding:4px 11px;font-size:11.5px;color:var(--muted)}
|
|
29
|
-
.chip.done{color:var(--green);border-color:transparent;background:var(--greenbg)}
|
|
30
|
-
.chip.err{color:var(--red);border-color:transparent;background:var(--redbg)}
|
|
31
|
-
.chip .sp{width:11px;height:11px;border:2px solid var(--line2);border-top-color:var(--accent);border-radius:50%;animation:s .7s linear infinite;display:inline-block}
|
|
32
|
-
.bar{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;margin-bottom:18px}
|
|
33
|
-
.fld{display:flex;flex-direction:column;gap:5px}
|
|
34
|
-
.fld label{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:700}
|
|
35
|
-
select{background:var(--card2);border:1px solid var(--line2);border-radius:9px;padding:9px 12px;color:var(--ink);font-size:13px;min-width:300px}
|
|
36
|
-
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:11px;margin-bottom:20px}
|
|
37
|
-
.kpi{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:15px 16px;position:relative;overflow:hidden}
|
|
38
|
-
.kpi .n{font-size:30px;font-weight:800;letter-spacing:-.03em;line-height:1}
|
|
39
|
-
.kpi .l{color:var(--muted);font-size:11.5px;margin-top:7px}
|
|
40
|
-
.kpi.accent .n{color:var(--accent)}
|
|
41
|
-
.kpi.purple{background:linear-gradient(180deg,var(--purplebg),var(--card));border-color:#2e2547}
|
|
42
|
-
.kpi.purple .n{color:var(--purple)}
|
|
43
|
-
.kpi.red{background:linear-gradient(180deg,var(--redbg),var(--card));border-color:#3a1f24}
|
|
44
|
-
.kpi.red .n{color:var(--red)}
|
|
45
|
-
.ld{color:var(--muted);opacity:.4}
|
|
46
|
-
.panel{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px}
|
|
47
|
-
.panel h3{margin:0 0 14px;font-size:11.5px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:700;display:flex;justify-content:space-between;align-items:center;gap:8px}
|
|
48
|
-
.panel h3 .cnt{color:var(--ink);font-weight:800}
|
|
49
|
-
.profile{display:flex;align-items:center;gap:15px;margin-bottom:6px}
|
|
50
|
-
.av{width:54px;height:54px;border-radius:50%;background:var(--accentbg);color:var(--accent);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:17px;flex-shrink:0;border:1px solid var(--line2)}
|
|
51
|
-
.pname{font-size:18px;font-weight:800;margin:0;letter-spacing:-.01em}
|
|
52
|
-
.pmeta{color:var(--muted);font-size:12.5px;margin-top:3px}
|
|
53
|
-
.pill{display:inline-block;padding:2px 10px;border-radius:20px;font-size:11px;font-weight:700;white-space:nowrap;margin-left:8px;vertical-align:middle}
|
|
54
|
-
.due{font-weight:700;white-space:nowrap}
|
|
55
|
-
.days{display:inline-block;margin-left:7px;padding:1px 8px;border-radius:6px;font-size:10.5px;font-weight:700}
|
|
56
|
-
.lk{color:var(--accent);text-decoration:none;font-weight:700;font-size:12px;white-space:nowrap}
|
|
57
|
-
.lk:hover{text-decoration:underline}
|
|
58
|
-
.center{text-align:center;color:var(--muted);padding:30px 0}
|
|
59
|
-
.spin{width:26px;height:26px;border:3px solid var(--line2);border-top-color:var(--accent);border-radius:50%;animation:s .7s linear infinite;margin:0 auto 13px}
|
|
60
|
-
@keyframes s{to{transform:rotate(360deg)}}
|
|
61
|
-
.atable{width:100%;border-collapse:separate;border-spacing:0}
|
|
62
|
-
.atable th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);font-weight:700;padding:0 8px 9px}
|
|
63
|
-
.atable td{padding:11px 8px;border-top:1px solid var(--line);vertical-align:middle}
|
|
64
|
-
.title{font-weight:700}
|
|
65
|
-
.meta{color:var(--muted);font-size:12px;margin-top:2px}
|
|
66
|
-
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:8px;vertical-align:middle}
|
|
67
|
-
.hbar{height:7px;border-radius:5px;background:var(--graybg);overflow:hidden;margin:4px 0 6px}
|
|
68
|
-
.hbar > i{display:block;height:100%;background:linear-gradient(90deg,var(--accent2),var(--accent));border-radius:5px}
|
|
69
|
-
.num{font-weight:800}
|
|
70
|
-
.cbtn{background:var(--card2);border:1px solid var(--line2);color:var(--ink);border-radius:8px;padding:5px 11px;font-size:12px;font-weight:700;cursor:pointer;white-space:nowrap;font-family:inherit}
|
|
71
|
-
.cbtn:hover{background:var(--greenbg);border-color:transparent;color:var(--green)}
|
|
72
|
-
.cbtn.arm{background:var(--green);border-color:transparent;color:#06281d}
|
|
73
|
-
.cbtn:disabled{opacity:.55;cursor:default}
|
|
74
|
-
.hint{background:var(--card2);border:1px solid var(--line);border-radius:11px;padding:11px 14px;color:var(--muted);font-size:12px;margin-top:6px}
|
|
75
|
-
@media(max-width:980px){ .kpis{grid-template-columns:repeat(2,1fr)} }
|
|
76
|
-
@media(max-width:560px){ select{min-width:0;width:100%} }
|
|
77
|
-
</style>
|
|
78
|
-
</head>
|
|
79
|
-
<body>
|
|
80
|
-
<div class="wrap">
|
|
81
|
-
<div class="top">
|
|
82
|
-
<div class="brand">
|
|
83
|
-
<span class="logo" aria-label="Fenix logo" role="img"><img src="data:image/png;base64,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" alt="Fenix logo" width="38" height="38"></span>
|
|
84
|
-
<div>
|
|
85
|
-
<h1>Team Member</h1>
|
|
86
|
-
<div class="sub" id="sub">Live · loading…</div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<div class="bar" style="align-items:flex-end">
|
|
92
|
-
<div class="fld">
|
|
93
|
-
<label for="userSel">Team member</label>
|
|
94
|
-
<select id="userSel"><option>Loading…</option></select>
|
|
95
|
-
</div>
|
|
96
|
-
<a href="#" id="resetDef" class="lk" style="display:none;padding-bottom:10px">↺ Reset to default</a>
|
|
97
|
-
<div class="prog" id="prog" style="margin:0 0 4px auto"></div>
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
<div class="kpis" id="kpis"></div>
|
|
101
|
-
|
|
102
|
-
<div class="panel" id="profilePanel" style="margin-bottom:14px">
|
|
103
|
-
<h3>Profile</h3>
|
|
104
|
-
<div class="center"><div class="spin"></div>Loading team…</div>
|
|
105
|
-
</div>
|
|
106
|
-
|
|
107
|
-
<div class="panel">
|
|
108
|
-
<h3>Active tasks <span class="cnt" id="taskCount"></span></h3>
|
|
109
|
-
<div id="tasksBody"><div class="center">Select a team member to see their open tasks.</div></div>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<!--
|
|
114
|
-
┌─────────────────────────────────────────────────────────────────────────┐
|
|
115
|
-
│ CLAUDE: SET THE DEFAULT TEAM MEMBER HERE │
|
|
116
|
-
│ │
|
|
117
|
-
│ When the user asks to open this view "for <person>", edit the │
|
|
118
|
-
│ DEFAULT_USER constant in the <script> below to that person's INITIALS │
|
|
119
|
-
│ (e.g. "MAC") or full NAME (e.g. "Michael Carey"), then re-publish the │
|
|
120
|
-
│ artifact with update_artifact. │
|
|
121
|
-
│ │
|
|
122
|
-
│ Behavior: │
|
|
123
|
-
│ • FIRST load (no saved choice yet) → the view selects DEFAULT_USER. │
|
|
124
|
-
│ • After the user picks anyone, that choice is saved to localStorage │
|
|
125
|
-
│ ("fx_user") and OVERRIDES DEFAULT_USER on every later open. │
|
|
126
|
-
│ • To force the default to win again, the user clears it — there is a │
|
|
127
|
-
│ "Reset to default" link next to the dropdown that wipes fx_user. │
|
|
128
|
-
│ • Leave DEFAULT_USER = "" to just open on whoever sorts first. │
|
|
129
|
-
│ │
|
|
130
|
-
│ Valid initials come from list_users (e.g. NJZ, MAC, BJA, STA, ...). │
|
|
131
|
-
└─────────────────────────────────────────────────────────────────────────┘
|
|
132
|
-
-->
|
|
133
|
-
<script>
|
|
134
|
-
const SRV='mcp__25de2133-8f0a-47c4-b66d-b520d6be5fe7__';
|
|
135
|
-
const BASE='https://chau.fenix.ai/dashboard/projects/';
|
|
136
|
-
|
|
137
|
-
// ▼▼▼ CLAUDE: edit this one value to choose who the view opens on first load. ▼▼▼
|
|
138
|
-
const DEFAULT_USER = ""; // initials ("MAC") or full name ("Michael Carey"); "" = first in list
|
|
139
|
-
// ▲▲▲ Saved user selections (localStorage "fx_user") override this after first use. ▲▲▲
|
|
140
|
-
let USERS=[],PROJECTS=[],TASKS=[];
|
|
141
|
-
const today=(()=>{const n=new Date();return new Date(n.getFullYear(),n.getMonth(),n.getDate())})();
|
|
142
|
-
|
|
143
|
-
function unwrap(res){
|
|
144
|
-
if(res==null) return null;
|
|
145
|
-
if(typeof res==='string'){ try{return JSON.parse(res)}catch(e){return res} }
|
|
146
|
-
if(Array.isArray(res)) return res;
|
|
147
|
-
if(Array.isArray(res.content)){ const t=res.content.map(c=>c&&c.text||'').join(''); try{return JSON.parse(t)}catch(e){return res} }
|
|
148
|
-
if(res.result!==undefined) return unwrap(res.result);
|
|
149
|
-
return res;
|
|
150
|
-
}
|
|
151
|
-
function asArray(res,key){ const u=unwrap(res); if(Array.isArray(u))return u; if(u&&Array.isArray(u[key]))return u[key]; return []; }
|
|
152
|
-
function call(tool,args){ return window.cowork.callMcpTool(SRV+tool,args||{}).catch(()=>null); }
|
|
153
|
-
function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]))}
|
|
154
|
-
|
|
155
|
-
function pd(raw){
|
|
156
|
-
if(!raw) return null;
|
|
157
|
-
let s=String(raw).trim(); if(!s) return null;
|
|
158
|
-
s=s.replace(/(\d+)(st|nd|rd|th)/gi,'$1');
|
|
159
|
-
let iso=s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})/);
|
|
160
|
-
if(iso) return new Date(+iso[1],+iso[2]-1,+iso[3]);
|
|
161
|
-
let mdy=s.match(/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2,4})$/);
|
|
162
|
-
if(mdy){ let mo=+mdy[1],d=+mdy[2],y=+mdy[3]; if(y<100)y+=2000; return new Date(y,mo-1,d); }
|
|
163
|
-
let t=Date.parse(s); if(isNaN(t)) return null;
|
|
164
|
-
const dt=new Date(t); return new Date(dt.getFullYear(),dt.getMonth(),dt.getDate());
|
|
165
|
-
}
|
|
166
|
-
function dleft(d){ return d?Math.round((d-today)/86400000):null; }
|
|
167
|
-
function fmt(d){ return d?d.toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'}):'—'; }
|
|
168
|
-
function urg(dl){
|
|
169
|
-
if(dl===null) return {bg:'var(--graybg)',fg:'var(--gray)',lab:'No date'};
|
|
170
|
-
if(dl<0) return {bg:'var(--redbg)',fg:'var(--red)',lab:Math.abs(dl)+'d overdue'};
|
|
171
|
-
if(dl<=7) return {bg:'var(--redbg)',fg:'var(--red)',lab:dl+'d left'};
|
|
172
|
-
if(dl<=21) return {bg:'var(--orangebg)',fg:'var(--orange)',lab:dl+'d left'};
|
|
173
|
-
if(dl<=45) return {bg:'var(--amberbg)',fg:'var(--amber)',lab:dl+'d left'};
|
|
174
|
-
return {bg:'var(--greenbg)',fg:'var(--green)',lab:dl+'d left'};
|
|
175
|
-
}
|
|
176
|
-
function dueCell(d){ const dl=d?dleft(d):null,u=urg(dl);
|
|
177
|
-
return `<span class="due mono">${fmt(d)}</span><span class="days" style="background:${u.bg};color:${u.fg}">${u.lab}</span>`; }
|
|
178
|
-
function initialsOf(u){ if(u.initials) return u.initials.toUpperCase();
|
|
179
|
-
const p=(u.name||'?').split(/\s+/); return ((p[0]||'')[0]||'')+((p[1]||'')[0]||''); }
|
|
180
|
-
|
|
181
|
-
function setChip(id,state,label){
|
|
182
|
-
const el=document.getElementById('chip-'+id); if(!el) return;
|
|
183
|
-
el.className='chip'+(state==='done'?' done':state==='err'?' err':'');
|
|
184
|
-
el.innerHTML=(state==='load'?'<span class="sp"></span>':state==='done'?'✓ ':'✕ ')+label;
|
|
185
|
-
}
|
|
186
|
-
function statusDot(s){ const l=(s||'').toLowerCase();
|
|
187
|
-
let c='var(--accent)'; if(l==='completed')c='var(--green)'; else if(l==='in-progress')c='var(--amber)'; else if(l==='inactive')c='var(--gray)';
|
|
188
|
-
return `<span class="dot" style="background:${c}"></span>`; }
|
|
189
|
-
|
|
190
|
-
// TASKS is fetched per-user (list_open_tasks({assigneeId})), so it already holds
|
|
191
|
-
// only the selected user's tasks; just drop any that have been completed.
|
|
192
|
-
function tasksFor(u){
|
|
193
|
-
if(!u||!Array.isArray(TASKS)) return [];
|
|
194
|
-
return TASKS.filter(t=>t.status!=='completed');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Two-click confirm, then mark the task complete in Fenix via complete_task.
|
|
198
|
-
function completeTask(taskId, btn){
|
|
199
|
-
if(!taskId) return;
|
|
200
|
-
if(btn.dataset.armed!=='1'){
|
|
201
|
-
btn.dataset.armed='1'; btn.textContent='Confirm?'; btn.classList.add('arm');
|
|
202
|
-
clearTimeout(btn._t);
|
|
203
|
-
btn._t=setTimeout(()=>{ btn.dataset.armed='0'; btn.textContent='✓ Done'; btn.classList.remove('arm'); },4000);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
clearTimeout(btn._t); doComplete(taskId, btn);
|
|
207
|
-
}
|
|
208
|
-
async function doComplete(taskId, btn){
|
|
209
|
-
btn.disabled=true; btn.textContent='…'; btn.classList.remove('arm');
|
|
210
|
-
const res=unwrap(await call('complete_task',{taskId}));
|
|
211
|
-
const ok = res!=null && res!==false && !(res&&res.error);
|
|
212
|
-
if(ok){
|
|
213
|
-
const t=Array.isArray(TASKS)?TASKS.find(x=>x.id===taskId):null; if(t) t.status='completed';
|
|
214
|
-
const sel=document.getElementById('userSel').value;
|
|
215
|
-
renderKpis(USERS.find(u=>u.userId===sel)); renderTasks(sel);
|
|
216
|
-
} else {
|
|
217
|
-
btn.disabled=false; btn.dataset.armed='0'; btn.textContent='Retry';
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function renderKpis(u){
|
|
222
|
-
const mine=u?tasksFor(u):[];
|
|
223
|
-
const overdue=mine.filter(t=>t._dl!==null&&t._dl<0).length;
|
|
224
|
-
const v=x=>u?x:'…';
|
|
225
|
-
document.getElementById('kpis').innerHTML=`
|
|
226
|
-
<div class="kpi accent"><div class="n mono">${v(mine.length)}</div><div class="l">Open tasks</div></div>
|
|
227
|
-
<div class="kpi red"><div class="n mono">${v(overdue)}</div><div class="l">Overdue tasks</div></div>
|
|
228
|
-
<div class="kpi"><div class="n mono">${u?(u.totalEstimatedHours??0):'…'}</div><div class="l">Estimated hours</div></div>
|
|
229
|
-
<div class="kpi purple"><div class="n mono">${u?(u.totalActiveProjects??0):'…'}</div><div class="l">Active projects</div></div>`;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
async function renderProfile(userId){
|
|
233
|
-
const panel=document.getElementById('profilePanel');
|
|
234
|
-
const u=USERS.find(x=>x.userId===userId);
|
|
235
|
-
renderKpis(u);
|
|
236
|
-
if(!u){ panel.innerHTML='<h3>Profile</h3><div class="center">No user selected.</div>'; return; }
|
|
237
|
-
let avail='',email='';
|
|
238
|
-
if(u.initials){ const info=unwrap(await call('get_user_by_initials',{initials:u.initials}));
|
|
239
|
-
if(info&&info.found){ avail=info.availabilityStatus||''; email=info.email||''; } }
|
|
240
|
-
const al=avail.toLowerCase();
|
|
241
|
-
let abg='var(--graybg)',afg='var(--gray)';
|
|
242
|
-
if(al==='busy'){abg='var(--redbg)';afg='var(--red)';}
|
|
243
|
-
else if(al==='available'){abg='var(--greenbg)';afg='var(--green)';}
|
|
244
|
-
const badge=avail?`<span class="pill" style="background:${abg};color:${afg}">${esc(avail)}</span>`:'';
|
|
245
|
-
panel.innerHTML=`
|
|
246
|
-
<h3>Profile</h3>
|
|
247
|
-
<div class="profile">
|
|
248
|
-
<div class="av">${esc(initialsOf(u)||'?')}</div>
|
|
249
|
-
<div>
|
|
250
|
-
<p class="pname">${esc(u.name||'Unknown')} ${badge}</p>
|
|
251
|
-
<p class="pmeta">${u.initials?esc(u.initials)+' · ':''}${email?esc(email):'<span class="ld">no email on file</span>'}</p>
|
|
252
|
-
</div>
|
|
253
|
-
</div>
|
|
254
|
-
<table class="atable" style="margin-top:14px">
|
|
255
|
-
<tr><td style="border-top:0;color:var(--muted)">Estimated hours</td><td style="border-top:0;text-align:right" class="num mono">${u.totalEstimatedHours??0}</td></tr>
|
|
256
|
-
<tr><td style="color:var(--muted)">Active projects</td><td style="text-align:right" class="num mono">${u.totalActiveProjects??0}</td></tr>
|
|
257
|
-
</table>`;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function renderTasks(userId){
|
|
261
|
-
const u=USERS.find(x=>x.userId===userId);
|
|
262
|
-
const body=document.getElementById('tasksBody');
|
|
263
|
-
const cnt=document.getElementById('taskCount');
|
|
264
|
-
if(!u){ body.innerHTML='<div class="center">Select a team member.</div>'; cnt.textContent=''; return; }
|
|
265
|
-
if(TASKS===null){
|
|
266
|
-
cnt.textContent='';
|
|
267
|
-
body.innerHTML='<div class="hint">Couldn’t load tasks for '+esc(u.name)+' (the <span class="mono">list_open_tasks</span> call failed). Estimated hours and active-project counts above still reflect this member’s load. Try reselecting the member.</div>';
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if(TASKS.length===0 && body.dataset.loading==='1'){
|
|
271
|
-
cnt.textContent=''; body.innerHTML='<div class="center"><div class="spin"></div>Loading tasks…</div>'; return;
|
|
272
|
-
}
|
|
273
|
-
const mine=tasksFor(u).slice().sort((a,b)=>(a._d?a._d:9e15)-(b._d?b._d:9e15));
|
|
274
|
-
cnt.textContent=mine.length?'· '+mine.length:'';
|
|
275
|
-
if(!mine.length){ body.innerHTML='<div class="center">No open tasks assigned to '+esc(u.name)+'.</div>'; return; }
|
|
276
|
-
const rows=mine.map(t=>`
|
|
277
|
-
<tr><td>${statusDot(t.status)}<span class="title">${esc(t.name||'(task)')}</span></td>
|
|
278
|
-
<td>${esc(t.matterNum||'—')}${t.client&&t.client!=='Unassigned'?'<div class="meta">'+esc(t.client)+'</div>':''}</td>
|
|
279
|
-
<td>${dueCell(t._d)}</td>
|
|
280
|
-
<td style="text-align:right;white-space:nowrap">
|
|
281
|
-
<button class="cbtn" onclick="completeTask('${esc(t.id)}',this)"${t.id?'':' disabled title="No task id available"'}>✓ Done</button>
|
|
282
|
-
<a class="lk" style="margin-left:12px" href="${esc(t.url)}" target="_blank" rel="noopener">Open ↗</a>
|
|
283
|
-
</td></tr>`).join('');
|
|
284
|
-
body.innerHTML=`<table class="atable">
|
|
285
|
-
<thead><tr><th>Task</th><th>Matter</th><th>Due</th><th style="text-align:right">Action</th></tr></thead>
|
|
286
|
-
<tbody>${rows}</tbody></table>`;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function fillSelect(el,items,valKey,labelFn,saved){
|
|
290
|
-
el.innerHTML=items.map(i=>`<option value="${esc(i[valKey])}">${esc(labelFn(i))}</option>`).join('');
|
|
291
|
-
if(saved&&items.some(i=>i[valKey]===saved)) el.value=saved;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Resolve the Claude-set DEFAULT_USER (initials or full name) to a userId.
|
|
295
|
-
function resolveDefaultUser(){
|
|
296
|
-
const d=String(DEFAULT_USER||'').trim().toLowerCase();
|
|
297
|
-
if(!d) return null;
|
|
298
|
-
const m=USERS.find(u=>(u.initials||'').toLowerCase()===d)||USERS.find(u=>(u.name||'').toLowerCase()===d);
|
|
299
|
-
return m?m.userId:null;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function mapTasks(res){
|
|
303
|
-
const pc={}; PROJECTS.forEach(p=>{pc[p.projectId]=p.clientName;});
|
|
304
|
-
TASKS=asArray(res,'tasks').map(t=>{const d=pd(t.dueDate);return {
|
|
305
|
-
id:t._id||t.taskId||t.id||'',
|
|
306
|
-
name:t.taskName||t.name||'(task)', matterNum:t.matterNum||'',
|
|
307
|
-
client:pc[t.projectId]||t.clientName||'Unassigned',
|
|
308
|
-
assigneeId:(t.assignee&&t.assignee.userId)||'',
|
|
309
|
-
status:t.status||'active', projectId:t.projectId, url:BASE+(t.projectId||''),
|
|
310
|
-
_d:d, _dl:dleft(d)
|
|
311
|
-
};});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Fetch only the selected user's open tasks via the server-side assigneeId
|
|
315
|
-
// filter (small, exact payload — no firm-wide pull, no initials matching).
|
|
316
|
-
async function loadTasks(userId){
|
|
317
|
-
const body=document.getElementById('tasksBody');
|
|
318
|
-
TASKS=[];
|
|
319
|
-
if(!userId){ body.dataset.loading='0'; renderTasks(userId); return; }
|
|
320
|
-
body.dataset.loading='1'; renderTasks(userId); // show spinner
|
|
321
|
-
const res=await call('list_open_tasks',{assigneeId:userId});
|
|
322
|
-
body.dataset.loading='0';
|
|
323
|
-
if(res===null){ TASKS=null; renderTasks(userId); return; } // fetch failed
|
|
324
|
-
mapTasks(res);
|
|
325
|
-
renderKpis(USERS.find(u=>u.userId===userId));
|
|
326
|
-
renderTasks(userId);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async function boot(){
|
|
330
|
-
document.getElementById('prog').innerHTML=
|
|
331
|
-
'<span id="chip-users" class="chip"><span class="sp"></span>Team</span>'+
|
|
332
|
-
'<span id="chip-projects" class="chip"><span class="sp"></span>Active projects</span>';
|
|
333
|
-
renderKpis(null);
|
|
334
|
-
|
|
335
|
-
const [uRes,pRes]=await Promise.all([call('list_users'),call('list_active_projects')]);
|
|
336
|
-
USERS=asArray(uRes).filter(u=>u.name&&u.name!=='Placeholder');
|
|
337
|
-
PROJECTS=asArray(pRes);
|
|
338
|
-
setChip('users',USERS.length?'done':'err',USERS.length+' members');
|
|
339
|
-
setChip('projects',PROJECTS.length?'done':'err',PROJECTS.length+' projects');
|
|
340
|
-
|
|
341
|
-
USERS.sort((a,b)=>(b.totalEstimatedHours||0)-(a.totalEstimatedHours||0)||(a.name||'').localeCompare(b.name||''));
|
|
342
|
-
|
|
343
|
-
const uSel=document.getElementById('userSel');
|
|
344
|
-
const savedU=localStorage.getItem('fx_user');
|
|
345
|
-
// First load uses Claude's DEFAULT_USER; a saved pick overrides it thereafter.
|
|
346
|
-
const initialSel=(savedU&&USERS.some(u=>u.userId===savedU))?savedU:resolveDefaultUser();
|
|
347
|
-
fillSelect(uSel,USERS,'userId',u=>`${u.name}${u.initials?' ('+u.initials+')':''} — ${u.totalActiveProjects||0} proj`,initialSel);
|
|
348
|
-
|
|
349
|
-
// Selecting a member fetches just that person's open tasks (server-side filter).
|
|
350
|
-
async function select(userId){ await renderProfile(userId); await loadTasks(userId); }
|
|
351
|
-
|
|
352
|
-
uSel.onchange=async()=>{ localStorage.setItem('fx_user',uSel.value); syncReset(); await select(uSel.value); };
|
|
353
|
-
|
|
354
|
-
// Reset-to-default link: clears the saved pick so DEFAULT_USER wins again.
|
|
355
|
-
const reset=document.getElementById('resetDef');
|
|
356
|
-
function syncReset(){ reset.style.display=(DEFAULT_USER&&localStorage.getItem('fx_user'))?'':'none'; }
|
|
357
|
-
reset.onclick=async(e)=>{ e.preventDefault(); localStorage.removeItem('fx_user');
|
|
358
|
-
const d=resolveDefaultUser(); if(d) uSel.value=d; syncReset(); await select(uSel.value); };
|
|
359
|
-
syncReset();
|
|
360
|
-
|
|
361
|
-
document.getElementById('sub').textContent='Live · '+USERS.length+' team members';
|
|
362
|
-
await select(uSel.value);
|
|
363
|
-
}
|
|
364
|
-
boot().catch(e=>{ document.getElementById('profilePanel').innerHTML=
|
|
365
|
-
'<h3>Profile</h3><div class="center">Failed to load Fenix data.</div>'; });
|
|
366
|
-
</script>
|
|
367
|
-
</body>
|
|
368
|
-
</html>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
place custom instructions here
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<title>Claims Editor</title>
|
|
6
|
-
<style>*{box-sizing:border-box;margin:0;padding:0}body{font-family:system-ui,-apple-system,sans-serif;background:#fff7ed;min-height:100vh;padding:20px 16px}.card{background:#fff;max-width:900px;margin:0 auto;border-radius:10px;box-shadow:0 1px 4px rgba(0,0,0,.12);overflow:hidden}.hdr{display:flex;align-items:center;gap:12px;padding:14px 20px;border-bottom:1px solid #fed7aa}.hdr img{height:26px;width:auto}.hdiv{width:1px;height:22px;background:#fed7aa;flex-shrink:0}.hdr h1{font-size:15px;font-weight:600;color:#1e293b}.hdr .toggle-all{margin-left:auto;flex-shrink:0}.bdy{padding:20px}.sub{font-size:13px;color:#64748b;margin-bottom:14px}input,select,textarea{font-family:inherit;font-size:13px;border:1.5px solid #fed7aa;border-radius:5px;padding:6px 10px;outline:none;width:100%;transition:border-color .15s;background:#fff}input:focus,select:focus,textarea:focus{border-color:#ea580c}.row2{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}.field{margin-bottom:12px}label{display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:4px}.acc{border:1px solid #fed7aa;overflow:hidden;margin-bottom:-1px}.acc-hd{display:flex;align-items:center;gap:10px;padding:11px 14px;cursor:pointer;background:#fffbf5;user-select:none}.acc-hd:hover{background:#fff7ed}.acc-hd .chev{color:#ea580c;font-size:12px;transition:transform .15s;flex-shrink:0}.acc.open .chev{transform:rotate(90deg)}.acc-hd .ttl{font-weight:600;font-size:13px;color:#1e293b;flex-shrink:0}.acc-hd .prev{font-size:12px;color:#94a3b8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.acc-hd .badge{font-size:11px;font-weight:600;padding:1px 8px;border-radius:10px;background:#ffedd5;color:#c2410c;flex-shrink:0}.acc-bd{display:none;padding:14px;border-top:1px solid #fed7aa;background:#fff}.acc.open .acc-bd{display:block}table{width:100%;border-collapse:collapse;margin-top:8px}th,td{padding:6px 10px;border:1px solid #fed7aa;font-size:13px;text-align:left}th{background:#fff7ed;font-weight:600;color:#374151}td input{border:none;padding:0;width:100%;background:transparent}td input:focus{outline:1px solid #ea580c;border-radius:3px}.tbl-bar{display:flex;justify-content:space-between;align-items:center;margin-top:8px}.btn-sm{padding:4px 12px;background:#fff;border:1.5px solid #fed7aa;border-radius:5px;font-size:12px;cursor:pointer;color:#374151}.btn-sm:hover{border-color:#ea580c}.del-btn{color:#dc2626;background:none;border:none;cursor:pointer;font-size:16px;line-height:1;padding:0 4px}.rm-item{margin-left:auto;color:#dc2626;background:none;border:none;cursor:pointer;font-size:13px;flex-shrink:0}.bar{margin-top:16px;display:flex;align-items:center;gap:14px}.cta{padding:9px 24px;background:#ea580c;color:#fff;border:none;border-radius:6px;font-size:14px;font-weight:600;cursor:pointer;transition:background .15s}.cta:hover{background:#c2410c}#st{color:#16a34a;font-size:13px;font-weight:500}textarea.sec{min-height:200px;font-family:Menlo,Consolas,monospace;line-height:1.6;resize:vertical}.addbar{margin:6px 0 4px}.ftr{padding:9px 20px;border-top:1px solid #fed7aa;font-size:11px;color:#94a3b8;display:flex;justify-content:space-between}</style>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div class="card">
|
|
10
|
-
<div class="hdr"><img src="data:image/png;base64,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" alt="Fenix"><div class="hdiv"></div><h1>Claims Editor</h1><button class="btn-sm toggle-all" id="toggleAll" onclick="toggleAll()">Expand all</button></div>
|
|
11
|
-
<div class="bdy">
|
|
12
|
-
<p class="sub">Open any claim to edit it. Add or remove claims as needed. Click Copy for Save and paste into chat.</p>
|
|
13
|
-
<div id="acc"></div>
|
|
14
|
-
<div class="addbar"><button class="btn-sm" onclick="addItem()">+ Add claim</button></div>
|
|
15
|
-
<div class="bar"><button class="cta" onclick="go()">Copy for Save</button><span id="st"></span></div>
|
|
16
|
-
</div>
|
|
17
|
-
<div class="ftr"><span>Fenix Patent Assistant</span><span>v3</span></div>
|
|
18
|
-
</div>
|
|
19
|
-
<script>
|
|
20
|
-
var EDITOR_TYPE="claims";
|
|
21
|
-
var INITIAL_CONTENT="";
|
|
22
|
-
function parseInit(){ var d=INITIAL_CONTENT; if(typeof d==="string"){ var s=d.trim(); if(!s) return null; try{ d=JSON.parse(s); }catch(e){ return s; } } return d; }
|
|
23
|
-
function toItems(){
|
|
24
|
-
var d=parseInit();
|
|
25
|
-
if(EDITOR_TYPE==="claims"){
|
|
26
|
-
if(Array.isArray(d)){ return d.map(function(c){ return {number:String(c.claimNumber||c.number||""),text:Array.isArray(c.claimText)?c.claimText.join("\n"):(c.claimText||c.text||""),orig:c}; }); }
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
if(EDITOR_TYPE==="figures"){ return Array.isArray(d)?d:(d&&d.figures?d.figures:[]); }
|
|
30
|
-
if(EDITOR_TYPE==="sections"){ var arr=[]; if(d&&typeof d==="object"&&!Array.isArray(d)){ Object.keys(d).forEach(function(k){ var v=d[k]; var txt=v&&v.paragraphs?v.paragraphs.map(function(p){return String(p).replace(/<[^>]+>/g,"").trim();}).join("\n\n"):(typeof v==="string"?v:""); arr.push({name:k,text:txt}); }); } return arr; }
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
function elt(tag,cls,txt){ var e=document.createElement(tag); if(cls)e.className=cls; if(txt!=null)e.textContent=txt; return e; }
|
|
34
|
-
function makeAcc(headerTitle,badge,preview,bodyBuilder){
|
|
35
|
-
var wrap=elt("div","acc"); var hd=elt("div","acc-hd");
|
|
36
|
-
hd.appendChild(elt("span","chev","\u25B6")); hd.appendChild(elt("span","ttl",headerTitle));
|
|
37
|
-
if(badge!=null){ hd.appendChild(elt("span","badge",badge)); }
|
|
38
|
-
var prev=elt("span","prev",preview||""); hd.appendChild(prev);
|
|
39
|
-
var rm=elt("button","rm-item","Remove"); rm.onclick=function(ev){ ev.stopPropagation(); wrap.remove(); syncToggle(); }; hd.appendChild(rm);
|
|
40
|
-
hd.onclick=function(){ wrap.classList.toggle("open"); syncToggle(); };
|
|
41
|
-
var bd=elt("div","acc-bd"); bodyBuilder(bd,prev);
|
|
42
|
-
wrap.appendChild(hd); wrap.appendChild(bd); return wrap;
|
|
43
|
-
}
|
|
44
|
-
function claimBody(bd,prev,num,text){
|
|
45
|
-
var f1=elt("div","field"); f1.appendChild(elt("label",null,"Claim Number")); var ni=document.createElement("input"); ni.className="c-num"; ni.value=num||""; f1.appendChild(ni); bd.appendChild(f1);
|
|
46
|
-
var f2=elt("div","field"); f2.appendChild(elt("label",null,"Claim Text")); var ta=document.createElement("textarea"); ta.className="c-txt sec"; ta.value=text||""; ta.oninput=function(){ prev.textContent=(ta.value||"").slice(0,80); }; f2.appendChild(ta); bd.appendChild(f2);
|
|
47
|
-
}
|
|
48
|
-
function addClaim(num,text,orig){ var acc=document.getElementById("acc"); var w=makeAcc("Claim "+(num||"?"),null,(text||"").slice(0,80),function(bd,prev){ claimBody(bd,prev,num,text); }); w.__claim=orig||{}; acc.appendChild(w); return w; }
|
|
49
|
-
function figRow(tbody,num,label,txt){
|
|
50
|
-
var tr=document.createElement("tr");
|
|
51
|
-
var td0=document.createElement("td"); var i0=document.createElement("input"); i0.className="r-num"; i0.style.width="60px"; i0.value=num||""; td0.appendChild(i0); tr.appendChild(td0);
|
|
52
|
-
var td1=document.createElement("td"); var i1=document.createElement("input"); i1.className="r-lab"; i1.value=label||""; td1.appendChild(i1); tr.appendChild(td1);
|
|
53
|
-
var td2=document.createElement("td"); var i2=document.createElement("input"); i2.className="r-txt"; i2.value=txt||""; td2.appendChild(i2); tr.appendChild(td2);
|
|
54
|
-
var td3=document.createElement("td"); var b=document.createElement("button"); b.className="del-btn"; b.textContent="x"; b.onclick=function(){ tr.remove(); }; td3.appendChild(b); tr.appendChild(td3);
|
|
55
|
-
tbody.appendChild(tr);
|
|
56
|
-
}
|
|
57
|
-
function figBody(bd,prev,fig){
|
|
58
|
-
var r=elt("div","row2");
|
|
59
|
-
var c1=elt("div","field"); c1.appendChild(elt("label",null,"Figure Number")); var nn=document.createElement("input"); nn.className="f-num"; nn.value=fig.number||""; c1.appendChild(nn); r.appendChild(c1);
|
|
60
|
-
var c2=elt("div","field"); c2.appendChild(elt("label",null,"Type")); var sel=document.createElement("select"); sel.className="f-type"; ["Flowchart","Device","Placeholder"].forEach(function(o){ var op=document.createElement("option"); op.textContent=o; sel.appendChild(op); }); sel.value=fig.type||"Flowchart"; c2.appendChild(sel); r.appendChild(c2); bd.appendChild(r);
|
|
61
|
-
var fb=elt("div","field"); fb.appendChild(elt("label",null,"Brief Description")); var bi=document.createElement("input"); bi.className="f-brief"; bi.value=fig.briefDescription||""; bi.oninput=function(){ prev.textContent=(bi.value||"").slice(0,70); }; fb.appendChild(bi); bd.appendChild(fb);
|
|
62
|
-
var lbl=elt("label",null,fig.type==="Device"?"Components":"Steps"); lbl.style.marginTop="6px"; bd.appendChild(lbl);
|
|
63
|
-
var tbl=document.createElement("table"); tbl.innerHTML="<thead><tr><th style='width:70px'>#</th><th style='width:170px'>Label</th><th>Description text</th><th style='width:34px'></th></tr></thead>"; var tbody=document.createElement("tbody"); tbl.appendChild(tbody); bd.appendChild(tbl);
|
|
64
|
-
var arr=(fig.type==="Device")?(fig.components||[]):(fig.steps||[]); arr.forEach(function(e){ figRow(tbody,e.number,e.label,e.text); });
|
|
65
|
-
var tb=elt("div","tbl-bar"); var ab=document.createElement("button"); ab.className="btn-sm"; ab.textContent="+ Add row"; ab.onclick=function(){ var rows=tbody.querySelectorAll("tr"); var last=rows.length?parseInt(rows[rows.length-1].querySelector(".r-num").value||"95"):95; figRow(tbody,String(last+5),"",""); }; tb.appendChild(ab); tb.appendChild(elt("span",null,"")); bd.appendChild(tb);
|
|
66
|
-
}
|
|
67
|
-
function addFigure(fig){ fig=fig||{number:"",type:"Flowchart",briefDescription:"",introductory:"",steps:[],components:[]}; var acc=document.getElementById("acc"); var w=makeAcc("FIG. "+(fig.number||"?"),fig.type||"Flowchart",(fig.briefDescription||"").slice(0,70),function(bd,prev){ figBody(bd,prev,fig); }); acc.appendChild(w); return w; }
|
|
68
|
-
function secBody(bd,prev,name,text){
|
|
69
|
-
var f1=elt("div","field"); f1.appendChild(elt("label",null,"Section Name")); var ni=document.createElement("input"); ni.className="s-name"; ni.value=name||""; f1.appendChild(ni); bd.appendChild(f1);
|
|
70
|
-
var f2=elt("div","field"); f2.appendChild(elt("label",null,"Text (blank line = new paragraph)")); var ta=document.createElement("textarea"); ta.className="s-txt sec"; ta.value=text||""; ta.oninput=function(){ prev.textContent=(ta.value||"").replace(/\n/g," ").slice(0,80); }; f2.appendChild(ta); bd.appendChild(f2);
|
|
71
|
-
}
|
|
72
|
-
function addSection(name,text){ var acc=document.getElementById("acc"); var w=makeAcc(name||"Section",null,(text||"").replace(/\n/g," ").slice(0,80),function(bd,prev){ secBody(bd,prev,name,text); }); acc.appendChild(w); return w; }
|
|
73
|
-
function addItem(){ if(EDITOR_TYPE==="claims"){ var rows=document.querySelectorAll("#acc .c-num"); var last=rows.length?parseInt(rows[rows.length-1].value||"0"):0; addClaim(String(last+1),"",{}).classList.add("open"); } else if(EDITOR_TYPE==="figures"){ addFigure(null).classList.add("open"); } else { addSection("New Section","").classList.add("open"); } syncToggle(); }
|
|
74
|
-
function syncToggle(){ var btn=document.getElementById("toggleAll"); if(!btn) return; var accs=document.querySelectorAll("#acc .acc"); if(!accs.length){ btn.style.display="none"; return; } btn.style.display=""; var anyClosed=Array.from(accs).some(function(a){ return !a.classList.contains("open"); }); btn.textContent=anyClosed?"Expand all":"Collapse all"; }
|
|
75
|
-
function toggleAll(){ var accs=document.querySelectorAll("#acc .acc"); var anyClosed=Array.from(accs).some(function(a){ return !a.classList.contains("open"); }); accs.forEach(function(a){ if(anyClosed){ a.classList.add("open"); } else { a.classList.remove("open"); } }); syncToggle(); }
|
|
76
|
-
window.onload=function(){ var items=toItems()||[]; if(EDITOR_TYPE==="claims"){ items.forEach(function(c){ addClaim(c.number,c.text,c.orig); }); } else if(EDITOR_TYPE==="figures"){ items.forEach(function(f){ addFigure(f); }); } else { items.forEach(function(s){ addSection(s.name,s.text); }); } syncToggle(); };
|
|
77
|
-
function collect(){
|
|
78
|
-
var accs=document.querySelectorAll("#acc .acc");
|
|
79
|
-
if(EDITOR_TYPE==="claims"){ var out=[]; accs.forEach(function(a){ var n=a.querySelector(".c-num").value.trim(); var t=a.querySelector(".c-txt").value; var orig=a.__claim||{}; if(n||t.trim()){ out.push({ claimNumber:n, claimStatus:orig.claimStatus||"original", claimType:orig.claimType||"", claimText:t.split(/\n/), parent:orig.parent||"", independentParent:orig.independentParent||"" }); } }); return "[EDITOR SUBMIT - claims]\n"+JSON.stringify(out,null,2); }
|
|
80
|
-
if(EDITOR_TYPE==="figures"){ var figs=[]; accs.forEach(function(a){ var type=a.querySelector(".f-type").value; var isDev=(type==="Device"); var rows=Array.from(a.querySelectorAll("tbody tr")).map(function(tr){ return {type:isDev?"component":"step",number:tr.querySelector(".r-num").value.trim(),label:tr.querySelector(".r-lab").value.trim(),text:tr.querySelector(".r-txt").value.trim(),name:tr.querySelector(".r-lab").value.trim()}; }); figs.push({number:a.querySelector(".f-num").value.trim(),type:type,briefDescription:a.querySelector(".f-brief").value.trim(),steps:isDev?[]:rows,components:isDev?rows:[]}); }); return "[EDITOR SUBMIT - figures]\n"+JSON.stringify(figs,null,2); }
|
|
81
|
-
var secs={}; accs.forEach(function(a){ var nm=a.querySelector(".s-name").value.trim(); var tx=a.querySelector(".s-txt").value; if(nm){ var paras=tx.split(/\n\n+/).map(function(p){return p.trim();}).filter(Boolean).map(function(p){return "<p>"+p.replace(/\n/g," ")+"</p>";}); secs[nm]={paragraphs:paras}; } }); return "[EDITOR SUBMIT - sections]\n"+JSON.stringify(secs,null,2);
|
|
82
|
-
}
|
|
83
|
-
function go(){ var result=collect(); var done=function(){ document.getElementById("st").textContent="\u2713 Copied -- paste into chat."; }; if(navigator.clipboard){ navigator.clipboard.writeText(result).then(done).catch(fb); } else { fb(); } function fb(){ var el=document.createElement("textarea"); el.value=result; el.style.cssText="position:fixed;top:0;left:0;opacity:0;"; document.body.appendChild(el); el.select(); try{ document.execCommand("copy"); done(); }catch(e){ document.getElementById("st").textContent="Copy failed."; } document.body.removeChild(el); } }
|
|
84
|
-
</script>
|
|
85
|
-
</body>
|
|
86
|
-
</html>
|