Inline timer in note via DataviewJS + metadata only(buggy)

Here’s my stuff

Here is: Inline timer in note via DataviewJS + metadata. It’s working very janky, but I really like the idea of inline timers. I didn’t find the implementation of this idea, so i did it myself…
I don’t have enough time to continue this prj for now

I would be glad if someone nice would fix this stuff, thx

Here is code:

[[timer::00:00]]

```dataviewjs
// U HAVE TO ADD the path to the SAX GANDALF MP3(or any other mp3) FIRST.. U HAVE TO !!!!
const soundPath = "---/MP3/HD Epic Sax Gandalf.mp3";




const file=dv.current();
const time = file.timer;

const afile = app.workspace.getActiveFile();
const content = await app.vault.read(afile);




const txt = content
const txtNum = [];
for (let i = 0; i < txt.length; i++) {
    txtNum.push(String(i));
}
let awaiting = false;
let isInl = false;
const markdown = [];
const InlMarkdown = [];

let newMark = [];
for (let index = 0; index < txt.length - 2; index++) {
    const i = txt[index];
    const j = txt[index + 1];
    const i1 = txtNum[index];
    const j1 = txtNum[index + 1];
    
    if (i === "[" && j === "[") {
        awaiting = true;
        newMark.push(i1);
    }
    if (awaiting && i === ":" && j === ":") {
        isInl = true;
    }
    if (awaiting && i === "]" && j === "]") {
        newMark.push(j1);
        if (isInl) {
            InlMarkdown.push(newMark);
        } else {
            markdown.push(newMark);
        }
        newMark = [];
        awaiting = false;
        isInl = false;
    }
}

for (const i of InlMarkdown) {
    console.log(txt.slice(parseInt(i[0]), parseInt(i[1])));
}
console.log(InlMarkdown);





// найти все входы старого таймера и поменять их на новый
    // Create and style timer container
const timerContainer = dv.el('div');

timerContainer.style.cssText = "top: 50%;left: 50%;font-family: 'Arial', sans-serif;text-align: center;user-select: none;color: #2D353B;";

const display = dv.el('div','111111');
display.id = 'timer-display';
display.style.cssText = "font-size: 5rem;font-weight: 300;letter-spacing: -3px;margin-bottom: 1rem;color: #2B59C3";

const btnContainer = dv.el('div');
    btnContainer.style.display = 'flex';
    btnContainer.style.gap = '0.5rem';
    btnContainer.style.justifyContent = 'center';
    const startBtn = dv.el('button');
    startBtn.textContent = 'Start';
    startBtn.style.cssText = `
      background: #f0f0f0;
      border: 1px solid #ddd;
      padding: 0.5rem 1rem;
      cursor: pointer;
      font-size: 1rem;
      border-radius: 4px;
      transition: all 0.2s;
    `;
startBtn.onmouseover = () => startBtn.style.background = '#3A4349';
    startBtn.onmouseout = () => startBtn.style.background = '#3A4349';
    const resetBtn = dv.el('button');
    resetBtn.textContent = 'Reset';
    resetBtn.style.cssText = startBtn.style.cssText;
    resetBtn.onmouseover = () => resetBtn.style.background = '#3A4349';
    resetBtn.onmouseout = () => resetBtn.style.background = '#3A4349';

    btnContainer.appendChild(startBtn);
    btnContainer.appendChild(resetBtn);
    timerContainer.appendChild(display);
	timerContainer.appendChild(btnContainer);
	dv.el= timerContainer;
	
	//ВОТ НАЧАЛЬНОЕ ВРЕМЯ!!!!!!
	//!!!
	const mins =parseInt(time.toString().slice(0,2))
	const secs=parseInt(time.toString().slice(3,5))
	//let initTime =(time.toString().slice(0,2).toInt()
	let initTime = mins*60+secs
	//!!!
	//ВОТ НАЧАЛЬНОЕ ВРЕМЯ!!!!!!
	let countdown;
    let remainingTime = initTime; // 25 minutes (25 * 60)
    function formatTime(seconds) {
      const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
      const secs = (seconds % 60).toString().padStart(2, '0');
      return `${mins}:${secs}`;
    }

    async function updateDisplay() {
      let newTime =formatTime(remainingTime);
      display.textContent = newTime;
      //time = Math.floor(initTime / 60)

	  const updInline = "timer::"+newTime.toString();
	  let copiedtxt = "";
	  for (let i of InlMarkdown) {
	      const meta = txt.slice(parseInt(i[0]) + 2, parseInt(i[1]) - 1);
	      if (meta.slice(0, meta.indexOf("::")) === "timer") {
	         copiedtxt = txt.slice(0, parseInt(i[0]) + 2) + updInline + txt.slice(parseInt(i[1]) - 1);
		    }
		}
		// Write changes back to file
		await app.vault.modify(afile, copiedtxt);

	
    }

    function startTimer() {
      if (countdown) return;
      startBtn.textContent = 'Pause';
      countdown = setInterval(() => {
        remainingTime--;
        updateDisplay();
        if (remainingTime <= 0) {
	      const audio = new Audio(app.vault.adapter.getResourcePath(soundPath));
          audio.play().catch(e => console.error("Playback failed:", e));
          clearInterval(countdown);
          countdown = null;
          startBtn.textContent = 'Start';
          display.style.color = '#e74c3c';
          setTimeout(() => display.style.color = '#333', 1000);
        }
      }, 1000);
    }

    function pauseTimer() {
      clearInterval(countdown);
      countdown = null;
      startBtn.textContent = 'Start';
    }

    function resetTimer() {
      clearInterval(countdown);
      countdown = null;
      remainingTime = initTime;
      updateDisplay();
      startBtn.textContent = 'Start';
      display.style.color = '#443FAE';
    }

    startBtn.addEventListener('click', () => {
      countdown ? pauseTimer() : startTimer();
    });

    resetBtn.addEventListener('click', resetTimer);
    // Initialize display
    updateDisplay();

Here is md page:

Timer MY TIMER (magic timer with a Gandalf blessing).md (5.3 KB)

U have to add up a path to the mp3 file first btw

pls don’t get destructed by strange notes in my note

Known problems:

  1. It can randomly stop when you change the note, but js code is still gonna run. So you can run like 5 countdowns in the 1 moment(it was a huuuuge surprise when they came off in 1 moment lol)
  2. The page is shaking all the time
  3. It doesn’t allow to edit page when countdown is active(so the only solution for this countdown to stop is actually alt+f4)
  4. When in preview mode from other page it will destroy the whole page for some reason. I.g. the code will run but not fully ruining the whole other page to the ground…

Moved to Help.

The Share&Showcase is becoming a place of AI churned stuff nobody cares about TBH.

1 Like

The first version of the timer:

It’s the first ver of timer without app.vault API, therefore no killing of pages. BUT it has problems like:

  1. when u think it’s not working - it’s working(u start timer. Timer stops for some reason. It goes down in the background anyway. It becomes a screamer when it’s done)
  2. through 1st problem you can start infinite amount of timers simultaneously
  3. The display stops for some reason when you change the page(and it’s doing it always)

The code:



const soundPath = "---/MP3/HD Epic Sax Gandalf.mp3";
    // Create and style timer container
const timerContainer = dv.el('div');

timerContainer.style.cssText = "top: 50%;left: 50%;font-family: 'Arial', sans-serif;text-align: center;user-select: none;color: #2D353B;";

const display = dv.el('div','111111');
display.id = 'timer-display';
display.style.cssText = "font-size: 5rem;font-weight: 300;letter-spacing: -3px;margin-bottom: 1rem;color: #2B59C3";

const btnContainer = dv.el('div');
    btnContainer.style.display = 'flex';
    btnContainer.style.gap = '0.5rem';
    btnContainer.style.justifyContent = 'center';
    const startBtn = dv.el('button');
    startBtn.textContent = 'Start';
    startBtn.style.cssText = `
      background: #f0f0f0;
      border: 1px solid #ddd;
      padding: 0.5rem 1rem;
      cursor: pointer;
      font-size: 1rem;
      border-radius: 4px;
      transition: all 0.2s;
    `;
startBtn.onmouseover = () => startBtn.style.background = '#3A4349';
    startBtn.onmouseout = () => startBtn.style.background = '#3A4349';
    const resetBtn = dv.el('button');
    resetBtn.textContent = 'Reset';
    resetBtn.style.cssText = startBtn.style.cssText;
    resetBtn.onmouseover = () => resetBtn.style.background = '#3A4349';
    resetBtn.onmouseout = () => resetBtn.style.background = '#3A4349';

    btnContainer.appendChild(startBtn);
    btnContainer.appendChild(resetBtn);
    timerContainer.appendChild(display);
	timerContainer.appendChild(btnContainer);
	dv.el= timerContainer;
	
	//ВОТ НАЧАЛЬНОЕ ВРЕМЯ!!!!!!
	//!!!
	let initTime = 60 * 60
	//!!!
	//ВОТ НАЧАЛЬНОЕ ВРЕМЯ!!!!!!
	let countdown;
    let remainingTime = initTime; // 25 minutes (25 * 60)
    function formatTime(seconds) {
      const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
      const secs = (seconds % 60).toString().padStart(2, '0');
      return `${mins}:${secs}`;
    }

    function updateDisplay() {
      display.textContent = formatTime(remainingTime);
    }

    function startTimer() {
      if (countdown) return;
      startBtn.textContent = 'Pause';
      countdown = setInterval(() => {
        remainingTime--;
        updateDisplay();
        if (remainingTime <= 0) {
	      const audio = new Audio(app.vault.adapter.getResourcePath(soundPath));
          audio.play().catch(e => console.error("Playback failed:", e));
          clearInterval(countdown);
          countdown = null;
          startBtn.textContent = 'Start';
          display.style.color = '#e74c3c';
          setTimeout(() => display.style.color = '#333', 1000);
        }
      }, 1000);
    }

    function pauseTimer() {
      clearInterval(countdown);
      countdown = null;
      startBtn.textContent = 'Start';
    }

    function resetTimer() {
      clearInterval(countdown);
      countdown = null;
      remainingTime = initTime;
      updateDisplay();
      startBtn.textContent = 'Start';
      display.style.color = '#443FAE';
    }

    startBtn.addEventListener('click', () => {
      countdown ? pauseTimer() : startTimer();
    });

    resetBtn.addEventListener('click', resetTimer);
    // Initialize display
    updateDisplay();