eLabFTWを見やすいように改造してみる

会社で電子実験ノートシステムを構築してから半年ほどが過ぎ、自部署のスタンダードツールに採用されるほどになった。この間、羊土社の実験医学2024年5月号にも記事掲載の機会をいただくなど、社内外を問わず、仕事の傍らそれなりに機能の改変に取り組んできた。
利用者が多くなるにつれて、「使いにくい！改良してほしい！」というポイントも数々出てきており、そこに対する改良も進めてきた。本記事では、改良の一部を紹介する。

※本記事の内容およびコードは主にChatGPTを利用して生成した。

### 1. フォントの追加
電子実験ノートの可読性を向上させるために、Google Fontsから「Noto Sans JP」、「Space Mono」、「Fira Code」を追加しました。これにより、特に日本語テキストの表示が美しくなり、コードの可読性も向上しました。
```html
<link rel="preconnect" href="https://fonts.googleapis.com" media="print" onload="this.media='all'">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin media="print" onload="this.media='all'">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&display=swap" rel="stylesheet">
```

### 2. デザインの改善
以下の点でデザインを改善しました
#### ボタンとリンクのデザイン
ボタンやリンクのホバー時の色変更、ボタンのシャドウ効果を追加し、視覚的にわかりやすくしました。

#### ナビゲーションバーの固定表示
ナビゲーションバーをページの上部に固定表示させ、スクロールしても常に表示されるようにしました。これにより、どのページにいてもメニューにすぐアクセスできるようになりました。
####目次の自動生成
ページ内の見出しを基に目次を自動生成し、ページの右側に固定表示することで、コンテンツのナビゲーションが容易になりました。

```css
.navbar {
  background-color: #212529;
  position: sticky;
  width: 100%;
  top: 0;
  height: 50px; /* ナビゲーションバーの高さ */
  z-index: 2; /* ナビゲーションバーを前面に */
}

#table-of-contents {
  line-height: 1.5;
  list-style-type: none;
  max-height: 90vh;
  overflow-y: auto;
  position: fixed;
  right: 2%;
  top: 8%;
  width: 15%;
}
```


### インタラクティブな機能の強化
以下のJavaScriptライブラリを導入し、ユーザー体験を向上させました：

#### Marked.js
Markdownのレンダリングを実現し、テキストエリアに入力された内容をリアルタイムでプレビュー表示できるようにしました。
#### Highlight.js
コードブロックのシンタックスハイライトを追加し、コードの可読性を向上させました。
#### Mermaid.js
Markdown内にMermaid記法で記述されたダイアグラムを自動的にレンダリングし、視覚的に情報を整理しやすくしました。

```html
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.5.0/dist/mermaid.min.js"></script>
```

### 検索機能と通知機能の改善
#### 検索バーの改善
ナビゲーションバーに検索バーを設置し、どのページからでもすぐに検索できるようにしました。
#### 通知機能の強化
通知ベルアイコンを追加し、新しい通知がある場合にわかりやすく表示されるようにしました。ドロップダウンメニューで通知の詳細を確認できるほか、「すべてクリア」ボタンで一括消去も可能にしました。

```html
<div class='nav-item form-inline ml-auto'>
  <input id='quicksearchInput' class='form-control big-search' type='search' name='q' aria-label='{{ 'Search'|trans }}' size='20' value='{{ App.Request.query.get('q')|trim|e('html_attr') }}' />
</div>

<div class='nav-item dropdown ml-2' id='navbarNotifDiv'>
  <button class='btn nav-link dropdown-toggle border-0' id='navbarNotifDropdown' type='button' aria-label='{{ 'Notifications'|trans }}' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>
    <i class='far fa-bell fa-2x {{ App.notifsArr|filter(n => n.is_ack == 0)|length ? 'active' : '' }}'></i>
  </button>
  <div class='dropdown-menu dropdown-menu-right' id='navbar-notif-dropdown' aria-labelledby='navbarNotifDropdown'>
    <span class='dropdown-header'>{{ 'Notifications'|trans }}</span>
    <!-- Notification items will be dynamically added here -->
    <a class='dropdown-item hover-danger' href='#' data-userid='{{ App.Users.userData.userid }}' data-action='destroy-notif'><i class='fas fa-trash-alt fa-fw' title='{{ 'Clear all'|trans }}'></i> {{ 'Clear all'|trans }}</a>
    <a href='ucp.php?tab=5' class='dropdown-item'><i class='fas fa-cogs fa-fw'></i> {{ 'Notifications settings'|trans }}</a>
  </div>
</div>
```

### Markdownエディタの改善
リアルタイムプレビュー: テキストエリアに入力するたびに、プレビューエリアが自動的に更新されるようにし、入力内容がどのように表示されるかを即座に確認できるようにしました。
数式のサポート: 数式の表示を改善し、...で囲まれた部分や$...$で囲まれた部分を自動的に処理して表示するようにしました。
```javascript
function updatePreview(textArea, previewArea) {
  const content = processMath(textArea.value);
  const renderedHTML = marked.parse(content);
  previewArea.innerHTML = renderedHTML;
  document.querySelectorAll('pre code').forEach(block => {
    hljs.highlightElement(block);
  });
  applyMermaid();
}

function processMath(text) {
  text = text.replace(/\$\$([\s\S]+?)\$\$/g, (match, p1) => {
    return `$$${escapeMath(p1)}$$`;
  });
  text = text.replace(/\$([^$]+?)\$/g, (match, p1) => {
    return `$${escapeMath(p1)}$`;
  });
  return text;
}

function escapeMath(mathText) {
  return mathText.replace(/\\{2,}/g, '\\\\\\\\');
}
```

※本記事はコニカミノルタ株式会社　技術開発本部にて@yanamiと共に実施しました。本記事の公開は、コニカミノルタ技術開発本部先進コア技術センターにより外部発表許可を受けていますが、本コードは私及び@yanamiが構築したものであり、コニカミノルタ株式会社は本ページ記載の情報の利用に関して、一切の責任を負いません。