@citeark/agent 0.3.20
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/LICENSE +202 -0
- package/README.md +128 -0
- package/data/dataset-source-registry.v1.json +300 -0
- package/dist/arkgraph/boot.js +6 -0
- package/dist/arkgraph/index.html +1 -0
- package/dist/arkgraph/viewer.css +1 -0
- package/dist/arkgraph/viewer.en.css +1 -0
- package/dist/arkgraph/viewer.en.js +49 -0
- package/dist/arkgraph/viewer.en.js.LEGAL.txt +56 -0
- package/dist/arkgraph/viewer.js +49 -0
- package/dist/arkgraph/viewer.js.LEGAL.txt +56 -0
- package/docker/claude-code/Dockerfile +97 -0
- package/docker/claude-code/codex-pro-relay.mjs +466 -0
- package/docker/claude-code/runtime-contract-check.mjs +79 -0
- package/docs/arkgraph-reading.md +79 -0
- package/docs/configuration.md +100 -0
- package/docs/integration.md +92 -0
- package/docs/maturity-plan.md +27 -0
- package/docs/npm-release.md +44 -0
- package/docs/paper-reading.md +40 -0
- package/docs/research-plan-granularity.md +27 -0
- package/docs/terminal.md +49 -0
- package/examples/toy-evaluation/compile-task.json +27 -0
- package/examples/toy-evaluation/paper.md +5 -0
- package/examples/toy-evaluation/repository/README.md +9 -0
- package/examples/toy-evaluation/repository/checkpoint.json +4 -0
- package/examples/toy-evaluation/repository/evaluate.py +17 -0
- package/examples/toy-evaluation/task.json +81 -0
- package/package.json +59 -0
- package/prompts/compile-research.md +58 -0
- package/prompts/execute-contract.md +72 -0
- package/prompts/execute-workspace-simple.md +51 -0
- package/prompts/execute-workspace.md +34 -0
- package/prompts/prepare-reproduction.md +82 -0
- package/prompts/repair-research.md +45 -0
- package/protocol/CAP.md +129 -0
- package/protocol/LICENSE +12 -0
- package/protocol/MAPPINGS.md +72 -0
- package/protocol/README.md +38 -0
- package/protocol/conformance-v2.0-alpha.1.json +36 -0
- package/protocol/examples/arkgraph/checkpoint-evaluation.json +309 -0
- package/protocol/examples/arkgraph/fixtures.mjs +49 -0
- package/protocol/examples/arkgraph/paper-free.json +291 -0
- package/protocol/examples/arkgraph/partial-failure.json +344 -0
- package/protocol/examples/arkgraph/training-evaluation.json +443 -0
- package/protocol/profiles/agent-trace.md +16 -0
- package/protocol/profiles/computational-run.md +16 -0
- package/protocol/profiles/core.md +15 -0
- package/protocol/profiles/public-bundle.md +18 -0
- package/protocol/profiles/reproduction.md +29 -0
- package/protocol/profiles/research-compilation.md +44 -0
- package/protocol/profiles/research-plan.md +39 -0
- package/protocol/profiles/restricted-evidence.md +15 -0
- package/runtime/bootstrap-autodl-runtime.sh +314 -0
- package/runtime/create-runtime-venv.sh +41 -0
- package/runtime/install-local-cpu-runtime.sh +23 -0
- package/runtime/install-scientific-runtime.sh +153 -0
- package/runtime/mineru/parse.py +62 -0
- package/runtime/mineru/requirements.txt +4 -0
- package/runtime/requirements-baseline.txt +38 -0
- package/schemas/cap/v2/activity.schema.json +47 -0
- package/schemas/cap/v2/agent.schema.json +32 -0
- package/schemas/cap/v2/assertion.schema.json +110 -0
- package/schemas/cap/v2/descriptor.schema.json +243 -0
- package/schemas/cap/v2/entity.schema.json +64 -0
- package/schemas/cap/v2/manifest.schema.json +67 -0
- package/schemas/cap/v2/relation.schema.json +82 -0
- package/schemas/compute-catalog.schema.json +63 -0
- package/schemas/compute-decision.schema.json +27 -0
- package/schemas/execution-contract.schema.json +1024 -0
- package/schemas/research-card.schema.json +30 -0
- package/schemas/research-inventory-draft.schema.json +366 -0
- package/schemas/research.schema.json +1044 -0
- package/schemas/result.schema.json +173 -0
- package/schemas/verification-policy.schema.json +47 -0
- package/schemas/verified-conclusion.schema.json +58 -0
- package/schemas/workspace-summary.schema.json +24 -0
- package/scripts/build-arkgraph-view.mjs +12 -0
- package/scripts/check-execution-feasibility.mjs +24 -0
- package/scripts/check-syntax.mjs +15 -0
- package/scripts/deterministic-asset-preparation.py +438 -0
- package/scripts/package-cap.mjs +23 -0
- package/scripts/package-local-agent.mjs +23 -0
- package/scripts/preview-arkgraph.mjs +25 -0
- package/scripts/replay-research-compiler-candidate.mjs +134 -0
- package/scripts/review-compiler-sources.mjs +44 -0
- package/scripts/run-asset-preparation.sh +17 -0
- package/scripts/run-research-plan.mjs +98 -0
- package/scripts/validate-asset-preparation.py +290 -0
- package/scripts/verify-local-runtime.mjs +57 -0
- package/scripts/verify-npm-package.mjs +57 -0
- package/src/adapters/paper2agent.mjs +107 -0
- package/src/assets/cache.mjs +159 -0
- package/src/assets/compute.mjs +98 -0
- package/src/assets/executor.mjs +145 -0
- package/src/assets/lifecycle.mjs +213 -0
- package/src/assets/manifest.mjs +242 -0
- package/src/assets/opportunistic-preparation.mjs +81 -0
- package/src/assets/plan.mjs +411 -0
- package/src/assets/prompts.mjs +29 -0
- package/src/assets/public-asset-probe.mjs +525 -0
- package/src/assets/qualification.mjs +119 -0
- package/src/assets/readiness.mjs +130 -0
- package/src/assets/reproduction-admission.mjs +355 -0
- package/src/assets/requirements.mjs +152 -0
- package/src/assets/source-grounding.mjs +341 -0
- package/src/assets/source-policy.mjs +118 -0
- package/src/autodl/client.mjs +260 -0
- package/src/autodl/ssh.mjs +380 -0
- package/src/autodl/tools.mjs +129 -0
- package/src/cap/redaction.mjs +38 -0
- package/src/cap/v2/archive.mjs +152 -0
- package/src/cap/v2/attestation.mjs +204 -0
- package/src/cap/v2/canonical-json.mjs +114 -0
- package/src/cap/v2/compilation-artifact.mjs +240 -0
- package/src/cap/v2/core.mjs +282 -0
- package/src/cap/v2/measurement-assessment-records.mjs +23 -0
- package/src/cap/v2/pipeline-artifact.mjs +922 -0
- package/src/cap/v2/read.mjs +41 -0
- package/src/cap/v2/reassessment-artifact.mjs +383 -0
- package/src/cap/v2/research-artifact.mjs +231 -0
- package/src/cap/v2/research-map-records.mjs +46 -0
- package/src/cap/v2/research-object-records.mjs +163 -0
- package/src/cap/v2/research-records.mjs +187 -0
- package/src/cap/v2/verify.mjs +642 -0
- package/src/cli.mjs +1146 -0
- package/src/compute/autodl-pro-compiler.mjs +347 -0
- package/src/compute/autodl-pro-executor.mjs +459 -0
- package/src/compute/autodl-pro-job.mjs +843 -0
- package/src/compute/autodl-pro-network.mjs +295 -0
- package/src/compute/autodl-pro-remote.mjs +810 -0
- package/src/compute/autodl-pro-staging.mjs +117 -0
- package/src/compute/campaign.mjs +110 -0
- package/src/compute/catalog.mjs +123 -0
- package/src/compute/checkpoint-protocol.mjs +154 -0
- package/src/compute/codex-account-lock.mjs +111 -0
- package/src/compute/codex-account-session.mjs +107 -0
- package/src/compute/compiler-profile.mjs +38 -0
- package/src/compute/compiler-router.mjs +23 -0
- package/src/compute/coordinator-recovery.mjs +210 -0
- package/src/compute/executor-router.mjs +29 -0
- package/src/compute/gcp-batch-compiler.mjs +685 -0
- package/src/compute/gcp-batch-executor.mjs +1215 -0
- package/src/compute/gcp-batch-failure.mjs +92 -0
- package/src/compute/gcp-batch-job.mjs +527 -0
- package/src/compute/gcp-batch-lifecycle.mjs +81 -0
- package/src/compute/gcp-checkpoint-worker.mjs +1633 -0
- package/src/compute/local-codex-compiler.mjs +52 -0
- package/src/compute/measurement-hardware.mjs +128 -0
- package/src/compute/remote-attempt.mjs +226 -0
- package/src/compute/requirements.mjs +124 -0
- package/src/compute/research-phases.mjs +48 -0
- package/src/compute/scheduler.mjs +452 -0
- package/src/compute/shared-workloads.mjs +26 -0
- package/src/compute/stage-archive.mjs +79 -0
- package/src/contracts/campaign-contract.mjs +52 -0
- package/src/contracts/execution-contract.mjs +819 -0
- package/src/contracts/execution-mode.mjs +19 -0
- package/src/contracts/execution-timeouts.mjs +45 -0
- package/src/contracts/execution-workload.mjs +68 -0
- package/src/contracts/preflight-schema.mjs +25 -0
- package/src/contracts/public-contract.mjs +63 -0
- package/src/contracts/subject-tags.mjs +31 -0
- package/src/dashboard/data.mjs +898 -0
- package/src/dashboard/server.mjs +79 -0
- package/src/dashboard/static/dashboard.css +366 -0
- package/src/dashboard/static/dashboard.js +560 -0
- package/src/dashboard/static/index.html +85 -0
- package/src/deployment/community-policy.mjs +9 -0
- package/src/deployment/environment.mjs +112 -0
- package/src/deployment/guided.mjs +98 -0
- package/src/deployment/handoff.mjs +102 -0
- package/src/deployment/local-contract.mjs +31 -0
- package/src/deployment/local.mjs +100 -0
- package/src/deployment/prepare.mjs +46 -0
- package/src/deployment/recipe.mjs +108 -0
- package/src/deployment/supplement.mjs +51 -0
- package/src/deployment/terminal.mjs +43 -0
- package/src/diagnosis/renderer.mjs +75 -0
- package/src/diagnosis/target-failure.mjs +46 -0
- package/src/evidence/parser-registry.mjs +54 -0
- package/src/evidence/parsers/fasttext-classification.mjs +82 -0
- package/src/evidence/parsers/json-scalar.mjs +96 -0
- package/src/evidence/parsers/simcse-senteval.mjs +104 -0
- package/src/evidence/parsers/starspace-classification.mjs +78 -0
- package/src/evidence/registry.mjs +147 -0
- package/src/execution/runner-audit.mjs +473 -0
- package/src/gcp/auth.mjs +106 -0
- package/src/gcp/batch-client.mjs +120 -0
- package/src/gcp/resource-discovery.mjs +177 -0
- package/src/gcp/rest.mjs +82 -0
- package/src/gcp/secret-manager.mjs +34 -0
- package/src/gcp/signed-url.mjs +133 -0
- package/src/gcp/storage.mjs +220 -0
- package/src/graph/command.mjs +41 -0
- package/src/graph/execution.mjs +97 -0
- package/src/graph/model.mjs +37 -0
- package/src/graph/presentation.mjs +110 -0
- package/src/graph/query.mjs +159 -0
- package/src/graph/research-relations.mjs +69 -0
- package/src/graph/source-page.mjs +12 -0
- package/src/graph/source-preview.mjs +34 -0
- package/src/graph/validate.mjs +76 -0
- package/src/job.mjs +496 -0
- package/src/network/autodl-routing-proxy.mjs +462 -0
- package/src/network/egress-proxy.mjs +158 -0
- package/src/observability/event-contract.mjs +230 -0
- package/src/observability/pipeline-monitor.mjs +166 -0
- package/src/pipeline/orchestrator.mjs +1281 -0
- package/src/pipeline/recovery-error.mjs +11 -0
- package/src/pipeline/replay.mjs +304 -0
- package/src/pipeline/shared-execution.mjs +115 -0
- package/src/pipeline/stage-checkpoint.mjs +86 -0
- package/src/pipeline/stage-recovery.mjs +101 -0
- package/src/pipeline/targets.mjs +110 -0
- package/src/process.mjs +143 -0
- package/src/protocol.mjs +312 -0
- package/src/provider/codex-account.mjs +44 -0
- package/src/provider/codex-completion.mjs +49 -0
- package/src/provider/completion.mjs +292 -0
- package/src/provider/model-client.mjs +44 -0
- package/src/provider/model-route.mjs +29 -0
- package/src/provider/openrouter-readiness.mjs +189 -0
- package/src/provider/reader-bridge.mjs +35 -0
- package/src/provider/relay.mjs +263 -0
- package/src/provider/runtime-auth.mjs +40 -0
- package/src/public/cap.d.mts +90 -0
- package/src/public/cap.mjs +12 -0
- package/src/public/contracts.d.mts +2 -0
- package/src/public/host.mjs +171 -0
- package/src/public/operations.d.mts +11 -0
- package/src/public/presentation.d.mts +4 -0
- package/src/records/views.mjs +26 -0
- package/src/remote/command.mjs +178 -0
- package/src/remote/ssh.mjs +59 -0
- package/src/repository-origin.mjs +81 -0
- package/src/reproduction/evidence-feedback.mjs +96 -0
- package/src/reproduction/incomplete-initialization.mjs +25 -0
- package/src/reproduction/lifecycle.mjs +253 -0
- package/src/reproduction/plan.mjs +132 -0
- package/src/reproduction/prompts.mjs +70 -0
- package/src/reproduction/runner.mjs +188 -0
- package/src/reproduction/summary.mjs +130 -0
- package/src/reproduction/workspace-mode.mjs +7 -0
- package/src/research/automatic-admission.mjs +156 -0
- package/src/research/compiler-coverage.mjs +85 -0
- package/src/research/compiler-failure.mjs +24 -0
- package/src/research/compiler-normalization-guards.mjs +112 -0
- package/src/research/compiler-repair.mjs +3 -0
- package/src/research/compiler.mjs +853 -0
- package/src/research/continuation-selection.mjs +26 -0
- package/src/research/execution-graph-context.mjs +43 -0
- package/src/research/experiment-importance.mjs +15 -0
- package/src/research/inventory-handoff.mjs +104 -0
- package/src/research/inventory-revisions.mjs +32 -0
- package/src/research/mineru-local.mjs +73 -0
- package/src/research/paper-command.mjs +19 -0
- package/src/research/paper-markdown.mjs +180 -0
- package/src/research/paper-source-map.mjs +69 -0
- package/src/research/planning-policy.mjs +88 -0
- package/src/research/reference-materials.mjs +11 -0
- package/src/research/reproduction-scope.mjs +30 -0
- package/src/research/research-map.mjs +94 -0
- package/src/research/research-objects.mjs +88 -0
- package/src/research/source-discovery.mjs +646 -0
- package/src/research/source-observations.mjs +75 -0
- package/src/research/source-review-cli-mcp.mjs +26 -0
- package/src/research/source-review-input.mjs +209 -0
- package/src/research/source-review-local-codex.mjs +36 -0
- package/src/research/source-review-model.mjs +70 -0
- package/src/research/source-review.mjs +173 -0
- package/src/research/structure.mjs +3163 -0
- package/src/research-card/renderer.mjs +277 -0
- package/src/research-card/verified-conclusion.mjs +143 -0
- package/src/results/output-registry.mjs +183 -0
- package/src/runtime/claude-code.mjs +52 -0
- package/src/runtime/codex-capacity-retry.mjs +87 -0
- package/src/runtime/codex.mjs +64 -0
- package/src/runtime/config.mjs +157 -0
- package/src/runtime/final-output.mjs +40 -0
- package/src/runtime/index.mjs +21 -0
- package/src/runtime/local-codex.mjs +74 -0
- package/src/runtime/opencode.mjs +95 -0
- package/src/runtime/prompt.mjs +13 -0
- package/src/sandbox/docker.mjs +363 -0
- package/src/settings/command.mjs +297 -0
- package/src/settings/store.mjs +119 -0
- package/src/telemetry/pricing.mjs +68 -0
- package/src/telemetry/usage.mjs +265 -0
- package/src/terminal/events.mjs +97 -0
- package/src/terminal/input.mjs +40 -0
- package/src/terminal/plain.mjs +40 -0
- package/src/terminal/remote-stream.mjs +22 -0
- package/src/terminal/screen.mjs +214 -0
- package/src/terminal/transcript.mjs +69 -0
- package/src/util.mjs +107 -0
- package/src/verification/ai-assessor.mjs +534 -0
- package/src/verification/claim-evaluator.mjs +242 -0
- package/src/verification/evidence-context.mjs +165 -0
- package/src/verification/evidence-reader.mjs +95 -0
- package/src/verification/integrity.mjs +570 -0
- package/src/verification/tolerance.mjs +32 -0
- package/src/workloads/cpu-research-preparation.mjs +56 -0
- package/src/workloads/definition.mjs +74 -0
- package/src/workloads/phase-aware-reproduction.mjs +46 -0
- package/src/workloads/reproduction.mjs +85 -0
- package/src/workspace/command.mjs +242 -0
- package/src/workspace/control.mjs +49 -0
- package/src/workspace/entry.mjs +28 -0
- package/src/workspace/input.mjs +93 -0
- package/src/workspace/interactive.mjs +94 -0
- package/src/workspace/jobs.mjs +418 -0
- package/src/workspace/session.mjs +97 -0
- package/src/workspace/worker.mjs +137 -0
- package/ui/arkgraph/ambient-motion.mjs +10 -0
- package/ui/arkgraph/app.jsx +153 -0
- package/ui/arkgraph/boot.js +6 -0
- package/ui/arkgraph/camera-motion.mjs +20 -0
- package/ui/arkgraph/context-reveal.mjs +39 -0
- package/ui/arkgraph/details.css +3 -0
- package/ui/arkgraph/entry.jsx +28 -0
- package/ui/arkgraph/experiment-curves.mjs +17 -0
- package/ui/arkgraph/experiment-selection.mjs +15 -0
- package/ui/arkgraph/experiment-style.css +26 -0
- package/ui/arkgraph/experiment-ui.jsx +32 -0
- package/ui/arkgraph/frame.html +1 -0
- package/ui/arkgraph/graph-gestures.mjs +62 -0
- package/ui/arkgraph/label-layout.mjs +57 -0
- package/ui/arkgraph/locales/en.json +229 -0
- package/ui/arkgraph/locales/source-types.json +15 -0
- package/ui/arkgraph/localization-build.mjs +27 -0
- package/ui/arkgraph/material-build.mjs +23 -0
- package/ui/arkgraph/material-colors.mjs +39 -0
- package/ui/arkgraph/material-style.css +15 -0
- package/ui/arkgraph/open-graph.jsx +326 -0
- package/ui/arkgraph/outline.jsx +49 -0
- package/ui/arkgraph/package-lock.json +888 -0
- package/ui/arkgraph/package.json +17 -0
- package/ui/arkgraph/reading-layout.mjs +130 -0
- package/ui/arkgraph/reading-presentation.mjs +73 -0
- package/ui/arkgraph/record-detail.css +51 -0
- package/ui/arkgraph/record-details.jsx +29 -0
- package/ui/arkgraph/research-types.mjs +31 -0
- package/ui/arkgraph/selection-mark.jsx +6 -0
- package/ui/arkgraph/soft-spine.mjs +26 -0
- package/ui/arkgraph/steering-style.css +187 -0
- package/ui/arkgraph/style.css +272 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@citeark/arkgraph-view",
|
|
3
|
+
"version": "0.3.20",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"d3-force": "3.0.0",
|
|
9
|
+
"force-graph": "1.52.0",
|
|
10
|
+
"react": "19.2.8",
|
|
11
|
+
"react-dom": "19.2.8",
|
|
12
|
+
"esbuild": "0.25.12"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "5.9.3"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Reading geometry uses design units throughout. It never reads the camera or viewport.
|
|
2
|
+
const intersects=(a,b,pad=0)=>a.x<b.x+b.w+pad&&a.x+a.w+pad>b.x&&a.y<b.y+b.h+pad&&a.y+a.h+pad>b.y;
|
|
3
|
+
const union=(a,b)=>({x:Math.min(a.x,b.x),y:Math.min(a.y,b.y),w:Math.max(a.x+a.w,b.x+b.w)-Math.min(a.x,b.x),h:Math.max(a.y+a.h,b.y+b.h)-Math.min(a.y,b.y)});
|
|
4
|
+
const shifted=(b,x,y)=>({...b,x:b.x+x,y:b.y+y});
|
|
5
|
+
const lineThroughBox=(end,box)=>{
|
|
6
|
+
let near=0,far=1;
|
|
7
|
+
for(const axis of ['x','y']){
|
|
8
|
+
const delta=end[axis],lo=box[axis],hi=lo+box[axis==='x'?'w':'h'];
|
|
9
|
+
if(Math.abs(delta)<1e-8){if(0<lo||0>hi)return false;continue;}
|
|
10
|
+
const a=lo/delta,b=hi/delta;near=Math.max(near,Math.min(a,b));far=Math.min(far,Math.max(a,b));if(near>far)return false;
|
|
11
|
+
}
|
|
12
|
+
return far>.02&&near<.98;
|
|
13
|
+
};
|
|
14
|
+
const opening=/[(《「『【(\[]$/u,closing=/^[,。!?;:、)》」』】,.!?;:)\]]/u;
|
|
15
|
+
const segmenter=new Intl.Segmenter('zh',{granularity:'word'});
|
|
16
|
+
|
|
17
|
+
// Balance complete words across lines, including Chinese words and model names.
|
|
18
|
+
// A short last line or a leading closing mark is more expensive than a slightly wider title.
|
|
19
|
+
export function wrapGraphLabel(text,measure,maxWidth){
|
|
20
|
+
const source=String(text??'');if(measure(source)<=maxWidth)return[source];
|
|
21
|
+
const tokens=[...segmenter.segment(source)].flatMap(({segment})=>measure(segment)>maxWidth?[...segment]:[segment]);
|
|
22
|
+
const widths=new Map(),line=(a,b)=>tokens.slice(a,b).join('').trim();
|
|
23
|
+
const width=(a,b)=>{const key=a+':'+b;if(!widths.has(key))widths.set(key,measure(line(a,b)));return widths.get(key);};
|
|
24
|
+
const minLines=Math.max(2,Math.ceil(measure(source)/maxWidth));
|
|
25
|
+
for(let count=minLines;count<=tokens.length;count++){
|
|
26
|
+
const ideal=measure(source)/count,memo=new Map();
|
|
27
|
+
const solve=(start,left)=>{
|
|
28
|
+
if(!left)return start===tokens.length?{cost:0,lines:[]}:null;
|
|
29
|
+
const key=start+':'+left;if(memo.has(key))return memo.get(key);
|
|
30
|
+
let best=null;
|
|
31
|
+
for(let end=start+1;end<=tokens.length-left+1;end++){
|
|
32
|
+
const value=line(start,end),w=width(start,end);if(w>maxWidth)break;if(!value)continue;
|
|
33
|
+
const rest=solve(end,left-1);if(!rest)continue;
|
|
34
|
+
const chars=[...value].length;
|
|
35
|
+
const cost=rest.cost+(w-ideal)**2+(chars<3?maxWidth**2:0)+(closing.test(value)||opening.test(value)?maxWidth**2*4:0);
|
|
36
|
+
if(!best||cost<best.cost)best={cost,lines:[value,...rest.lines]};
|
|
37
|
+
}
|
|
38
|
+
memo.set(key,best);return best;
|
|
39
|
+
};
|
|
40
|
+
const result=solve(0,count);if(result)return result.lines;
|
|
41
|
+
}
|
|
42
|
+
return[source];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function attachment(node,x,y,mainSide){
|
|
46
|
+
const {w,h,r}=node;
|
|
47
|
+
if(node.main)return {dx:-w/2,dy:mainSide==='above'?-r-h-13:r+13,side:mainSide};
|
|
48
|
+
// Keep labels beside their dots. The dot, label and small breathing margin move together.
|
|
49
|
+
if(Math.abs(x)>.58*Math.abs(y))return {dx:x<0?-r-9-w:r+9,dy:-h/2,side:x<0?'left':'right'};
|
|
50
|
+
return {dx:-w/2,dy:y<0?-r-9-h:r+9,side:y<0?'above':'below'};
|
|
51
|
+
}
|
|
52
|
+
function body(node,label,x=0,y=0){
|
|
53
|
+
return shifted(union({x:-node.r-4,y:-node.r-4,w:2*node.r+8,h:2*node.r+8},{x:label.dx-4,y:label.dy-4,w:node.w+8,h:node.h+8}),x,y);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Pack organic neighborhoods first, then allocate horizontal capacity from actual
|
|
57
|
+
// text area. D3 can interleave their empty corners using the labeled-object force.
|
|
58
|
+
export function planReadingLayout(items,spine,parents){
|
|
59
|
+
const byId=new Map(items.map(n=>[n.id,n])),placements=new Map(),neighborhoods=[];
|
|
60
|
+
const rootOf=id=>{const seen=new Set();while(!spine.includes(id)&&parents.has(id)&&!seen.has(id)){seen.add(id);id=parents.get(id);}return spine.includes(id)?id:spine[0];};
|
|
61
|
+
for(const [index,id] of spine.entries()){
|
|
62
|
+
const root=byId.get(id);if(!root)continue;
|
|
63
|
+
const members=items.filter(n=>!n.main&&rootOf(n.id)===id);
|
|
64
|
+
const mainSide=index%2?'below':'above',label=attachment(root,0,0,mainSide);
|
|
65
|
+
const local=[{id,x:0,y:0,owner:id,label,body:body(root,label)}];
|
|
66
|
+
const targetWidth=Math.max(root.w+18,Math.sqrt(members.reduce((a,n)=>a+(n.w+28)*(n.h+28),0)*1.15));
|
|
67
|
+
for(const [order,n] of members.entries()){
|
|
68
|
+
let best=null;
|
|
69
|
+
for(let ring=0;ring<36||!best;ring++){
|
|
70
|
+
const radius=50+ring*13;
|
|
71
|
+
for(let j=0;j<32;j++)for(const mirror of [-1,1]){
|
|
72
|
+
const angle=-Math.PI/2+j*Math.PI/16+(order%2?.07:-.07),x=mirror*Math.cos(angle)*radius,y=Math.sin(angle)*radius;
|
|
73
|
+
const nextLabel=attachment(n,x,y),box=body(n,nextLabel,x,y);
|
|
74
|
+
if(local.some(p=>intersects(box,p.body,13)))continue;
|
|
75
|
+
const spread=Math.max(0,Math.abs(box.x+box.w/2)+box.w/2-targetWidth/2);
|
|
76
|
+
const balance=local.reduce((sum,p)=>sum+p.x,0)+x;
|
|
77
|
+
const rootText={x:label.dx-5,y:label.dy-5,w:root.w+10,h:root.h+10};
|
|
78
|
+
const crossings=Number(lineThroughBox({x,y},rootText))+local.slice(1).reduce((sum,p)=>sum+Number(lineThroughBox({x,y},p.body))+Number(lineThroughBox(p,box)),0);
|
|
79
|
+
const cost=radius+spread*1.6+crossings*240+Math.abs(balance)*.25+(Math.sign(y)===(order%2?1:-1)?0:8);
|
|
80
|
+
if(!best||cost<best.cost)best={id:n.id,x,y,owner:id,label:nextLabel,body:box,cost};
|
|
81
|
+
}
|
|
82
|
+
if(best&&radius>best.cost+20)break;
|
|
83
|
+
}
|
|
84
|
+
if(!best)throw Error('Unable to arrange reading neighborhood: '+n.id);
|
|
85
|
+
local.push(best);
|
|
86
|
+
}
|
|
87
|
+
const previous=neighborhoods.at(-1);
|
|
88
|
+
const x=previous?previous.x+Math.max((root.w+byId.get(previous.id).w)/2+24,(targetWidth+previous.capacity)*.42):0;
|
|
89
|
+
neighborhoods.push({id,x,capacity:targetWidth,members:local});
|
|
90
|
+
}
|
|
91
|
+
const center=(neighborhoods[0]?.x+neighborhoods.at(-1)?.x)/2||0;
|
|
92
|
+
for(const group of neighborhoods)for(const n of group.members)placements.set(n.id,{x:group.x-center+n.x,y:n.y,dx:n.x,dy:n.y,owner:n.owner,label:n.label});
|
|
93
|
+
return placements;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// D3 forces act on the whole labeled object. They are soft springs: dragging can
|
|
97
|
+
// move an entire neighborhood or an individual member and retarget its resting place.
|
|
98
|
+
export function readingNeighborhoodForce(unit=1){
|
|
99
|
+
let nodes=[],byId=new Map();
|
|
100
|
+
function force(alpha){
|
|
101
|
+
for(const n of nodes){
|
|
102
|
+
const parent=byId.get(n.layoutOwner),x=n.main?n.layoutX:(parent?.x??n.layoutX)+n.layoutDX*unit,y=n.main?n.layoutY:(parent?.y??n.layoutY)+n.layoutDY*unit;
|
|
103
|
+
if(n.fx==null)n.vx+=(x-n.x)*alpha*(n.main?.045:.04);
|
|
104
|
+
if(n.fy==null)n.vy+=(y-n.y)*alpha*(n.main?.14:.045);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
force.initialize=values=>{nodes=values;byId=new Map(nodes.map(n=>[n.id,n]));};
|
|
108
|
+
force.retarget=n=>{const parent=byId.get(n.layoutOwner);if(n.main){n.layoutX=n.x;n.layoutY=n.y;}else if(parent){n.layoutDX=(n.x-parent.x)/unit;n.layoutDY=(n.y-parent.y)/unit;}};
|
|
109
|
+
return force;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function labeledObjectCollision(unit=1){
|
|
113
|
+
let nodes=[];
|
|
114
|
+
function force(){
|
|
115
|
+
for(let i=0;i<nodes.length;i++)for(let j=i+1;j<nodes.length;j++){
|
|
116
|
+
const a=nodes[i],b=nodes[j];if(!a.layoutBody||!b.layoutBody)continue;
|
|
117
|
+
const box=n=>({x:n.x+n.vx+n.layoutBody.x*unit,y:n.y+n.vy+n.layoutBody.y*unit,w:n.layoutBody.w*unit,h:n.layoutBody.h*unit});
|
|
118
|
+
const aa=box(a),bb=box(b),pad=(a.layoutOwner===b.layoutOwner?8:20)*unit;
|
|
119
|
+
if(!intersects(aa,bb,pad))continue;
|
|
120
|
+
const dx=Math.min(aa.x+aa.w+pad-bb.x,bb.x+bb.w+pad-aa.x),dy=Math.min(aa.y+aa.h+pad-bb.y,bb.y+bb.h+pad-aa.y);
|
|
121
|
+
const axis=dx<dy?'x':'y',amount=Math.min((axis==='x'?dx:dy)*.48,14*unit),sign=aa[axis]+aa[axis==='x'?'w':'h']/2<bb[axis]+bb[axis==='x'?'w':'h']/2?-1:1;
|
|
122
|
+
const fixed=axis==='x'?'fx':'fy',velocity=axis==='x'?'vx':'vy';
|
|
123
|
+
const ma=a[fixed]!=null?0:a.main?.12:1,mb=b[fixed]!=null?0:b.main?.12:1,total=ma+mb;
|
|
124
|
+
if(total){a[velocity]+=sign*amount*ma/total;b[velocity]-=sign*amount*mb/total;}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
force.initialize=values=>{nodes=values;};return force;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const readingObjectBody=(item,label)=>body(item,label);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {MD,coreTypeOf,colorOf} from './research-types.mjs';
|
|
2
|
+
|
|
3
|
+
// These five reader-facing groups preserve all twelve source categories.
|
|
4
|
+
export const contentGroups=Object.freeze([
|
|
5
|
+
{id:'context',label:'问题与概念',types:['question','concept'],color:MD.blue700},
|
|
6
|
+
{id:'argument',label:'观点与解释',types:['claim','hypothesis','argument'],color:MD.indigo500},
|
|
7
|
+
{id:'process',label:'方法与过程',types:['method','plan','activity'],color:MD.deepOrange700},
|
|
8
|
+
{id:'evidence',label:'结果与评价',types:['result','assessment'],color:MD.teal600},
|
|
9
|
+
{id:'resource',label:'材料与来源',types:['resource','agent'],color:MD.grey600}
|
|
10
|
+
]);
|
|
11
|
+
export const contentGroupOf=n=>contentGroups.find(g=>g.types.includes(coreTypeOf(n)));
|
|
12
|
+
|
|
13
|
+
const spectrum=[MD.red700,MD.deepOrange700,MD.amber700,MD.lightGreen700,MD.green600,MD.teal600,MD.blue700,MD.indigo500,MD.purple500];
|
|
14
|
+
export function mainlineColor(index,total){
|
|
15
|
+
const position=total<=1?0:Math.max(0,Math.min(total-1,index))/(total-1)*(spectrum.length-1);
|
|
16
|
+
const a=spectrum[Math.floor(position)],b=spectrum[Math.ceil(position)],t=position%1;
|
|
17
|
+
return '#'+[1,3,5].map(i=>Math.round(parseInt(a.slice(i,i+2),16)*(1-t)+parseInt(b.slice(i,i+2),16)*t).toString(16).padStart(2,'0')).join('').toUpperCase();
|
|
18
|
+
}
|
|
19
|
+
export function numberColor(background){
|
|
20
|
+
const channels=[1,3,5].map(i=>parseInt(background.slice(i,i+2),16)/255).map(v=>v<=.04045?v/12.92:((v+.055)/1.055)**2.4);
|
|
21
|
+
const light=channels[0]*.2126+channels[1]*.7152+channels[2]*.0722;
|
|
22
|
+
return light>.179?MD.black:MD.white;
|
|
23
|
+
}
|
|
24
|
+
export function createReadingColors(spine){
|
|
25
|
+
const colors=new Map(spine.map((id,index)=>[id,mainlineColor(index,spine.length)]));
|
|
26
|
+
return n=>colors.get(n.key)||colorOf(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const compare=new Intl.Collator('zh-CN',{numeric:true}).compare;
|
|
30
|
+
const sorted=nodes=>[...nodes].sort((a,b)=>compare(a.label,b.label)||compare(a.key,b.key));
|
|
31
|
+
export function buildOutlineReading(D){
|
|
32
|
+
const byKey=new Map(D.nodes.map(n=>[n.key,n])),spine=D.presentation.spine,children=new Map();
|
|
33
|
+
for(const group of D.presentation.groups){
|
|
34
|
+
if(!children.has(group.parent))children.set(group.parent,[]);
|
|
35
|
+
children.get(group.parent).push(...group.members);
|
|
36
|
+
}
|
|
37
|
+
const placed=new Set(spine);
|
|
38
|
+
const branch=id=>(children.get(id)||[]).flatMap(key=>{
|
|
39
|
+
if(placed.has(key)||!byKey.has(key))return [];
|
|
40
|
+
placed.add(key);return [{id:key,children:branch(key)}];
|
|
41
|
+
});
|
|
42
|
+
const sections=spine.map((id,index)=>({id,index,children:branch(id)}));
|
|
43
|
+
const buckets=new Map();
|
|
44
|
+
for(const n of D.nodes){
|
|
45
|
+
if(placed.has(n.key))continue;
|
|
46
|
+
const type=coreTypeOf(n);
|
|
47
|
+
const id=n.prospective||type==='plan'?'plans':n.role==='source-work'||type==='agent'?'sources':type==='resource'?'materials':['result','assessment'].includes(type)?'evidence':['method','activity'].includes(type)?'methods':'ideas';
|
|
48
|
+
if(!buckets.has(id))buckets.set(id,[]);buckets.get(id).push(n);
|
|
49
|
+
}
|
|
50
|
+
const definitions=[
|
|
51
|
+
['evidence','原文图表与评述','论文报告的图表、观测和补充评价'],
|
|
52
|
+
['ideas','补充概念与解释','主线以外的概念、前提、观点和解释'],
|
|
53
|
+
['methods','方法与活动细节','论文使用的方法及报告的研究活动'],
|
|
54
|
+
['materials','模型、数据与材料','研究使用的模型、数据、提示、材料和工具'],
|
|
55
|
+
['plans','后续研究与实验计划','后续研究意向、待执行方案及预期材料与产出'],
|
|
56
|
+
['sources','论文来源与参与者','来源论文以及内容提供者、作者或整理者']
|
|
57
|
+
];
|
|
58
|
+
const subgroup=(nodes,definitions)=>definitions.map(([id,label,accept])=>({id,label,nodes:sorted(nodes.filter(accept))})).filter(g=>g.nodes.length);
|
|
59
|
+
const appendices=definitions.flatMap(([id,label,note])=>{
|
|
60
|
+
const nodes=sorted(buckets.get(id)||[]);if(!nodes.length)return [];
|
|
61
|
+
const parts=id==='materials'?subgroup(nodes,[
|
|
62
|
+
['models','模型与检查点',n=>['model','checkpoint'].includes(n.role)],
|
|
63
|
+
['data','数据与提示',n=>['dataset','sample'].includes(n.role)],
|
|
64
|
+
['tools','材料与工具',n=>!['model','checkpoint','dataset','sample'].includes(n.role)]
|
|
65
|
+
]):id==='plans'?subgroup(nodes,[
|
|
66
|
+
['plans','研究方案',n=>coreTypeOf(n)==='plan'&&n.role!=='procedure-step'],
|
|
67
|
+
['steps','计划步骤',n=>n.role==='procedure-step'],
|
|
68
|
+
['outputs','预期材料与产出',n=>coreTypeOf(n)!=='plan'&&n.role!=='procedure-step']
|
|
69
|
+
]):[];
|
|
70
|
+
return [{id,label,note,nodes,parts}];
|
|
71
|
+
});
|
|
72
|
+
return {sections,appendices};
|
|
73
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* Content navigation is a text row, never a browser-styled button shell. */
|
|
2
|
+
.reference-app .inspector .detail-row{appearance:none;-webkit-appearance:none;background:transparent;border:0;border-radius:0;box-shadow:none;padding:12px 0;color:#616161;transition:color 150ms ease}
|
|
3
|
+
.reference-app .inspector .detail-row b{color:#424242;font-weight:500}
|
|
4
|
+
.reference-app .inspector .detail-row small{color:#757575}
|
|
5
|
+
.reference-app .inspector .detail-row>span:last-child:not(:first-child){color:#9E9E9E;transition:color 150ms ease,transform 180ms ease}
|
|
6
|
+
.reference-app .inspector .detail-row:hover b,.reference-app .inspector .detail-row:hover>span:last-child:not(:first-child){color:#1976D2}
|
|
7
|
+
.reference-app .inspector .detail-row:hover>span:last-child:not(:first-child){transform:translate(2px,-2px)}
|
|
8
|
+
.reference-app .inspector .detail-row+.detail-row{border-top:1px solid #F5F5F5}
|
|
9
|
+
.reference-app .inspector .figure-button{display:block;appearance:none;-webkit-appearance:none;width:100%;padding:0;border:0;border-radius:0;background:transparent;box-shadow:none;cursor:zoom-in}
|
|
10
|
+
.reference-app .inspector .figure-button img{border:0;border-radius:0}
|
|
11
|
+
.reference-app .inspector .figure-button:hover{border:0;background:transparent;box-shadow:none}
|
|
12
|
+
.reference-app .inspector .figure-button:focus-visible{outline:2px solid #1976D2;outline-offset:4px}
|
|
13
|
+
.reference-app .inspector figure{margin:18px 0 24px}
|
|
14
|
+
.reference-app .inspector figcaption{color:#757575;margin-top:10px}
|
|
15
|
+
.reference-app .inspector .related-method{margin:0 0 24px}
|
|
16
|
+
.reference-app .inspector .related-method .detail-row{padding:2px 0 12px}
|
|
17
|
+
.reference-app .inspector .related-method figure{margin:0}
|
|
18
|
+
.reference-app .inspector .secondary,.reference-app .inspector .text-button{appearance:none;-webkit-appearance:none;border:0;box-shadow:none;background:transparent;color:#1976D2;border-radius:0;padding:6px 0;width:auto;text-align:left}
|
|
19
|
+
.reference-app .inspector .secondary:hover,.reference-app .inspector .text-button:hover{background:transparent;color:#1565C0}
|
|
20
|
+
@media(prefers-reduced-motion:reduce){.reference-app .inspector .detail-row,.reference-app .inspector .detail-row>span:last-child{transition:none}}
|
|
21
|
+
|
|
22
|
+
/* A single reading hierarchy: summary, visual evidence, qualifications, related objects. */
|
|
23
|
+
.record-content{display:flex;flex-direction:column;gap:26px}
|
|
24
|
+
.record-intro h2{margin-bottom:10px!important;font-size:21px!important;line-height:1.5!important;letter-spacing:-.3px}
|
|
25
|
+
.record-intro .lead{margin:0 0 12px!important;color:#616161;font-size:13px;line-height:1.85}
|
|
26
|
+
.record-source{display:inline-flex;align-items:center;gap:8px;color:#9E9E9E;font-size:10px;line-height:1.6}
|
|
27
|
+
.record-source:hover{color:#1976D2}
|
|
28
|
+
.record-content .record-section,.record-content .record-own-figure,.record-content .record-measurements{margin:0}
|
|
29
|
+
.record-content h3{margin:0 0 12px;color:#616161;font-size:11px;font-weight:550}
|
|
30
|
+
.record-content h3 small{color:#9E9E9E}
|
|
31
|
+
.record-content .record-own-figure figure{margin:0!important}
|
|
32
|
+
.record-content .figure-tabs{display:flex;gap:7px;flex-wrap:wrap;margin-bottom:14px}
|
|
33
|
+
.record-content .figure-tabs button{appearance:none;border:0;background:transparent;color:#9E9E9E;font-size:11px;padding:5px 9px;border-radius:6px;transition:background 150ms,color 150ms}
|
|
34
|
+
.record-content .figure-tabs button[aria-selected=true]{background:#F5F5F5;color:#424242}
|
|
35
|
+
.record-content .figure-view figure{margin:0!important}
|
|
36
|
+
.record-content .figure-details-link{display:flex;gap:8px;align-items:center;margin-top:12px;padding:0;border:0;background:none;color:#1976D2;font-size:10px}
|
|
37
|
+
.record-content .record-link{padding:10px 0!important;line-height:1.55}
|
|
38
|
+
.record-content .record-link small{font-size:10px;color:#9E9E9E;margin:3px 0 0}
|
|
39
|
+
.record-content .record-limits p{font-size:12px;color:#757575;line-height:1.85;margin:0 0 8px}
|
|
40
|
+
.record-content .record-limits p:last-child{margin:0}
|
|
41
|
+
.record-content .record-measurements section{margin:0}
|
|
42
|
+
.record-content .record-measurements td{font-size:11px;padding:10px 7px;color:#757575}
|
|
43
|
+
.record-content .record-measurements th{font-size:10px;color:#9E9E9E}
|
|
44
|
+
.record-content .record-measurements td b{color:#424242;font-weight:550}
|
|
45
|
+
.record-content details{margin:0;padding:0;border:0}
|
|
46
|
+
.record-content summary{font-size:11px;color:#757575;padding:5px 0}
|
|
47
|
+
.record-content .record-relations{border-top:1px solid #EEEEEE;padding-top:12px}
|
|
48
|
+
.record-content .record-conditions dl{margin:12px 0 0;font-size:11px;line-height:1.7}
|
|
49
|
+
.record-content .record-conditions dl>div{display:grid;grid-template-columns:100px 1fr;gap:12px;padding:7px 0}
|
|
50
|
+
.record-content .record-conditions dt{color:#9E9E9E}.record-content .record-conditions dd{margin:0;color:#616161;overflow-wrap:anywhere}
|
|
51
|
+
@media(prefers-reduced-motion:reduce){.record-content .figure-tabs button{transition:none}}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React,{useState} from 'react';
|
|
2
|
+
|
|
3
|
+
export function createRecordDetails({canPreview=n=>!!n.image,incident,byKey,isResult,isMethodIllustration,recordLabel,typeLabel,sourceLink,FigureCard,Measurements,Disclosure,predicateLabel}){
|
|
4
|
+
function FigureCollection({items,title,openRecord}){
|
|
5
|
+
const [active,setActive]=useState(items[0]?.key),n=items.find(n=>n.key===active)||items[0];
|
|
6
|
+
if(!n)return null;
|
|
7
|
+
return <section className="record-section figure-collection"><h3>{title}<small>{items.length}</small></h3>{items.length>1&&<div className="figure-tabs" role="tablist" aria-label={title}>{items.map(o=><button role="tab" aria-selected={o.key===n.key} key={o.key} onClick={()=>setActive(o.key)}>{(o.imageCaption||o.label).split(' · ')[0]}</button>)}</div>}<div className="figure-view" role="tabpanel" aria-label={recordLabel(n)}><FigureCard n={n}/><button className="figure-details-link" onClick={()=>openRecord(n.key)}>查看{(n.imageCaption||n.label).split(' · ')[0]}详情 <span aria-hidden="true">↗</span></button></div></section>;
|
|
8
|
+
}
|
|
9
|
+
const format=v=>typeof v==='object'?JSON.stringify(v):String(v);
|
|
10
|
+
return function RecordDetails({n,openRecord,openEdge}){
|
|
11
|
+
const links=incident.get(n.key)||[],related=[...new Set(links.map(e=>e.from===n.key?e.to:e.from))].map(k=>byKey.get(k)).filter(Boolean);
|
|
12
|
+
const evidence=related.filter(o=>isResult(o)&&!o.prospective),methods=related.filter(o=>isMethodIllustration(o)&&o.role!=='observation');
|
|
13
|
+
const diagrams=related.filter(o=>isMethodIllustration(o)&&o.role==='observation');
|
|
14
|
+
const routes=[...new Set(links.filter(e=>e.predicate==='plannedFor').map(e=>e.from===n.key?e.to:e.from))].map(k=>byKey.get(k)).filter(Boolean);
|
|
15
|
+
const rows=items=>items.map(o=><button className="detail-row record-link" key={o.key} onClick={()=>openRecord(o.key)}><span><b>{recordLabel(o)}</b><small>{typeLabel(o)}</small></span><span aria-hidden="true">↗</span></button>);
|
|
16
|
+
return <div className="record-content"><header className="record-intro"><h2>{recordLabel(n)}</h2>{n.brief&&<p className="lead">{n.brief}</p>}{n.source&&<a className="record-source" href={sourceLink(n)} target="_blank" rel="noopener">{n.source} <span aria-hidden="true">↗</span></a>}</header>
|
|
17
|
+
{canPreview(n)&&<section className="record-own-figure"><FigureCard n={n}/></section>}
|
|
18
|
+
{!!n.measurements?.length&&<div className="record-measurements"><Measurements key={n.key} items={n.measurements}/></div>}
|
|
19
|
+
{diagrams.length>0&&<FigureCollection key={n.key+':methods'} items={diagrams} title="方法图示" openRecord={openRecord}/>}
|
|
20
|
+
{evidence.some(o=>canPreview(o))&&<FigureCollection key={n.key+':evidence'} items={evidence.filter(o=>canPreview(o))} title="原文图表" openRecord={openRecord}/>}
|
|
21
|
+
{evidence.some(o=>!canPreview(o))&&<section className="record-section"><h3>相关证据</h3>{rows(evidence.filter(o=>!canPreview(o)))}</section>}
|
|
22
|
+
{n.conditions&&Object.keys(n.conditions).length>0&&<section className="record-section record-conditions"><Disclosure title="适用条件与方法">{()=> <dl>{Object.entries(n.conditions).map(([key,value])=><div key={key}><dt>{key}</dt><dd>{format(value)}</dd></div>)}</dl>}</Disclosure></section>}
|
|
23
|
+
{n.limitations?.length>0&&<section className="record-section record-limits"><h3>范围与限制</h3>{n.limitations.map((l,i)=><p key={i}>{l}</p>)}</section>}
|
|
24
|
+
{methods.length>0&&<section className="record-section"><h3>相关方法 <small>{methods.length}</small></h3>{rows(methods)}</section>}
|
|
25
|
+
{routes.length>0&&<section className="record-section"><h3>关联实验计划</h3>{rows(routes)}</section>}
|
|
26
|
+
<div className="record-relations"><Disclosure title={'全部关联 · '+links.length}>{()=>links.map(e=><button className="detail-row relation-row" key={e.id} onClick={()=>openEdge(e.id)}><span><small>{e.from===n.key?'→':'←'} {predicateLabel[e.predicate]||e.predicate}</small>{recordLabel(byKey.get(e.from===n.key?e.to:e.from))}</span></button>)}</Disclosure></div>
|
|
27
|
+
</div>;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import sourceTypes from './locales/source-types.json' with {type:'json'};
|
|
2
|
+
import {MD,alpha} from './material-colors.mjs';
|
|
3
|
+
export {MD,alpha};
|
|
4
|
+
|
|
5
|
+
// Reading semantics are a separate projection, never a rewrite of source records.
|
|
6
|
+
export const researchTypes=Object.freeze({
|
|
7
|
+
question:{label:'研究问题',short:'问题',family:'context',hint:'研究试图回答的问题或目标'},
|
|
8
|
+
concept:{label:'概念与定义',short:'概念',family:'context',hint:'概念、定义、理论框架,以及变量和指标的定义'},
|
|
9
|
+
hypothesis:{label:'假说与前提',short:'假说',family:'argument',hint:'待检验假说与条件前提;通过子类型区分'},
|
|
10
|
+
claim:{label:'主张',short:'主张',family:'argument',hint:'作者提出的发现、解释、定理或结论'},
|
|
11
|
+
method:{label:'方法',short:'方法',family:'process',hint:'可复用的操作方法、算法、分析或测量方法'},
|
|
12
|
+
plan:{label:'研究方案',short:'方案',family:'process',hint:'具体研究的安排或尚未执行的后续研究意向'},
|
|
13
|
+
activity:{label:'研究活动',short:'活动',family:'process',hint:'实际发生的采集、实验、训练、访谈或分析'},
|
|
14
|
+
result:{label:'观测与结果',short:'结果',family:'evidence',hint:'实际获得或计算得到的数据、图像及分析结果'},
|
|
15
|
+
argument:{label:'论证与证明',short:'论证',family:'argument',hint:'连接依据与主张的推导、证明或解释性论证'},
|
|
16
|
+
assessment:{label:'评估',short:'评估',family:'evidence',hint:'有归属与依据的验证、评价或局限性分析'},
|
|
17
|
+
resource:{label:'对象与资源',short:'资源',family:'resource',hint:'研究对象、材料、数据集、模型、仪器、代码和来源文献'},
|
|
18
|
+
agent:{label:'责任主体',short:'主体',family:'resource',hint:'提出、执行、提供或评估研究内容的人、组织或软件代理'}
|
|
19
|
+
});
|
|
20
|
+
const familyColors=Object.freeze({context:MD.blue700,argument:MD.indigo500,process:MD.deepOrange700,evidence:MD.teal600,resource:MD.grey600});
|
|
21
|
+
|
|
22
|
+
export const coreLabels=Object.fromEntries(Object.entries(researchTypes).map(([key,value])=>[key,value.label]));
|
|
23
|
+
export const coreHints=Object.fromEntries(Object.entries(researchTypes).map(([key,value])=>[key,value.hint]));
|
|
24
|
+
export const typeColors=Object.fromEntries(Object.entries(researchTypes).map(([key,value])=>[key,familyColors[value.family]]));
|
|
25
|
+
export const classificationOf=n=>({category:n?.category||'resource',subtype:n?.subtype&&n.subtype!==sourceTypes[n.category]?n.subtype:researchTypes[n?.category||'resource'].label,reason:'',contextualRoles:[]});
|
|
26
|
+
export const coreTypeOf=n=>classificationOf(n).category;
|
|
27
|
+
export const semanticLabelOf=n=>classificationOf(n).subtype;
|
|
28
|
+
export const descriptionOf=n=>classificationOf(n).reason;
|
|
29
|
+
export const colorOf=n=>typeColors[coreTypeOf(n)];
|
|
30
|
+
export const shapeOf=n=>({hypothesis:'hypothesis',method:'method',plan:'plan',activity:'activity',result:'observation',assessment:'assessment'})[coreTypeOf(n)]||'circle';
|
|
31
|
+
export const shortTypeLabel=type=>researchTypes[type].short;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
// Keep both strokes mounted: CSS interpolates their geometry in either direction.
|
|
4
|
+
export function SelectionMark({selected=false}){
|
|
5
|
+
return <svg className="selection-mark" data-selected={selected} viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path className="selection-mark-short" d="M5 12 L19 12"/><path className="selection-mark-long" d="M12 19 L12 5"/></svg>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Shared tangents make consecutive links flow through freely moving nodes.
|
|
2
|
+
// Control handles are bounded so uneven spacing cannot create large loops.
|
|
3
|
+
export function spineControls(points,index){
|
|
4
|
+
const a=points[index],b=points[index+1];
|
|
5
|
+
if(!a||!b||![a.x,a.y,b.x,b.y].every(Number.isFinite))return null;
|
|
6
|
+
const before=points[index-1]||a,after=points[index+2]||b;
|
|
7
|
+
const length=Math.hypot(b.x-a.x,b.y-a.y);
|
|
8
|
+
const handle=(from,to)=>{
|
|
9
|
+
const x=to.x-from.x,y=to.y-from.y,d=Math.hypot(x,y),scale=d?Math.min(.18,length*.28/d):0;
|
|
10
|
+
return {x:x*scale,y:y*scale};
|
|
11
|
+
};
|
|
12
|
+
const u=handle(before,b),v=handle(a,after);
|
|
13
|
+
return [a,{x:a.x+u.x,y:a.y+u.y},{x:b.x-v.x,y:b.y-v.y},b];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function paintSpine(ctx,[a,c,d,b],color,unit){
|
|
17
|
+
ctx.save();ctx.strokeStyle=color;ctx.lineWidth=1.45*unit;ctx.lineCap='round';ctx.lineJoin='round';ctx.setLineDash([]);
|
|
18
|
+
ctx.beginPath();ctx.moveTo(a.x,a.y);ctx.bezierCurveTo(c.x,c.y,d.x,d.y,b.x,b.y);ctx.stroke();
|
|
19
|
+
// A small open arrow follows the same curve, rather than its straight chord.
|
|
20
|
+
const t=.72,s=1-t,x=s*s*s*a.x+3*s*s*t*c.x+3*s*t*t*d.x+t*t*t*b.x,y=s*s*s*a.y+3*s*s*t*c.y+3*s*t*t*d.y+t*t*t*b.y;
|
|
21
|
+
const dx=3*s*s*(c.x-a.x)+6*s*t*(d.x-c.x)+3*t*t*(b.x-d.x),dy=3*s*s*(c.y-a.y)+6*s*t*(d.y-c.y)+3*t*t*(b.y-d.y),angle=Math.atan2(dy,dx);
|
|
22
|
+
if(Math.hypot(dx,dy)>8*unit){
|
|
23
|
+
ctx.translate(x,y);ctx.rotate(angle);ctx.lineWidth=1.1*unit;ctx.beginPath();ctx.moveTo(-3.2*unit,-1.9*unit);ctx.lineTo(0,0);ctx.lineTo(-3.2*unit,1.9*unit);ctx.stroke();
|
|
24
|
+
}
|
|
25
|
+
ctx.restore();
|
|
26
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
.legend { left:14px; max-width:min(540px, calc(100% - 28px)); column-gap:0; }
|
|
2
|
+
.legend button { font-size:11px; padding:5px 6px; }
|
|
3
|
+
.legend small { color:#616161; font-size:11px; }
|
|
4
|
+
.reference-app .legend-title { font-size:11px; margin:0 4px; }
|
|
5
|
+
.classification-note { color:#616161; font-size:11px; line-height:1.8; margin:0 0 16px; }
|
|
6
|
+
.classification-note span { display:block; color:#424242; font-weight:500; margin-bottom:3px; }
|
|
7
|
+
.category-description { margin:0; color:#616161; font-size:11px; line-height:1.7; }
|
|
8
|
+
.archive { gap:14px; flex-wrap:wrap; }
|
|
9
|
+
@media (max-width:780px) {
|
|
10
|
+
.projection-note { bottom:105px; }
|
|
11
|
+
}
|
|
12
|
+
@media (min-width:781px) { .projection-note { bottom:100px; } }
|
|
13
|
+
.reference-app .projection-note { bottom:66px; }
|
|
14
|
+
.reference-app .paper-card, .reference-app .paper-card.folded { display:block; top:76px; left:12px; width:340px; max-width:calc(100% - 24px); padding:12px 16px 10px; }
|
|
15
|
+
.reference-app .paper-card .toolbar { position:static; inset:auto; width:100%; max-width:none; margin:10px 0 0; padding:8px 0 0; border-top:1px solid #EEEEEE; border-radius:0; box-shadow:none; background:transparent; backdrop-filter:none; gap:3px; }
|
|
16
|
+
.reference-app .paper-card .toolbar button { min-height:30px; padding:5px 8px; }
|
|
17
|
+
.reference-app .paper-card .toolbar .catalog-button { margin-left:auto; }
|
|
18
|
+
.reference-app .paper-card .toolbar .toolbar-divider { display:none; }
|
|
19
|
+
.reference-app .legend-group-mark { display:grid; place-items:center; flex:none; }
|
|
20
|
+
.reference-app .paper-card-title>div { min-width:0; }
|
|
21
|
+
.reference-app .paper-card h1 { font-size:14px; line-height:1.6; }
|
|
22
|
+
.reference-app .graph-region { background-image:radial-gradient(#E0E0E066 .7px,transparent .7px); background-size:24px 24px; }
|
|
23
|
+
.reference-app .paper-card, .reference-app .toolbar, .reference-app .legend { border:0; backdrop-filter:none; background:#FFFFFF; box-shadow:0 2px 12px #21212106; }
|
|
24
|
+
.reference-app .paper-card { border-radius:16px; }
|
|
25
|
+
.reference-app .inspector, .reference-app .catalog { border:0; box-shadow:0 8px 28px #2121210C; }
|
|
26
|
+
.reference-app .settings-popover { border:0; box-shadow:0 8px 24px #21212112; }
|
|
27
|
+
.reference-app .source-chip { border:0; }
|
|
28
|
+
.reference-app .header-start { border:0; background:#F5F5F5; }
|
|
29
|
+
.reference-app .spine-nav { border:0; box-shadow:0 2px 12px #21212106; }
|
|
30
|
+
.reference-app .graph-hit, .reference-app .graph-label-hit { pointer-events:none; }
|
|
31
|
+
.reference-app .graph-canvas canvas { cursor:grab; }
|
|
32
|
+
.reference-app .graph-canvas canvas.grabbable { cursor:grabbing; }
|
|
33
|
+
.inspector, .catalog { animation:graph-panel-in 260ms cubic-bezier(.2,.8,.2,1); }
|
|
34
|
+
@keyframes graph-panel-in { from { opacity:0; transform:translateX(16px); } to { opacity:1; transform:translateX(0); } }
|
|
35
|
+
@media (prefers-reduced-motion:reduce) { .inspector, .catalog { animation:none; } }
|
|
36
|
+
|
|
37
|
+
/* Keep the graph and its evidence visible together in narrow windows. */
|
|
38
|
+
.type-mark { display:block; flex:none; }
|
|
39
|
+
.reference-app .legend { align-items:center; gap:1px; }
|
|
40
|
+
.reference-app .legend button { min-height:30px; gap:6px; padding:5px 7px; }
|
|
41
|
+
.drawer-actions { display:flex; align-items:center; gap:6px; }
|
|
42
|
+
.drawer-actions .detail-resize { display:none; }
|
|
43
|
+
.settings-note { margin:5px 8px 4px; font-size:11px; line-height:1.7; color:#757575; }
|
|
44
|
+
.reference-app .settings-popover { width:228px; }
|
|
45
|
+
.reference-app .spine-nav { bottom:102px; }
|
|
46
|
+
@media (max-width:780px) {
|
|
47
|
+
.reference-app.detail-open .scene { display:flex; flex-direction:column; }
|
|
48
|
+
.reference-app.detail-open .scene-main { flex:0 0 44%; min-height:0; }
|
|
49
|
+
.reference-app.detail-open .inspector { position:relative; inset:auto; width:calc(100% - 16px); height:auto; flex:1; min-height:0; margin:0 8px 8px; border-radius:18px; }
|
|
50
|
+
.reference-app.detail-open .spine-nav { left:auto; right:12px; top:20px; bottom:auto; transform:none; gap:4px; }
|
|
51
|
+
.reference-app.detail-open .spine-nav span { min-width:46px; }
|
|
52
|
+
.reference-app.detail-open .paper-card { display:block; width:250px; padding:6px 10px; }
|
|
53
|
+
.reference-app.detail-open .paper-card .paper-summary { display:none; }
|
|
54
|
+
.reference-app.detail-open .paper-card .toolbar { border:0; margin:0; padding:0; }
|
|
55
|
+
.reference-app.detail-open .drawer-actions .detail-resize { display:grid; }
|
|
56
|
+
.reference-app.detail-expanded .scene-main { flex-basis:64px; }
|
|
57
|
+
.reference-app.detail-expanded .graph-region { visibility:hidden; }
|
|
58
|
+
.reference-app.detail-open .drawer-top { height:46px; flex-shrink:0; padding:10px 18px; }
|
|
59
|
+
.reference-app.detail-open .drawer-body { padding:16px 20px 30px; }
|
|
60
|
+
.reference-app .legend { padding:5px; }
|
|
61
|
+
.reference-app .projection-note { bottom:61px; }
|
|
62
|
+
}
|
|
63
|
+
@media (max-width:420px) {
|
|
64
|
+
.reference-app .paper-card, .reference-app .paper-card.folded { width:calc(100% - 24px); max-width:none; }
|
|
65
|
+
.reference-app.detail-open .spine-nav { right:10px; }
|
|
66
|
+
.reference-app .legend { max-width:calc(100% - 24px); }
|
|
67
|
+
.reference-app .legend button { font-size:10px; padding:4px 6px; }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (max-width:420px) { .reference-app .projection-note { bottom:88px; } }
|
|
71
|
+
|
|
72
|
+
/* Details overlay a stable canvas; opening them never resizes the graph. */
|
|
73
|
+
.reference-app .scene-main { position:absolute; inset:0; }
|
|
74
|
+
.reference-app .inspector, .reference-app .catalog { position:absolute; top:12px; right:12px; bottom:12px; height:auto; margin:0; }
|
|
75
|
+
.drawer-top .detail-eyebrow { margin:0; letter-spacing:0; font-size:11px; flex:1; min-width:0; flex-wrap:wrap; }
|
|
76
|
+
.drawer-top .drawer-actions { flex:none; margin-left:10px; }
|
|
77
|
+
@media (max-width:780px) {
|
|
78
|
+
.reference-app.detail-open .scene { display:block; }
|
|
79
|
+
.reference-app.detail-open .scene-main { position:absolute; inset:0; }
|
|
80
|
+
.reference-app.detail-open .inspector { position:absolute; top:44%; right:8px; bottom:8px; left:8px; width:auto; height:auto; margin:0; animation:detail-rise 300ms cubic-bezier(.2,.8,.2,1); transition:top 300ms cubic-bezier(.2,.8,.2,1); }
|
|
81
|
+
.reference-app.detail-expanded .inspector { top:64px; }
|
|
82
|
+
.reference-app.detail-open .graph-region { visibility:visible; transition:opacity 200ms ease; }
|
|
83
|
+
.reference-app.detail-expanded .graph-region { visibility:visible; opacity:0; pointer-events:none; }
|
|
84
|
+
.reference-app .catalog { top:72px; width:min(384px,calc(100% - 24px)); }
|
|
85
|
+
@keyframes detail-rise { from { opacity:0; transform:translateY(24px); } to { opacity:1; transform:translateY(0); } }
|
|
86
|
+
}
|
|
87
|
+
@media (prefers-reduced-motion:reduce) { .reference-app .inspector, .reference-app .graph-region { animation:none; transition:none; } }
|
|
88
|
+
|
|
89
|
+
/* Record navigation is an inline control group, not another floating card. */
|
|
90
|
+
.reference-app .drawer-top .spine-nav {
|
|
91
|
+
position:static; inset:auto; transform:none; padding:0 6px 0 0; margin-right:2px;
|
|
92
|
+
background:transparent; border:0; border-radius:0; box-shadow:none; gap:2px; flex:none;
|
|
93
|
+
}
|
|
94
|
+
.reference-app .drawer-top .spine-nav span { min-width:34px; font-size:11px; color:#757575; }
|
|
95
|
+
.reference-app .drawer-top .spine-nav button { width:26px; height:28px; color:#616161; }
|
|
96
|
+
.reference-app .drawer-top .spine-nav button:disabled { opacity:.25; }
|
|
97
|
+
.reference-app .drawer-top .drawer-actions { gap:3px; margin-left:8px; }
|
|
98
|
+
.reference-app .drawer-top { height:auto; min-height:48px; gap:4px; padding:10px 14px; }
|
|
99
|
+
.reference-app .drawer-top .detail-eyebrow { font-size:10px; line-height:1.65; gap:4px; }
|
|
100
|
+
.reference-app .drawer-top button { transition:background 140ms ease,color 140ms ease; }
|
|
101
|
+
.reference-app .drawer-top button:hover:not(:disabled) { color:#424242; background:#F5F5F5; }
|
|
102
|
+
.reference-app .toolbar.settings-open { z-index:20; }
|
|
103
|
+
.reference-app .settings-popover {
|
|
104
|
+
position:fixed; right:auto; bottom:auto; overflow-y:auto; overscroll-behavior:contain;
|
|
105
|
+
border-radius:14px; padding:10px; background:#FFFFFF; box-shadow:0 8px 28px #2121211A;
|
|
106
|
+
}
|
|
107
|
+
.reference-app .settings-heading { color:#616161; font-size:11px; padding-bottom:6px; }
|
|
108
|
+
@media (max-width:420px) {
|
|
109
|
+
.reference-app .drawer-top { padding:9px 10px; }
|
|
110
|
+
.reference-app .drawer-top .spine-nav { padding-right:2px; margin-right:0; }
|
|
111
|
+
.reference-app .drawer-top .spine-nav span { min-width:30px; }
|
|
112
|
+
.reference-app .drawer-top .drawer-actions { margin-left:4px; gap:1px; }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* The outline is a reading navigator with one clear hierarchy. */
|
|
116
|
+
.reference-app .catalog.outline-panel { width:400px; border-radius:20px; }
|
|
117
|
+
.outline-header { display:flex; align-items:center; justify-content:space-between; padding:22px 22px 14px; flex:none; }
|
|
118
|
+
.outline-header h2 { margin:0; color:#212121; font-size:20px; font-weight:650; line-height:1.4; }
|
|
119
|
+
.outline-header p { margin:5px 0 0; color:#9E9E9E; font-size:11px; }
|
|
120
|
+
.outline-header p span { margin:0 5px; }
|
|
121
|
+
.outline-close, .outline-search button { display:grid; place-items:center; flex:none; border:0; padding:0; width:30px; height:30px; background:transparent; color:#757575; border-radius:8px; }
|
|
122
|
+
.outline-close:hover, .outline-search button:hover { background:#F5F5F5; }
|
|
123
|
+
.outline-search { display:flex; align-items:center; gap:9px; margin:0 20px 16px; padding:0 11px; min-height:40px; border-radius:10px; background:#F5F5F5; color:#9E9E9E; flex:none; }
|
|
124
|
+
.outline-search:focus-within { box-shadow:0 0 0 2px #E8EAF6; }
|
|
125
|
+
.outline-search input { border:0; background:transparent; color:#424242; font:inherit; font-size:12px; width:100%; min-width:0; padding:10px 0; outline:none; }
|
|
126
|
+
.outline-search input::-webkit-search-cancel-button { display:none; }
|
|
127
|
+
.outline-search input::placeholder { color:#9E9E9E; }
|
|
128
|
+
.outline-tabs { display:flex; flex:none; gap:24px; padding:0 22px; box-shadow:0 1px 0 #EEEEEE; }
|
|
129
|
+
.outline-tabs button { position:relative; border:0; padding:0 0 12px; background:transparent; color:#9E9E9E; font-size:12px; }
|
|
130
|
+
.outline-tabs button[aria-pressed=true] { color:#424242; font-weight:600; }
|
|
131
|
+
.outline-tabs button[aria-pressed=true]:after { content:''; position:absolute; bottom:0; left:0; right:0; height:2px; background:#616161; border-radius:2px; }
|
|
132
|
+
.outline-scroll { flex:1; min-height:0; overflow-y:auto; overscroll-behavior:contain; padding:8px 12px 20px; scrollbar-width:thin; scrollbar-color:#E0E0E0 transparent; }
|
|
133
|
+
.outline-section-label { display:flex; justify-content:space-between; align-items:center; gap:12px; margin:16px 10px 9px; color:#757575; font-size:10px; font-weight:500; }
|
|
134
|
+
.outline-section-label>span { color:#9E9E9E; font-weight:400; font-variant-numeric:tabular-nums; }
|
|
135
|
+
.outline-section { margin:0 0 10px; }
|
|
136
|
+
.outline-row { display:flex; align-items:center; gap:9px; width:100%; border:0; background:transparent; text-align:left; border-radius:9px; padding:9px 10px; line-height:1.55; color:#616161; }
|
|
137
|
+
.outline-row:hover { background:#FAFAFA; }
|
|
138
|
+
.outline-mark { display:grid; place-items:center; width:18px; flex:none; }
|
|
139
|
+
.outline-mark.numbered { width:28px; height:28px; border-radius:9px; background:#F5F5F5; font-size:11px; font-weight:600; font-variant-numeric:tabular-nums; }
|
|
140
|
+
.outline-row-copy { flex:1; min-width:0; }
|
|
141
|
+
.outline-row-copy b { display:block; color:#616161; font-size:12px; font-weight:450; overflow-wrap:anywhere; }
|
|
142
|
+
.outline-row-copy small { display:block; color:#9E9E9E; font-size:10px; margin-top:2px; }
|
|
143
|
+
.outline-main { gap:10px; padding-top:11px; padding-bottom:11px; }
|
|
144
|
+
.outline-main .outline-row-copy b { color:#424242; font-size:13px; font-weight:600; }
|
|
145
|
+
.outline-kind { font-size:9px; color:#9E9E9E; flex:none; max-width:84px; text-align:right; }
|
|
146
|
+
.outline-row>.ui-icon { color:#9E9E9E; opacity:0; transition:opacity 120ms ease; }
|
|
147
|
+
.outline-row:hover>.ui-icon, .outline-row:focus-visible>.ui-icon { opacity:1; }
|
|
148
|
+
.outline-children { margin:0 0 0 24px; padding-left:12px; border-left:1px solid #EEEEEE; }
|
|
149
|
+
.outline-children .outline-row { padding:7px 8px; gap:7px; }
|
|
150
|
+
.outline-children .outline-row-copy b { font-size:11px; }
|
|
151
|
+
.outline-children .outline-kind { max-width:68px; font-size:9px; }
|
|
152
|
+
.outline-filters { display:flex; flex:none; flex-wrap:wrap; align-items:center; gap:6px; padding:14px 20px 6px; scrollbar-width:thin; }
|
|
153
|
+
.outline-filters button { display:flex; align-items:center; gap:5px; flex:none; padding:5px 9px; border:0; border-radius:7px; background:transparent; font-size:10px; color:#757575; }
|
|
154
|
+
.outline-filters button[aria-pressed=true] { background:#F5F5F5; color:#424242; }
|
|
155
|
+
.outline-filters small { color:#9E9E9E; font-size:9px; }
|
|
156
|
+
.outline-result-count, .outline-type-note, .outline-empty { padding:8px 10px; color:#9E9E9E; font-size:11px; line-height:1.8; }
|
|
157
|
+
.outline-type-note { margin:0 0 8px; }
|
|
158
|
+
.outline-empty button { border:0; background:none; color:#3F51B5; }
|
|
159
|
+
.outline-relation { display:flex; flex-direction:column; align-items:flex-start; gap:4px; width:100%; padding:12px; margin:2px 0 8px; border:0; border-radius:10px; background:#FAFAFA; text-align:left; font-size:12px; line-height:1.6; }
|
|
160
|
+
.outline-relation:hover { background:#F5F5F5; }
|
|
161
|
+
.outline-relation-from { color:#616161; }
|
|
162
|
+
.outline-predicate { display:flex; align-items:center; gap:7px; color:#9E9E9E; font-size:10px; }
|
|
163
|
+
.outline-relation-to { color:#424242; padding-left:20px; }
|
|
164
|
+
.outline-more { display:flex; justify-content:center; gap:12px; margin:14px auto 0; border:0; background:#F5F5F5; border-radius:9px; padding:10px 18px; font-size:11px; color:#616161; }
|
|
165
|
+
.outline-more span { color:#9E9E9E; }
|
|
166
|
+
.outline-footer { display:flex; align-items:center; justify-content:space-between; gap:16px; flex:none; padding:15px 22px; box-shadow:0 -1px 0 #EEEEEE; }
|
|
167
|
+
.outline-footer a { display:flex; align-items:center; gap:6px; font-size:10px; color:#9E9E9E; }
|
|
168
|
+
.outline-footer a:hover { color:#616161; }
|
|
169
|
+
.reference-app .toolbar .catalog-button { display:flex; font-size:11px; width:auto; gap:6px; }
|
|
170
|
+
.reference-app .projection-note:empty { display:none; }
|
|
171
|
+
@media (max-width:780px) { .reference-app .catalog.outline-panel { top:72px; width:min(400px,calc(100% - 24px)); } }
|
|
172
|
+
@media (prefers-reduced-motion:reduce) { .outline-row>.ui-icon { transition:none; } }
|
|
173
|
+
|
|
174
|
+
/* Supplementary reading is grouped by purpose and expanded only when needed. */
|
|
175
|
+
.outline-supplement { padding:0; margin:0; border:0; }
|
|
176
|
+
.outline-supplement>summary { display:flex; align-items:center; gap:9px; padding:13px 10px; list-style:none; border-radius:9px; color:#424242; font-size:12px; }
|
|
177
|
+
.outline-supplement>summary::-webkit-details-marker { display:none; }
|
|
178
|
+
.outline-supplement>summary:hover { background:#FAFAFA; }
|
|
179
|
+
.outline-supplement>summary>span { flex:1; }
|
|
180
|
+
.outline-supplement>summary>small { color:#757575; font-size:10px; }
|
|
181
|
+
.outline-supplement>summary>.ui-icon { color:#9E9E9E; transition:transform 160ms ease; }
|
|
182
|
+
.outline-supplement[open]>summary>.ui-icon { transform:rotate(180deg); }
|
|
183
|
+
.outline-supplement>.outline-type-note { padding-top:0; margin:0 0 4px; font-size:10px; }
|
|
184
|
+
.outline-subgroups { padding:0 0 8px 12px; border-left:1px solid #EEEEEE; margin-left:10px; }
|
|
185
|
+
.outline-subgroups>.outline-supplement>summary { padding:9px 10px; font-size:11px; }
|
|
186
|
+
.outline-children .outline-children { margin-left:12px; padding-left:7px; }
|
|
187
|
+
@media (prefers-reduced-motion:reduce) { .outline-supplement>summary>.ui-icon { transition:none; } }
|