Closet For Anki [Official support]

Wouldn’t it be better to use [[mix1::]] tags on all the cells of the first column, then mix2 on all the cells in the second column, and add [[ord::mix1, mix2]] in the cmds0 field. You’ll get the same results, without the messiness of having to apply tags across cells.

Also, is there a reason you’re using the default anki clozes with closet instead of just using the closet clozes?

@alex11 Don’t use undo, there’s this addon to flip cards. Or if you don’t wanna install it, pressing e to open the edit window, then immediately pressing Escape to close it, will give you the front of the card again.

1 Like

I don’t know whether or not this has been posted yet.
Is it possible to group the cover fields on an image together but reveal them one by one?
I know it’s not possible in Image occlusion because each field is an individual card. But maybe in Closet if they are all linked together? :slight_smile:
cheers! :wave:

Yes, revealing them one by one [upon clicking the occlusions] is quite possible with the note type somenone [maybe kleinerpirat] made and posted earlier in this thread.

It’s somewhat limited though, as it’ll reveal each occlusion upon clicking it, regardless of which card the occlusion belongs to. For example: if you made a cloze with several [[rect1::]] and several [[rect2::]], it’ll ask you to incrementally click and reveal all of the occlusions, in both Card 1 and Card 2 produced from that Note. But it’s not a major hassle as I haven’t found myself needing multiple card incremental image occlusion yet.

Link. The other template has incremental text reveal function too, you can play around with the scripts using asset manager to add the functionality to your own templates.

1 Like

Is it possible to combine click to reveal with keydown to reveal? Also, the script from the closet engine doesn’t seem to work anymore since when I tried pressing Q or W, nothing happened.

Very much possible, check this out: Cloze one by one uncovering - #16 by shallash.

And this for context: Cloze one by one uncovering - #14 by shallash

1 Like

Hello,

I just discovered this plugin yesterday and it seems to cover exactly what I need.
After reading I found this automatic addition of “active” into the cmds fields.

