Add TOC custom overrides and fix version numbers to 03.06.02

Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-30 01:09:18 +00:00
parent 70ba795d2d
commit d00fc75bca
9 changed files with 417 additions and 7 deletions

View File

@@ -105,7 +105,7 @@ This project adheres to [MokoStandards](https://github.com/mokoconsulting-tech/M
* Repository: [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
* Path: /docs/README.md
* Owner: Moko Consulting
* Version: 03.06.00
* Version: 03.06.02
* Status: Active
* Effective Date: 2026-01-09

View File

@@ -24,7 +24,7 @@ This document provides a comprehensive, version-specific roadmap for the MokoCas
- [Version Timeline](#version-timeline)
- [Past Releases](#past-releases)
- [Future Roadmap (5-Year Plan)](#future-roadmap-5-year-plan)
- [Current Release (v03.06.00)](#current-release-v030600)
- [Current Release (v03.06.02)](#current-release-v030600)
- [Implemented Features](#implemented-features)
- [Planned Features](#planned-features)
- [Development Priorities](#development-priorities)
@@ -437,7 +437,7 @@ The following versions represent our planned annual major releases, each buildin
---
## Current Release (v03.06.00)
## Current Release (v03.06.02)
### System Requirements
- **Joomla**: 4.4.x or 5.x
@@ -841,7 +841,7 @@ Have ideas for future features? We welcome community input!
* Repository: [https://github.com/mokoconsulting-tech/moko-cassiopeia](https://github.com/mokoconsulting-tech/moko-cassiopeia)
* Path: /docs/ROADMAP.md
* Owner: Moko Consulting
* Version: 03.06.00
* Version: 03.06.02
* Status: Active
* Last Updated: 2026-01-27
* Classification: Public Open Source Documentation

View File

@@ -10,7 +10,7 @@
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./templates/mokocassiopeia/component.php
VERSION: 03.06.00
VERSION: 03.06.02
BRIEF: Main template index file for MokoCassiopeia rendering site layout
*/

View File

@@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@@ -0,0 +1,204 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.MokoCassiopeia
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 3 or later; see LICENSE.txt
*
* FILE INFORMATION
* DEFGROUP: Joomla.Template.Site
* INGROUP: MokoCassiopeia
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-left.php
* VERSION: 03.06.02
* BRIEF: Article layout with table of contents on the left side
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
// Get article params
$params = $this->item->params;
$images = json_decode($this->item->images);
$urls = json_decode($this->item->urls);
$canEdit = $params->get('access-edit');
$info = $params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
<div class="row">
<!-- Table of Contents - Left Side -->
<div class="col-lg-3 col-md-4 order-md-1 mb-4">
<div class="sticky-top toc-wrapper" style="top: 20px;">
<nav id="toc" class="toc-container">
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
<nav id="toc-nav" class="nav flex-column"></nav>
</nav>
</div>
</div>
<!-- Article Content -->
<div class="col-lg-9 col-md-8 order-md-2">
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1><?php echo $this->escape($this->params->get('page_heading')); ?></h1>
</div>
<?php endif; ?>
<?php if (!$this->print) : ?>
<?php if ($canEdit || $params->get('show_print_icon') || $params->get('show_email_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => false]); ?>
<?php endif; ?>
<?php else : ?>
<?php if ($params->get('show_print_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => true]); ?>
<?php endif; ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '0') || ($params->get('urls_position') == '0' && empty($urls->urls_position))) || (empty($urls->urls_position) && (!$params->get('urls_position')))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
<?php if (isset($info) && $info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<div class="article-content" itemprop="articleBody">
<?php echo $this->item->text; ?>
</div>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '1') || ($params->get('urls_position') == '1'))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php elseif ($params->get('show_noauth') == true && $this->user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
<?php if (isset($info) && ($info == 1 || $info == 2)) : ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
</div>
</div>
</div>
<script>
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', function() {
const content = document.querySelector('.article-content');
const tocNav = document.getElementById('toc-nav');
if (!content || !tocNav) return;
const headings = content.querySelectorAll('h2, h3, h4, h5, h6');
if (headings.length === 0) {
document.getElementById('toc').style.display = 'none';
return;
}
headings.forEach((heading, index) => {
if (!heading.id) {
heading.id = 'heading-' + index;
}
const link = document.createElement('a');
link.className = 'nav-link';
link.href = '#' + heading.id;
link.textContent = heading.textContent;
const level = parseInt(heading.tagName.substring(1));
link.style.paddingLeft = ((level - 2) * 15 + 10) + 'px';
tocNav.appendChild(link);
});
tocNav.querySelectorAll('a').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const target = document.getElementById(targetId);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
});
})();
</script>
<style>
.toc-container {
background: var(--cassiopeia-color-bg, #fff);
border: 1px solid var(--cassiopeia-color-border, #dee2e6);
border-radius: 0.375rem;
padding: 1rem;
}
.toc-title {
margin-bottom: 0.75rem;
font-size: 1rem;
font-weight: 600;
color: var(--cassiopeia-color-text, #212529);
}
#toc-nav .nav-link {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
color: var(--cassiopeia-color-link, #0d6efd);
text-decoration: none;
display: block;
border-left: 2px solid transparent;
transition: all 0.2s ease;
}
#toc-nav .nav-link:hover {
color: var(--cassiopeia-color-hover, #0a58ca);
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
}
#toc-nav .nav-link.active {
color: var(--cassiopeia-color-primary, #0d6efd);
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
}
@media (max-width: 767.98px) {
.toc-wrapper {
position: static !important;
margin-bottom: 1.5rem;
}
}
</style>

View File

@@ -0,0 +1,204 @@
<?php
/**
* @package Joomla.Site
* @subpackage Templates.MokoCassiopeia
*
* @copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
* @license GNU General Public License version 3 or later; see LICENSE.txt
*
* FILE INFORMATION
* DEFGROUP: Joomla.Template.Site
* INGROUP: MokoCassiopeia
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-right.php
* VERSION: 03.06.02
* BRIEF: Article layout with table of contents on the right side
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Content\Administrator\Extension\ContentComponent;
// Get article params
$params = $this->item->params;
$images = json_decode($this->item->images);
$urls = json_decode($this->item->urls);
$canEdit = $params->get('access-edit');
$info = $params->get('info_block_position', 0);
// Check if associations are implemented. If they are, define the parameter.
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
?>
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
<div class="row">
<!-- Article Content -->
<div class="col-lg-9 col-md-8 order-md-1">
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
<?php if ($this->params->get('show_page_heading')) : ?>
<div class="page-header">
<h1><?php echo $this->escape($this->params->get('page_heading')); ?></h1>
</div>
<?php endif; ?>
<?php if (!$this->print) : ?>
<?php if ($canEdit || $params->get('show_print_icon') || $params->get('show_email_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => false]); ?>
<?php endif; ?>
<?php else : ?>
<?php if ($params->get('show_print_icon')) : ?>
<?php echo LayoutHelper::render('joomla.content.icons', ['params' => $params, 'item' => $this->item, 'print' => true]); ?>
<?php endif; ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayTitle; ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo $this->item->event->beforeDisplayContent; ?>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '0') || ($params->get('urls_position') == '0' && empty($urls->urls_position))) || (empty($urls->urls_position) && (!$params->get('urls_position')))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php if ($params->get('access-view')) : ?>
<?php echo LayoutHelper::render('joomla.content.full_image', $this->item); ?>
<?php if (isset($info) && $info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'above']); ?>
<?php endif; ?>
<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<div class="article-content" itemprop="articleBody">
<?php echo $this->item->text; ?>
</div>
<?php if (isset($urls) && ((!empty($urls->urls_position) && $urls->urls_position == '1') || ($params->get('urls_position') == '1'))) : ?>
<?php echo $this->loadTemplate('links'); ?>
<?php endif; ?>
<?php elseif ($params->get('show_noauth') == true && $this->user->get('guest')) : ?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<?php echo HTMLHelper::_('content.prepare', $this->item->introtext); ?>
<?php endif; ?>
<?php echo $this->item->event->afterDisplayContent; ?>
<?php if (isset($info) && ($info == 1 || $info == 2)) : ?>
<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.content.info_block', ['item' => $this->item, 'params' => $params, 'position' => 'below']); ?>
<?php endif; ?>
</div>
<!-- Table of Contents - Right Side -->
<div class="col-lg-3 col-md-4 order-md-2 mb-4">
<div class="sticky-top toc-wrapper" style="top: 20px;">
<nav id="toc" class="toc-container">
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
<nav id="toc-nav" class="nav flex-column"></nav>
</nav>
</div>
</div>
</div>
</div>
<script>
(function() {
'use strict';
document.addEventListener('DOMContentLoaded', function() {
const content = document.querySelector('.article-content');
const tocNav = document.getElementById('toc-nav');
if (!content || !tocNav) return;
const headings = content.querySelectorAll('h2, h3, h4, h5, h6');
if (headings.length === 0) {
document.getElementById('toc').style.display = 'none';
return;
}
headings.forEach((heading, index) => {
if (!heading.id) {
heading.id = 'heading-' + index;
}
const link = document.createElement('a');
link.className = 'nav-link';
link.href = '#' + heading.id;
link.textContent = heading.textContent;
const level = parseInt(heading.tagName.substring(1));
link.style.paddingLeft = ((level - 2) * 15 + 10) + 'px';
tocNav.appendChild(link);
});
tocNav.querySelectorAll('a').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
const target = document.getElementById(targetId);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
});
})();
</script>
<style>
.toc-container {
background: var(--cassiopeia-color-bg, #fff);
border: 1px solid var(--cassiopeia-color-border, #dee2e6);
border-radius: 0.375rem;
padding: 1rem;
}
.toc-title {
margin-bottom: 0.75rem;
font-size: 1rem;
font-weight: 600;
color: var(--cassiopeia-color-text, #212529);
}
#toc-nav .nav-link {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
color: var(--cassiopeia-color-link, #0d6efd);
text-decoration: none;
display: block;
border-left: 2px solid transparent;
transition: all 0.2s ease;
}
#toc-nav .nav-link:hover {
color: var(--cassiopeia-color-hover, #0a58ca);
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
}
#toc-nav .nav-link.active {
color: var(--cassiopeia-color-primary, #0d6efd);
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
}
@media (max-width: 767.98px) {
.toc-wrapper {
position: static !important;
margin-bottom: 1.5rem;
}
}
</style>

View File

@@ -17,7 +17,7 @@
"defgroup": "Joomla.Template.Site",
"ingroup": "MokoCassiopeia.Template.Assets",
"path": "./media/templates/site/mokocassiopeia/joomla.asset.json",
"version": "03.06.00",
"version": "03.06.02",
"brief": "Joomla asset registry for MokoCassiopeia"
}
},

View File

@@ -36,6 +36,7 @@
<filename>joomla.asset.json</filename>
<filename>offline.php</filename>
<filename>templateDetails.xml</filename>
<folder>html</folder>
</files>
<stylesheets>
<stylesheet>media/templates/site/mokocassiopeia/css/editor.css</stylesheet>

View File

@@ -10,7 +10,7 @@
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./updates.xml
VERSION: 03.06.00
VERSION: 03.06.02
BRIEF: Update manifest XML file for MokoCassiopeia
-->