diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bed0c91..0ffe9cef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,9 @@
# AMII Changelog
## [Unreleased]
+
### Added
+- The ability for MIKU to continuously give you a stream of AniMemes (Silence Breaker feature).
### Changed
diff --git a/README.md b/README.md
index 863d1a0c..46337e21 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,15 @@ Programs that exit with:
are part of the default allowed exit codes, MIKU will not react to these (but can if you want to).
+### Silence Breaker
+
+So you've been working diligently building your code, but not using any features of your IDE.
+Such as building, testing, or running your project.
+Well MIKU likes to remind you every so often that they exist.
+
+You can specify how long you can go without seeing a meme.
+After that, MIKU will give you one!
+
### Logs
Do you work on a project that takes a billion years for the application to start?
diff --git a/gradle.properties b/gradle.properties
index 0ae45335..9b5343d9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,7 +3,7 @@
pluginGroup = io.unthrottled
pluginName_ = AMII
-pluginVersion = 0.3.1
+pluginVersion = 0.4.0
pluginSinceBuild = 201.4515.24
pluginUntilBuild = 203.*
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
diff --git a/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.form b/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.form
index ea505c4d..ce9e9bc0 100644
--- a/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.form
+++ b/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.form
@@ -3,7 +3,7 @@
-
+
@@ -459,204 +459,241 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
-
+
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.java b/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.java
index cf5d2131..c9cb13ff 100644
--- a/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.java
+++ b/src/main/java/io/unthrottled/amii/config/ui/PluginSettingsUI.java
@@ -34,6 +34,7 @@
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
@@ -53,6 +54,7 @@
import static io.unthrottled.amii.events.UserEvents.IDLE;
import static io.unthrottled.amii.events.UserEvents.LOGS;
import static io.unthrottled.amii.events.UserEvents.PROCESS;
+import static io.unthrottled.amii.events.UserEvents.SILENCE;
import static io.unthrottled.amii.events.UserEvents.STARTUP;
import static io.unthrottled.amii.events.UserEvents.TASK;
import static io.unthrottled.amii.events.UserEvents.TEST;
@@ -95,6 +97,9 @@ public class PluginSettingsUI implements SearchableConfigurable, Configurable.No
private JTextPane generalLinks;
private JPanel idleAnchorPanel;
private JTabbedPane tabbedPane1;
+ private JScrollPane eventsPane;
+ private JSpinner silenceSpinner;
+ private JCheckBox permitBreaksInSilenceCheckBox;
private PreferredCharacterPanel characterModel;
private PreferredCharacterPanel blacklistedCharacterModel;
private JBTable exitCodeTable;
@@ -285,8 +290,7 @@ public boolean isCellEditable(Integer info) {
1
);
eventsBeforeFrustrationSpinner.setModel(frustrationSpinnerModel);
- eventsBeforeFrustrationSpinner.addChangeListener(e -> pluginSettingsModel.setEventsBeforFrustration(frustrationSpinnerModel.getNumber().intValue()));
-
+ eventsBeforeFrustrationSpinner.addChangeListener(e -> pluginSettingsModel.setEventsBeforeFrustration(frustrationSpinnerModel.getNumber().intValue()));
soundEnabled.addActionListener(e -> volumeSlider.setEnabled(soundEnabled.isSelected()));
volumeSlider.setForeground(UIUtil.getContextHelpForeground());
@@ -307,6 +311,19 @@ public boolean isCellEditable(Integer info) {
idleTimeoutSpinner.setModel(idleSpinnerModel);
idleTimeoutSpinner.addChangeListener(e -> pluginSettingsModel.setIdleTimeOutInMinutes(idleSpinnerModel.getNumber().intValue()));
+ SpinnerNumberModel silenceSpinnerModel = new SpinnerNumberModel(
+ config.getSilenceTimeoutInMinutes(),
+ 1,
+ Integer.MAX_VALUE,
+ 1
+ );
+ silenceSpinner.setModel(silenceSpinnerModel);
+ silenceSpinner.addChangeListener(e -> pluginSettingsModel.setSilenceTimeOutInMinutes(silenceSpinnerModel.getNumber().intValue()));
+
+ permitBreaksInSilenceCheckBox.addActionListener(e -> {
+ updateIdleComponents();
+ updateEventPreference(SILENCE.getValue(), permitBreaksInSilenceCheckBox.isSelected());
+ });
idleEnabled.addActionListener(e -> {
updateIdleComponents();
updateEventPreference(IDLE.getValue(), idleEnabled.isSelected());
@@ -364,6 +381,9 @@ private void updateLogComponents() {
private void updateIdleComponents() {
idleTimeoutSpinner.setEnabled(idleEnabled.isSelected());
}
+ private void updateSilenceComponents() {
+ silenceSpinner.setEnabled(permitBreaksInSilenceCheckBox.isSelected());
+ }
private void updateFrustrationComponents() {
frustrationProbabilitySlider.setEnabled(allowFrustrationCheckBox.isSelected());
@@ -398,6 +418,7 @@ private void initFromState() {
preferOther.setSelected(isGenderSelected(Gender.OTHER.getValue()));
idleEnabled.setSelected(isEventEnabled(IDLE.getValue()));
+ permitBreaksInSilenceCheckBox.setSelected(isEventEnabled(SILENCE.getValue()));
updateIdleComponents();
watchLogs.setSelected(isEventEnabled(LOGS.getValue()));
updateLogComponents();
@@ -469,7 +490,8 @@ public void apply() {
config.setLogSearchIgnoreCase(pluginSettingsModel.getLogSearchIgnoreCase());
config.setShowMood(pluginSettingsModel.getShowMood());
config.setIdleTimeoutInMinutes(pluginSettingsModel.getIdleTimeOutInMinutes());
- config.setEventsBeforeFrustration(pluginSettingsModel.getEventsBeforFrustration());
+ config.setSilenceTimeoutInMinutes(pluginSettingsModel.getSilenceTimeOutInMinutes());
+ config.setEventsBeforeFrustration(pluginSettingsModel.getEventsBeforeFrustration());
ApplicationManager.getApplication().getMessageBus().syncPublisher(
ConfigListener.Companion.getCONFIG_TOPIC()
).pluginConfigUpdated(config);
diff --git a/src/main/kotlin/io/unthrottled/amii/PluginMaster.kt b/src/main/kotlin/io/unthrottled/amii/PluginMaster.kt
index 2abed12b..d5071eb0 100644
--- a/src/main/kotlin/io/unthrottled/amii/PluginMaster.kt
+++ b/src/main/kotlin/io/unthrottled/amii/PluginMaster.kt
@@ -11,6 +11,7 @@ import io.unthrottled.amii.assets.CharacterContentManager
import io.unthrottled.amii.assets.Status
import io.unthrottled.amii.assets.VisualContentManager
import io.unthrottled.amii.listeners.IdleEventListener
+import io.unthrottled.amii.listeners.SilenceListener
import io.unthrottled.amii.onboarding.UpdateNotification
import io.unthrottled.amii.onboarding.UserOnBoarding
import io.unthrottled.amii.platform.LifeCycleManager
@@ -83,8 +84,10 @@ internal data class ProjectListeners(
) : Disposable {
private val idleEventListener = IdleEventListener(project)
+ private val silenceListener = SilenceListener(project)
override fun dispose() {
idleEventListener.dispose()
+ silenceListener.dispose()
}
}
diff --git a/src/main/kotlin/io/unthrottled/amii/config/Config.kt b/src/main/kotlin/io/unthrottled/amii/config/Config.kt
index d007cbf9..89c68ef7 100644
--- a/src/main/kotlin/io/unthrottled/amii/config/Config.kt
+++ b/src/main/kotlin/io/unthrottled/amii/config/Config.kt
@@ -25,6 +25,7 @@ class Config : PersistentStateComponent, Cloneable {
get() = ServiceManager.getService(Config::class.java)
const val DEFAULT_DELIMITER = ","
const val DEFAULT_IDLE_TIMEOUT_IN_MINUTES: Long = 5L
+ const val DEFAULT_SILENCE_TIMEOUT_IN_MINUTES: Long = 10L
const val DEFAULT_MEME_INVULNERABLE_DURATION: Int = 3
const val DEFAULT_TIMED_MEME_DISPLAY_DURATION: Int = 40
const val DEFAULT_EVENTS_BEFORE_FRUSTRATION: Int = 5
@@ -48,6 +49,7 @@ class Config : PersistentStateComponent, Cloneable {
FORCE_KILLED_EXIT_CODE
).joinToString(DEFAULT_DELIMITER)
var idleTimeoutInMinutes = DEFAULT_IDLE_TIMEOUT_IN_MINUTES
+ var silenceTimeoutInMinutes = DEFAULT_SILENCE_TIMEOUT_IN_MINUTES
var allowFrustration = true
var eventsBeforeFrustration = DEFAULT_EVENTS_BEFORE_FRUSTRATION
var probabilityOfFrustration = DEFAULT_FRUSTRATION_PROBABILITY
diff --git a/src/main/kotlin/io/unthrottled/amii/config/PluginSettings.kt b/src/main/kotlin/io/unthrottled/amii/config/PluginSettings.kt
index 13058818..b5eead6e 100644
--- a/src/main/kotlin/io/unthrottled/amii/config/PluginSettings.kt
+++ b/src/main/kotlin/io/unthrottled/amii/config/PluginSettings.kt
@@ -5,6 +5,7 @@ import java.net.URI
data class ConfigSettingsModel(
var allowedExitCodes: String,
var idleTimeOutInMinutes: Long,
+ var silenceTimeOutInMinutes: Long,
var memeDisplayAnchorValue: String,
var idleMemeDisplayAnchorValue: String,
var memeDisplayModeValue: String,
@@ -18,7 +19,7 @@ data class ConfigSettingsModel(
var logKeyword: String,
var logSearchIgnoreCase: Boolean,
var showMood: Boolean,
- var eventsBeforFrustration: Int,
+ var eventsBeforeFrustration: Int,
) {
fun duplicate(): ConfigSettingsModel = copy()
}
@@ -34,6 +35,7 @@ object PluginSettings {
fun getInitialConfigSettingsModel() = ConfigSettingsModel(
Config.instance.allowedExitCodes,
Config.instance.idleTimeoutInMinutes,
+ Config.instance.silenceTimeoutInMinutes,
Config.instance.memeDisplayAnchorValue,
Config.instance.memeDisplayModeValue,
Config.instance.idleMemeDisplayAnchorValue,
diff --git a/src/main/kotlin/io/unthrottled/amii/core/MIKU.kt b/src/main/kotlin/io/unthrottled/amii/core/MIKU.kt
index bb818884..b99624a9 100644
--- a/src/main/kotlin/io/unthrottled/amii/core/MIKU.kt
+++ b/src/main/kotlin/io/unthrottled/amii/core/MIKU.kt
@@ -140,6 +140,7 @@ class MIKU :
private fun reactToEvent(userEvent: UserEvent, emotionalState: Mood) {
when (userEvent.type) {
in USER_TRIGGERED_EVENTS -> taskPersonalityCore.processUserEvent(userEvent, emotionalState)
+ UserEvents.SILENCE,
UserEvents.ON_DEMAND -> onDemandPersonalityCore.processUserEvent(userEvent, emotionalState)
UserEvents.IDLE,
UserEvents.RETURN, -> idlePersonalityCore.processUserEvent(userEvent, emotionalState)
diff --git a/src/main/kotlin/io/unthrottled/amii/core/personality/OnDemandPersonalityCore.kt b/src/main/kotlin/io/unthrottled/amii/core/personality/OnDemandPersonalityCore.kt
index 673bb97b..b8549196 100644
--- a/src/main/kotlin/io/unthrottled/amii/core/personality/OnDemandPersonalityCore.kt
+++ b/src/main/kotlin/io/unthrottled/amii/core/personality/OnDemandPersonalityCore.kt
@@ -1,8 +1,10 @@
package io.unthrottled.amii.core.personality
import io.unthrottled.amii.assets.MemeAssetCategory
+import io.unthrottled.amii.config.Config
import io.unthrottled.amii.core.personality.emotions.Mood
import io.unthrottled.amii.events.UserEvent
+import io.unthrottled.amii.events.UserEvents
import io.unthrottled.amii.memes.Comparison
import io.unthrottled.amii.memes.memeService
@@ -13,13 +15,20 @@ class OnDemandPersonalityCore : PersonalityCore {
mood: Mood
) {
userEvent.project.memeService()
- .createMeme(
+ .createMemeFromCategories(
userEvent,
- MemeAssetCategory.MOTIVATION,
+ MemeAssetCategory.HAPPY,
+ MemeAssetCategory.CELEBRATION,
+ MemeAssetCategory.ALERT,
) {
- it.withComparator {
- Comparison.GREATER
- }.build()
+ it
+ .withSound(
+ if (userEvent.type == UserEvents.SILENCE) false
+ else Config.instance.soundEnabled
+ )
+ .withComparator {
+ Comparison.GREATER
+ }.build()
}
}
}
diff --git a/src/main/kotlin/io/unthrottled/amii/events/UserEvents.kt b/src/main/kotlin/io/unthrottled/amii/events/UserEvents.kt
index 61fa820a..bbe83064 100644
--- a/src/main/kotlin/io/unthrottled/amii/events/UserEvents.kt
+++ b/src/main/kotlin/io/unthrottled/amii/events/UserEvents.kt
@@ -16,6 +16,7 @@ enum class UserEvents(val value: Int) {
TASK(1 shl 5),
TEST(1 shl 6),
RELAX(1 shl 7),
+ SILENCE(1 shl 8),
}
enum class UserEventCategory {
diff --git a/src/main/kotlin/io/unthrottled/amii/listeners/SilenceListener.kt b/src/main/kotlin/io/unthrottled/amii/listeners/SilenceListener.kt
new file mode 100644
index 00000000..4d1a2c43
--- /dev/null
+++ b/src/main/kotlin/io/unthrottled/amii/listeners/SilenceListener.kt
@@ -0,0 +1,83 @@
+package io.unthrottled.amii.listeners
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.project.Project
+import com.intellij.util.Alarm
+import io.unthrottled.amii.config.Config
+import io.unthrottled.amii.config.ConfigListener
+import io.unthrottled.amii.events.EVENT_TOPIC
+import io.unthrottled.amii.events.UserEvent
+import io.unthrottled.amii.events.UserEventCategory
+import io.unthrottled.amii.events.UserEventListener
+import io.unthrottled.amii.events.UserEvents
+import io.unthrottled.amii.tools.PluginMessageBundle
+import java.util.concurrent.TimeUnit
+
+class SilenceListener(private val project: Project) : Runnable, UserEventListener, Disposable {
+ private val messageBus = ApplicationManager.getApplication().messageBus.connect()
+ private val log = Logger.getInstance(this::class.java)
+ private val silenceAlarm = Alarm()
+
+ init {
+ val self = this
+ messageBus.subscribe(EVENT_TOPIC, this)
+ messageBus.subscribe(
+ ConfigListener.CONFIG_TOPIC,
+ ConfigListener { newPluginState ->
+ silenceAlarm.cancelAllRequests()
+ silenceAlarm.addRequest(
+ self,
+ TimeUnit.MILLISECONDS.convert(
+ newPluginState.silenceTimeoutInMinutes,
+ TimeUnit.MINUTES
+ ).toInt()
+ )
+ }
+ )
+ scheduleSilenceAlert()
+ }
+
+ private fun scheduleSilenceAlert() {
+ silenceAlarm.addRequest(
+ this,
+ TimeUnit.MILLISECONDS.convert(
+ getCurrentTimoutInMinutes(),
+ TimeUnit.MINUTES
+ ).toInt()
+ )
+ }
+
+ private fun getCurrentTimoutInMinutes(): Long =
+ Config.instance.silenceTimeoutInMinutes
+
+ override fun dispose() {
+ messageBus.dispose()
+ silenceAlarm.dispose()
+ }
+
+ override fun run() {
+ log.debug("Observed silence timeout")
+ ApplicationManager.getApplication().messageBus
+ .syncPublisher(EVENT_TOPIC)
+ .onDispatch(
+ UserEvent(
+ UserEvents.SILENCE,
+ UserEventCategory.NEUTRAL,
+ PluginMessageBundle.message("user.event.silence.name"),
+ project
+ )
+ )
+ }
+
+ override fun onDispatch(userEvent: UserEvent) {
+ when (userEvent.type) {
+ UserEvents.IDLE -> silenceAlarm.cancelAllRequests()
+ else -> {
+ silenceAlarm.cancelAllRequests()
+ scheduleSilenceAlert()
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/io/unthrottled/amii/memes/Meme.kt b/src/main/kotlin/io/unthrottled/amii/memes/Meme.kt
index dc051168..4979349e 100644
--- a/src/main/kotlin/io/unthrottled/amii/memes/Meme.kt
+++ b/src/main/kotlin/io/unthrottled/amii/memes/Meme.kt
@@ -54,6 +54,7 @@ class Meme(
) {
private var notificationMode = Config.instance.notificationMode
private var notificationAnchor = Config.instance.notificationAnchor
+ private var soundEnabled = Config.instance.soundEnabled
private var memeDisplayInvulnerabilityDuration = Config.instance.memeDisplayInvulnerabilityDuration
private var memeDisplayTimedDuration = Config.instance.memeDisplayTimedDuration
private var memeComparator: (Meme) -> Comparison = { Comparison.EQUAL }
@@ -69,6 +70,11 @@ class Meme(
return this
}
+ fun withSound(newSoundOption: Boolean): Builder {
+ soundEnabled = newSoundOption
+ return this
+ }
+
fun withMetaData(newMetaData: Map): Builder {
metaData = newMetaData
return this
@@ -81,6 +87,7 @@ class Meme(
fun build(): Meme {
val memePlayer = audibleContent.toOptional()
+ .filter { soundEnabled }
.map { MemePlayerFactory.createPlayer(it) }
.orElse(null)
return Meme(
diff --git a/src/main/kotlin/io/unthrottled/amii/memes/player/Mp3Player.kt b/src/main/kotlin/io/unthrottled/amii/memes/player/Mp3Player.kt
deleted file mode 100644
index fc7874c3..00000000
--- a/src/main/kotlin/io/unthrottled/amii/memes/player/Mp3Player.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-package io.unthrottled.amii.memes.player
-
-import io.unthrottled.amii.assets.AudibleContent
-import javazoom.jl.player.FactoryRegistry
-import javazoom.jl.player.advanced.AdvancedPlayer
-import javazoom.jl.player.advanced.PlaybackEvent
-import javazoom.jl.player.advanced.PlaybackListener
-import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader
-import java.nio.file.Files
-import java.nio.file.Paths
-import javax.sound.sampled.AudioFileFormat
-
-// todo: post mvp: volume adjustment
-class Mp3Player(
- private val audibleAssetContent: AudibleContent
-) : MemePlayer {
-
- val player: AdvancedPlayer
-
- init {
- val audioDevice = FactoryRegistry.systemRegistry().createAudioDevice()
- player = Files.newInputStream(Paths.get(audibleAssetContent.filePath))
- .use { audioInputStream ->
- AdvancedPlayer(audioInputStream, audioDevice)
- }
- player.playBackListener = object : PlaybackListener() {
- override fun playbackFinished(evt: PlaybackEvent?) {
- evt?.source?.close()
- player.close()
- }
- }
- }
-
- override val duration: Long
- get() {
- val baseFileFormat: AudioFileFormat = MpegAudioFileReader().getAudioFileFormat(
- audibleAssetContent.filePath.toURL()
- )
- val duration = baseFileFormat.properties()["duration"] as Long?
- return duration ?: MemePlayer.NO_LENGTH
- }
-
- override fun play() {
- player.play()
- }
-
- override fun stop() {
- player.close()
- }
-}
diff --git a/src/main/kotlin/io/unthrottled/amii/onboarding/UserOnBoarding.kt b/src/main/kotlin/io/unthrottled/amii/onboarding/UserOnBoarding.kt
index 358e6c98..0f6b14eb 100644
--- a/src/main/kotlin/io/unthrottled/amii/onboarding/UserOnBoarding.kt
+++ b/src/main/kotlin/io/unthrottled/amii/onboarding/UserOnBoarding.kt
@@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupManager
import io.unthrottled.amii.config.Config
import io.unthrottled.amii.config.Constants.PLUGIN_ID
+import io.unthrottled.amii.events.UserEvents
import io.unthrottled.amii.platform.UpdateAssetsListener
import io.unthrottled.amii.tools.toOptional
import java.util.Optional
@@ -14,6 +15,10 @@ import java.util.UUID
object UserOnBoarding {
+ private val addedEvents = setOf(
+ UserEvents.SILENCE
+ ).map { it.value }
+
fun attemptToPerformNewUpdateActions(project: Project) {
getNewVersion().ifPresent { newVersion ->
Config.instance.version = newVersion
@@ -29,6 +34,12 @@ object UserOnBoarding {
if (Config.instance.userId.isEmpty()) {
Config.instance.userId = UUID.randomUUID().toString()
}
+
+ // Add new events for user
+ Config.instance.enabledEvents = addedEvents.stream()
+ .reduce(Config.instance.enabledEvents) { accum, newEventToAdd ->
+ accum or newEventToAdd
+ }
}
private fun getNewVersion() =
diff --git a/src/main/resources/messages/AMII.properties b/src/main/resources/messages/AMII.properties
index 09158f60..579a9ae5 100644
--- a/src/main/resources/messages/AMII.properties
+++ b/src/main/resources/messages/AMII.properties
@@ -59,3 +59,8 @@ actions.sync.start.title=Starting Asset Sync
actions.sync.start.message=Fetching list of assets from the remote repository.
miku.startup.error.body=For full functionality, please try restarting your IDE. Please submit an issue if it persists.
miku.startup.error.title=Unable to fully initialize!
+user.event.silence.name=Silence Events
+settings.events.silence.name=Silence
+settings.events.general.title=General Events
+settings.events.silence.label=Duration before event (minutes)
+settings.events.silence.enabled=Permit breaks in silence
diff --git a/src/main/resources/messages/AMII_zh.properties b/src/main/resources/messages/AMII_zh.properties
index 62dda90b..ead01091 100644
--- a/src/main/resources/messages/AMII_zh.properties
+++ b/src/main/resources/messages/AMII_zh.properties
@@ -52,3 +52,8 @@ actions.sync.start.title=Starting Asset Sync
actions.sync.start.message=Fetching list of assets from the remote repository.
miku.startup.error.body=For full functionality, please try restarting your IDE. Please submit an issue if it persists.
miku.startup.error.title=Unable to fully initialize!
+user.event.silence.name=Silence Events
+settings.events.silence.name=Silence
+settings.events.general.title=General Events
+settings.events.silence.label=Duration before event (minutes)
+settings.events.silence.enabled=Permit breaks in silence