However, this does not seem to be working for me and I am wondering if I missed a step.

  • I installed the closet, asset manager and preserve plug ins
  • I installed the custom styles and added a shortcut to the wrapping function with this [[c%j:: so it should add the active in the cmds field
  • I added some Cmds fields and cards with conditional adding. Also this Cmds0 and cmds1 with display none CSS styling.

I first thought it might be due to my first card which is only like a normal basic with front and back. Then I renamed and sorted the cards, so that card1 matches cmds1.

But when I click on this shortcut button created from the styles add on it only adds the styling but no “active”.

I don’t know what else to do. Thanks

In newer versions of Closet, you don’t need the Custom Styles add-on anymore. Maybe the shortcut you defined overrides Closet’s shortcut.

There should be a dropdown select element in the editor toolbar which says “Cloze” by default. You can choose different wrappers there, and they all work with Ctrl+Shift+C, but only with non-cloze note types.

2 Likes

Thank you very much for this clarification!
I saw the dropdown but as it had no direct effect, I didn’t know that it had such a functionality.
This is cool!

Now it would be cool, to have a shortcut for switching these modes instead of going with the dropdown :thinking:

You can use Ctrl/Cmd-1, Ctrl/Cmd-2, etc. to toggle quickly between them

1 Like

thank you very much!

Just a short remark on the custom styles plug-in: when disables (or not installed on my other computer), the shortcut does not work. I needed to download the plugin, but not define a custom shortcut. Only then it worked with the addition of [[c1::]]

Hello,

I am trying to make minimal pair cards with Closet that will pick one word from the minimal pair at random. This StackExchange question explains why this randomization useful, as well as what minimal pairs are. The first answer listed didn’t work for me.

What I had in mind with Closet was a note with four fields: Sound 1, Word 1, Sound 2, Word 2, with the following templates (imitating the last example on this page):

Front

<!--
[[spec1::[[setl::v::{{Sound 1}}||{{Word 1}}]]]]
[[spec2::[[setl::v::{{Sound 2}}||{{Word 2}}]]]]
-->
What word do you hear?<br>
<span>[[pi::v]]</span>

Back

{{FrontSide}}

<hr id=answer>

You have heard the word:
<span>[[pi1::v]]</span>

You may practice both words again:<br/>
{{Word 1}} {{Sound 1}}<br>
{{Word 2}} {{Sound 2}}

So, following the introductory video, I created three scripts for each of the ‘blocks’ in the setup of the same example:



However, this didn’t give the desired functionality as Anki just typeset the commands verbatim. Worse, the mix and ord functions also stopped working. I changed closet.recipes to closet.flashcard.recipes but that didn’t work either.

I also tried something simpler like generating a random number with the Basic note type to no avail.

Would appreciate any help. Thanks.

Hi guys!
I was exploring the Closet website in order to generate some overlapping clozes. I’ve copied and pasted the following setup from the website.

Setup


/** Flashcard features */
filterManager.install(
  closet.recipes.activate({ tagname: 'on', storeId: 'flashcardActive' }),
  closet.recipes.deactivate({ tagname: 'off', storeId: 'flashcardActive' }),

  closet.wrappers.product(closet.recipes.setNumber, closet.recipes.setNumber)({
    tagname: 'around',
    optionsFirst: { storeId: 'flashcardActiveTop' },
    optionsSecond: { storeId: 'flashcardActiveBottom' },
  }),

  closet.recipes.setNumber({ tagname: 'up', storeId: 'flashcardActiveTop' }),
  closet.recipes.setNumber({ tagname: 'down', storeId: 'flashcardActiveBottom' }),

  closet.recipes.activate({ tagname: 'show', storeId: 'flashcardShow' }),
  closet.recipes.activate({ tagname: 'hide', storeId: 'flashcardHide' }),

  closet.recipes.setNumber({ tagname: 'top', storeId: 'flashcardShowTop' }),
  closet.recipes.setNumber({ tagname: 'bottom', storeId: 'flashcardShowBottom' }),

  closet.wrappers.product(closet.recipes.setNumber, closet.recipes.setNumber)({
    tagname: 'ctxt',
    optionsFirst: { storeId: 'flashcardShowTop' },
    optionsSecond: { storeId: 'flashcardShowBottom' },
  }),
)



The result:

anki_VKne3kH8iZ

Thanks in advance!

The examples on the website are outdated.

Like @PotatoHead correctly identified, you have to change closet.recipes to closet.flashcard.recipes.

1 Like

Hi, I noticed the same issue as Casartelli yesterday.

When I add .flashcard to closet.recipes.activate({ tagname: 'on', storeId: 'flashcardActive' }) and closet.recipes.deactivate({ tagname: 'off', storeId: 'flashcardActive' })
Closet stops functioning entirely.
If you have a working setup could you please share it?

There's one present in the note type you shared a while ago, but it doesn't work.
function flashcardFeatures(filterManager) {

filterManager.install(
  closet.recipes.activate({ tagname: 'on', storeId: 'flashcardActive' }),
  closet.recipes.deactivate({ tagname: 'off', storeId: 'flashcardActive' }),
  closet.recipes.activate({ tagname: 'show', storeId: 'flashcardShow' }),
  closet.recipes.activate({ tagname: 'hide', storeId: 'flashcardHide' }),
)

}

Also, the [[around]] tag works with the unchanged script from the Closet site.

1 Like

Thanks.

I’ve been trying to use the “show” tag in the “Cmds1” field( [[show::ch2]] ) but it hasn’t been working. And If I change closet.recipes to closet.flashcard.recipes, the cards stop working properly.

closet.flashcard.recipes.activate({ tagname: 'show', storeId: 'flashcardShow' }),
closet.flashcard.recipes.activate({ tagname: 'hide', storeId: 'flashcardHide' }),

Other tags like “c” are working perfectly with the change:

closet.flashcard.recipes.cloze({
tagname: "c",
defaultBehavior: closet.flashcard.behaviors.Show,
}),
closet.flashcard.recipes.multipleChoice({
tagname: "mc",
defaultBehavior: closet.flashcard.behaviors.Show,
}),
closet.flashcard.recipes.sort({
tagname: "sort",
defaultBehavior: closet.flashcard.behaviors.Show,
}),

@kleinerpirat @dylanmcooper

Is it possible to combine incremental reveal with cloze?

I saw that one of your card types has almost what I want (from here: Closet Note Template - AnkiWeb) - but I’m basically just trying to get the cloze to reveal automatically when the IO is clicked

1 Like

@kleinerpirat

Hello!

I’m trying to set up Closet and wanted to add the Graphical Effects function to my cards.

I tried your suggestions from earlier in the thread and added the styling directly to the card as well as the edited code to the closet setup.

However, the changes aren’t actually showing up on the cards. Additionally there’s an error message at the bottom of the front and back templates.

Anki version: Version ⁨2.1.40 on macOS Big Sur

Front Template

{{Front}}

<div id="anki-am" data-name="Assets by ASSET MANAGER" data-version="2.1">
    <script data-name="Prevent reinclusion" data-version="v0.1">
        var ankiAms = document.querySelectorAll('#anki-am')
          if (ankiAms.length > 1) {
            for (const am of Array.from(ankiAms).slice(0, -1)) {
              am.outerHTML = ''
          }
        }
    </script>
    <script data-name="Closet Setup" data-version="v0.1">
        function closetUserLogic(
            closet,
            preset,
            chooseMemory,
        ) {
                const elements = closet.anki.getQaChildNodes()
            const memory = chooseMemory('closet__1')
            const filterManager = closet.FilterManager.make(preset, memory.map)

            const output = [[
                elements,
                memory,
                filterManager,
            ]]

            /* here goes the setup - change it to fit your own needs */

            /** Fancy multiple choice */

            const wrappedMultipleChoiceShow = closet.wrappers.aftermath(closet.flashcard.recipes.multipleChoice.show, (e, inter) => {
              document.querySelectorAll('.cl--child')
                .forEach(v => v.addEventListener('click', () => {
                  const container = document.querySelector('.cl--container')

                  if (container) {
                    container.classList.add('cl--reveal')
                  }
                }))

              const keyword = 'fancyMultipleChoice'

              if (!inter.environment.has(keyword)) {
                inter.environment.set(keyword, true)
              }
            })

            const wrapItem = (v, _i, cat) => `<div class="cl--card"><div class="cl--child cl--category-${cat}"><h3>${v}</h3></div></div>`

            filterManager.install(wrappedMultipleChoiceShow({
              tagname: 'mc',
              frontStylizer: closet.Stylizer.make({
                mapper: wrapItem,
                separator: '',
                processor: (v) => `<div class="cl--container">${v}</div>`,
              }),
              backStylizer: closet.Stylizer.make({
                mapper: wrapItem,
                separator: '',
                processor: (v) => `<div class="cl--container cl--reveal">${v}</div>`,
              }),
            }))
            return output
        }

        var getAnkiPrefix = () => globalThis.ankiPlatform === 'desktop'
            ? ''
            : globalThis.AnkiDroidJS
            ? 'https://appassets.androidplatform.net'
            : '.'

        var closetPromise = import(`${getAnkiPrefix()}/__closet-v0.3.0.js`)
            .then(
                closet => closet.anki.initialize(closet, closetUserLogic, '{{Card}}', '{{Tags}}', 'front'),
                error => console.log('An error occured while loading Closet:', error),
            ).catch(error => console.log('An error occured while executing Closet:', error))

        if (globalThis.onUpdateHook) {
            onUpdateHook.push(() => closetPromise)
        }
    </script>
</div>

Back Template

{{FrontSide}}

<hr id="answer">

{{Back}}

<div id="anki-am" data-name="Assets by ASSET MANAGER" data-version="2.1">
    <script data-name="Closet Setup" data-version="v0.1">
        function closetUserLogic(
            closet,
            preset,
            chooseMemory,
        ) {
                const elements = closet.anki.getQaChildNodes()
            const memory = chooseMemory('closet__1')
            const filterManager = closet.FilterManager.make(preset, memory.map)

            const output = [[
                elements,
                memory,
                filterManager,
            ]]

            /* here goes the setup - change it to fit your own needs */

            /** Fancy multiple choice */

            const wrappedMultipleChoiceShow = closet.wrappers.aftermath(closet.flashcard.recipes.multipleChoice.show, (e, inter) => {
              document.querySelectorAll('.cl--child')
                .forEach(v => v.addEventListener('click', () => {
                  const container = document.querySelector('.cl--container')

                  if (container) {
                    container.classList.add('cl--reveal')
                  }
                }))

              const keyword = 'fancyMultipleChoice'

              if (!inter.environment.has(keyword)) {
                inter.environment.set(keyword, true)
              }
            })

            const wrapItem = (v, _i, cat) => `<div class="cl--card"><div class="cl--child cl--category-${cat}"><h3>${v}</h3></div></div>`

            filterManager.install(wrappedMultipleChoiceShow({
              tagname: 'mc',
              frontStylizer: closet.Stylizer.make({
                mapper: wrapItem,
                separator: '',
                processor: (v) => `<div class="cl--container">${v}</div>`,
              }),
              backStylizer: closet.Stylizer.make({
                mapper: wrapItem,
                separator: '',
                processor: (v) => `<div class="cl--container cl--reveal">${v}</div>`,
              }),
            }))
            return output
        }

        var getAnkiPrefix = () => globalThis.ankiPlatform === 'desktop'
            ? ''
            : globalThis.AnkiDroidJS
            ? 'https://appassets.androidplatform.net'
            : '.'

        var closetPromise = import(`${getAnkiPrefix()}/__closet-v0.3.0.js`)
            .then(
                closet => closet.anki.initialize(closet, closetUserLogic, '{{Card}}', '{{Tags}}', 'back'),
                error => console.log('An error occured while loading Closet:', error),
            ).catch(error => console.log('An error occured while executing Closet:', error))

        if (globalThis.onUpdateHook) {
            onUpdateHook.push(() => closetPromise)
        }
    </script>
</div>

Styling



.cl--container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  grid-gap: 0px 40px;
  margin: 40px auto;
}

.cl--card {
  cursor: pointer;
  position: relative;
  height: 0;
  padding-bottom: 100%;
  transition: transform 0.6s ease;
  --translate: 0;
  transform: translate(var(--translate), var(--translate));
}

.cl--card:hover {
  --translate: calc(-5px);
  transition: transform 0.3s ease;
}

.cl--child {
  position: absolute;
  width: 100%;
  height: 80%;

  padding: 8px 16px;
  background: #fff;
  box-shadow: 0px 4px 8px rgba(128, 128, 128, 0.1), 0px -4px 8px rgba(255, 255, 255, 0.8);
  border-radius: 6px;
  transition: inherit;
  transform: translate(var(--translate), var(--translate));
  z-index: 5;
}

.cl--child > h3 {
  color: black;
}

.cl--reveal .cl--category-0 {
  background-color: lime;
}

.cl--reveal .cl--category-1 {
  background-color: coral;
}

**Closet Setup Code**

const elements = closet.anki.getQaChildNodes()
const memory = chooseMemory(‘closet__1’)
const filterManager = closet.FilterManager.make(preset, memory.map)

const output = [[
elements,
memory,
filterManager,
]]

/* here goes the setup - change it to fit your own needs */

filterManager.install(
closet.recipes.shuffle({ tagname: ‘mix’ }),
closet.recipes.order({ tagname: ‘ord’ }),
closet.recipes.cloze({ tagname: ‘c’ }),
closet.recipes.multipleChoice({ tagname: ‘mc’ }),
closet.browser.recipes.rect({ tagname: ‘rect’ }),
)

/** Fancy multiple choice */

const wrappedMultipleChoiceShow = closet.wrappers.aftermath(closet.flashcard.recipes.multipleChoice.show, (e, inter) => {
document.querySelectorAll(’.cl–child’)
.forEach(v => v.addEventListener(‘click’, () => {
const container = document.querySelector(’.cl–container’)

  if (container) {
    container.classList.add('cl--reveal')
  }
}))

const keyword = ‘fancyMultipleChoice’

if (!inter.environment.has(keyword)) {
inter.environment.set(keyword, true)
}
})

const wrapItem = (v, _i, cat) => <div class="cl--card"><div class="cl--child cl--category-${cat}"><h3>${v}</h3></div></div>

filterManager.install(wrappedMultipleChoiceShow({
tagname: ‘mc’,
frontStylizer: closet.Stylizer.make({
mapper: wrapItem,
separator: ‘’,
processor: (v) => <div class="cl--container">${v}</div>,
}),
backStylizer: closet.Stylizer.make({
mapper: wrapItem,
separator: ‘’,
processor: (v) => <div class="cl--container cl--reveal">${v}</div>,
}),
}))

There’s a few things I’m working on with that script that I need help with. I’ll add this to the list but I know next to nothing about coding, so no promises.
Other plans for that note template / script:

  • Get I/O incremental reveal to work for rect2 and rect3;
  • Keyboard shortcut for I/O incremental reveal;
  • Ditch the incremental reveal and let you click any I/O box to reveal it

If @kleinerpirat or @hengiesel wants to get involved, I would be willing to pay for their help.